Question

I have what seems to be a common business request but I can't find no clear solution. I have a daily report (amongst many) that gets generated based on failed criteria and gets saved to a table. Each report has a type id tied to it to signify which report it is, and there is an import event id that signifies the day the imports came in (a date column is added for extra clarification). I've added a sqlfiddle to see the basic schema of the table (renamed for privacy issues).

http://www.sqlfiddle.com/#!3/81945/8

All reports currently generated are working fine, so nothing needs to be modified on the table. However, for one report (type 11), not only I need pull the invoices that showed up today, I also need to add one column that totals the amount of consecutive days from date of run for that invoice (including current day). The result should look like the following, based on the schema provided:

INVOICE     MESSAGE     EVENT_DATE      CONSECUTIVE_DAYS_ON_REPORT
12345       Yes         July, 30 2013    6
54355       Yes         July, 30 2013    2
644644      Yes         July, 30 2013    4

I only need the latest consecutive days, not any other set that may show up. I've tried to run self joins to no avail, and my last attempt is also listed as part of the sqlfiddle file, to no avail. Any suggestions or ideas? I'm quite stuck at the moment.

FYI: I am working in SQL Server 2000! I have seen a lot of neat tricks that have come out in 2005 and 2008, but I can't access them.

Your help is greatly appreciated!

Was it helpful?

Solution

Something like this? http://www.sqlfiddle.com/#!3/81945/14

SELECT
  [final].*,
  [last].total_rows
FROM
  tblEventInfo   AS [final]
INNER JOIN
(
  SELECT
    [first_of_last].type_id,
    [first_of_last].invoice,
    MAX([all_of_last].event_date)   AS event_date,
    COUNT(*)                        AS total_rows
  FROM
  (
    SELECT
      [current].type_id,
      [current].invoice,
      MAX([current].event_date)   AS event_date
    FROM
      tblEventInfo   AS [current]
    LEFT JOIN
      tblEventInfo   AS [previous]
        ON  [previous].type_id    = [current].type_id
        AND [previous].invoice    = [current].invoice
        AND [previous].event_date = [current].event_date-1
    WHERE
          [current].type_id = 11
      AND [previous].type_id IS NULL
    GROUP BY
      [current].type_id,
      [current].invoice
  )
    AS [first_of_last]
  INNER JOIN
    tblEventInfo  AS [all_of_last]
      ON  [all_of_last].type_id     = [first_of_last].type_id
      AND [all_of_last].invoice     = [first_of_last].invoice
      AND [all_of_last].event_date >= [first_of_last].event_date
  GROUP BY
    [first_of_last].type_id,
    [first_of_last].invoice
)
  AS [last]
    ON  [last].type_id    = [final].type_id
    AND [last].invoice    = [final].invoice
    AND [last].event_date = [final].event_date

The inner most query looks up the starting record of the last block of consecutive records.

Then that joins on to all the records in that block of consecutive records, giving the final date and the count of rows (consecutive days).

Then that joins on to the row for the last day to get the message, etc.


Make sure that in reality you have an index on (type_id, invoice, event_date).

OTHER TIPS

You have multiple problems. Tackle them separately and build up.

Problems:

1) Identifying consecutive ranges: subtract the row_number from the range column and group by the result

2) No ROW_NUMBER() functions in SQL 2000: Fake it with a correlated subquery.

3) You actually want DENSE_RANK() instead of ROW_NUMBER: Make a list of unique dates first.

Solutions:

3)

SELECT MAX(id) AS id,invoice,event_date FROM tblEventInfo GROUP BY invoice,event_date

2)

  SELECT t2.invoice,t2.event_date,t2.id,
    DATEDIFF(day,(SELECT COUNT(DISTINCT event_date) FROM (SELECT MAX(id) AS id,invoice,event_date FROM tblEventInfo GROUP BY invoice,event_date) t1 WHERE t2.invoice = t1.invoice AND t2.event_date > t1.event_date),t2.event_date) grp
  FROM (SELECT MAX(id) AS id,invoice,event_date FROM tblEventInfo GROUP BY invoice,event_date) t2
  ORDER BY invoice,grp,event_date

1)

SELECT
  t3.invoice AS INVOICE,
  MAX(t3.event_date) AS EVENT_DATE,
  COUNT(t3.event_date) AS CONSECUTIVE_DAYS_ON_REPORT
FROM (
  SELECT t2.invoice,t2.event_date,t2.id,
    DATEDIFF(day,(SELECT COUNT(DISTINCT event_date) FROM (SELECT MAX(id) AS id,invoice,event_date FROM tblEventInfo GROUP BY invoice,event_date) t1 WHERE t2.invoice = t1.invoice AND t2.id > t1.id),t2.event_date) grp
  FROM (SELECT MAX(id) AS id,invoice,event_date FROM tblEventInfo GROUP BY invoice,event_date) t2
) t3
GROUP BY t3.invoice,t3.grp

The rest of your question is a little ambiguous. If two ranges are of equal length, do you want both or just the most recent? Should the output MESSAGE be 'Yes' if any message = 'Yes' or only if the most recent message = 'Yes'?

This should give you enough of a breadcrumb though

I had a similar requirement not long ago getting a "Top 5" ranking with a consecutive number of periods in Top 5. The only solution I found was to do it in a cursor. The cursor has a date = @daybefore and inside the cursor if your data does not match quit the loop, otherwise set @daybefore = datediff(dd, -1, @daybefore).

Let me know if you want an example. There just seem to be a large number of enthusiasts, who hit downvote when they see the word "cursor" even if they don't have a better solution...

Here, try a scalar function like this:

CREATE FUNCTION ConsequtiveDays
(
    @invoice bigint, @date datetime
)
RETURNS int
AS
BEGIN
    DECLARE @ct int = 0, @Count_Date datetime, @Last_Date datetime

    SELECT @Last_Date = @date

    DECLARE counter CURSOR LOCAL FAST_FORWARD
    FOR
    SELECT event_date FROM tblEventInfo 
    WHERE invoice = @invoice
    ORDER BY event_date DESC

    FETCH NEXT FROM counter
    INTO @Count_Date

    WHILE @@FETCH_STATUS = 0 AND DATEDIFF(dd,@Last_Date,@Count_Date) < 2
    BEGIN
        @ct = @ct + 1
    END

    CLOSE counter
    DEALLOCATE counter

    RETURN @ct

END
GO
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top