resarting row number based on values in categories

  • use this code please

    Drop table #table

    Drop table #table_with_groupid

    -- Prepare test data

    CREATE TABLE #table

    ([Admissions_key] bigint NOT NULL PRIMARY KEY,

    MRN nvarchar(10) NOT NULL,

    hosp_code nvarchar(10) NOT NULL,

    adm_datetime datetime NOT NULL,

    sep_datetime datetime NOT NULL,

    Sequence nvarchar(10) NOT NULL

    )

    SET DATEFORMAT DMY

    INSERT INTO #table(Admissions_key,MRN, hosp_code, adm_datetime, sep_datetime,Sequence)

    VALUES

    (7099222,'0000544607','0003','22/07/2011 04:55','22/07/2011 10:44','First'),

    (7099223,'0000544607','0003','22/07/2011 10:45','25/07/2011 19:43','Middle'),

    (7099224,'0000544607','0003','25/07/2011 19:44','26/07/2011 11:29','Middle'),

    (7099225,'0000544607','0003','27/07/2011 13:30','27/07/2011 19:30','First'),

    (7099226,'0000544607','0003','27/07/2011 19:31','28/07/2011 11:30','Final'),

    (7099227,'0000559282','0003','03/07/2011 22:50','03/07/2011 23:51','First'),

    (7099228,'0000559282','0003','03/07/2011 23:52','04/07/2011 15:30','Middle'),

    (7099229,'0000559282','0003','04/07/2011 15:31','04/07/2011 17:59','Final'),

    (7099230,'0000559282','0003','05/07/2011 18:00','05/07/2011 18:05','Middle'),

    (7099231,'0000559282','0003','05/07/2011 18:06','09/07/2011 14:58','Final'),

    (7099232,'0000999271','0003','07/08/2011 01:00','07/08/2011 18:05','Middle'),

    (7099233,'0000999271','0003','07/08/2011 18:06','09/08/2011 14:58','Final'),

    (7099234,'0000999271','0003','10/08/2011 18:00','10/08/2011 18:05','First'),

    (7099235,'0000446435','0003','11/08/2011 18:06','12/08/2011 12:08','First'),

    (7099236,'0000446435','0003','12/08/2011 12:09','12/08/2011 14:58','Final')

    ;WITH cur_prev AS

    (-- Match current row with row above

    SELECT cur.*, prev_Sequence = prev.Sequence, pre_sep_datetime = CASE WHEN cur.Sequence='First' THEN NULL ELSE prev.sep_datetime END

    FROM #table cur

    OUTER APPLY -- we use OUTER APPLY instead of CROSS APPLY to get a row even if previous row does not exist

    (-- Find previous row to current row.

    SELECT TOP 1 tt.*

    FROM #table tt

    WHERE tt.MRN = cur.MRN AND tt.hosp_code = cur.hosp_code -- within the same group

    and -- only rows above

    (tt.adm_datetime < cur.adm_datetime

    OR tt.adm_datetime = cur.adm_datetime AND tt.Admissions_key < cur.Admissions_key

    )

    ORDER BY tt.adm_datetime DESC, tt.Admissions_key DESC -- sort should be unique, so we added a PK column

    ) prev

    )

    SELECT c.*,

    GroupID =

    (-- Find the first row above that starts a group

    SELECT TOP 1 cc.Admissions_key

    FROM cur_prev cc

    WHERE cc.MRN = c.MRN AND cc.hosp_code = c.hosp_code -- within the same group

    AND -- only rows above, but this time INCLUDING current row!

    (cc.adm_datetime < c.adm_datetime

    OR cc.adm_datetime = c.adm_datetime AND cc.Admissions_key <= c.Admissions_key

    )

    ORDER BY CASE WHEN cc.Sequence='First' OR cc.prev_Sequence='Final' THEN 0 ELSE 1 END, -- first try to find a row that starts a group

    cc.adm_datetime DESC, cc.Admissions_key DESC -- sort should be unique, so we added a PK column

    )

    INTO #table_with_groupid

    FROM cur_prev c

    SELECT * FROM #table_with_groupid

    SELECT t.*, g.IsGroupCorrect

    FROM #table_with_groupid t

    LEFT JOIN

    (-- Find which group is correct and which is not

    SELECT tg.GroupID,

    IsGroupCorrect = CASE -- correct is group that have 'First' and 'Final' in it.

    WHEN MAX(CASE WHEN tg.Sequence='First' THEN 1 ELSE 0 END)=1 AND MAX(CASE WHEN tg.Sequence='Final' THEN 1 ELSE 0 END)=1 THEN 'C'

    ELSE 'E'

    END

    FROM #table_with_groupid tg

    --where datediff(MINUTE,tg.adm_datetime,tg.pre_sep_date)< =1

    GROUP BY tg.GroupID

    ) g on t.GroupID = g.GroupID

  • SELECT t.*, g.IsGroupCorrect

    FROM #table_with_groupid t

    LEFT JOIN

    (-- Find which group is correct and which is not

    SELECT tg.GroupID,

    IsGroupCorrect = CASE -- correct is group that have 'First' and 'Final' in it.

    WHEN MAX(CASE WHEN tg.Sequence='First' THEN 1 ELSE 0 END)=1 AND MAX(CASE WHEN tg.Sequence='Final' THEN 1 ELSE 0 END)=1

    and MAX(Case When tg.pre_sep_datetime IS NOT NUll then 1 else 0 end) = 1

    and MAX(Case When datediff(MINUTE,tg.pre_sep_datetime,tg.adm_datetime)< =1 then 1 else 0 end) = 1

    THEN 'C'

    ELSE 'E'

    END

    FROM #table_with_groupid tg

    --where datediff(MINUTE,tg.adm_datetime,tg.pre_sep_date)< =1

    GROUP BY tg.GroupID

    ) g on t.GroupID = g.GroupID

    This is working when there is first and final, but not when there is first, many middle and final

    I think, I am very near, please rectify, do i need to use cursor or CTE again?

  • Drop table #table

    Drop table #table_with_groupid

    -- Prepare test data

    CREATE TABLE #table

    ([Admissions_key] bigint NOT NULL PRIMARY KEY,

    MRN nvarchar(10) NOT NULL,

    hosp_code nvarchar(10) NOT NULL,

    adm_datetime datetime NOT NULL,

    sep_datetime datetime NOT NULL,

    Sequence nvarchar(10) NOT NULL

    )

    SET DATEFORMAT DMY

    INSERT INTO #table(Admissions_key,MRN, hosp_code, adm_datetime, sep_datetime,Sequence)

    VALUES

    (7099222,'0000544607','0003','22/07/2011 04:55','22/07/2011 10:44','First'),

    (7099223,'0000544607','0003','22/07/2011 10:45','25/07/2011 19:43','Middle'),

    (7099224,'0000544607','0003','25/07/2011 19:44','26/07/2011 11:29','Middle'),

    (7099225,'0000544607','0003','27/07/2011 13:30','27/07/2011 19:30','First'),

    (7099226,'0000544607','0003','27/07/2011 19:36','28/07/2011 11:30','Final'),

    (7099227,'0000559282','0003','03/07/2011 22:50','03/07/2011 23:51','First'),

    (7099228,'0000559282','0003','03/07/2011 23:52','04/07/2011 15:30','Middle'),

    (7099229,'0000559282','0003','04/07/2011 15:33','04/07/2011 17:59','Final'),

    (7099230,'0000559282','0003','05/07/2011 18:00','05/07/2011 18:05','Middle'),

    (7099231,'0000559282','0003','05/07/2011 18:06','09/07/2011 14:58','Final'),

    (7099232,'0000999271','0003','07/08/2011 01:00','07/08/2011 18:05','Middle'),

    (7099233,'0000999271','0003','07/08/2011 18:06','09/08/2011 14:58','Final'),

    (7099234,'0000999271','0003','10/08/2011 18:00','10/08/2011 18:05','First'),

    (7099235,'0000446435','0003','11/08/2011 18:06','12/08/2011 12:08','First'),

    (7099236,'0000446435','0003','12/08/2011 12:09','12/08/2011 14:58','Final')

    ;WITH cur_prev AS

    (-- Match current row with row above

    SELECT cur.*, prev_Sequence = prev.Sequence, pre_sep_datetime = CASE WHEN cur.Sequence='First' THEN NULL ELSE prev.sep_datetime END

    FROM #table cur

    OUTER APPLY -- we use OUTER APPLY instead of CROSS APPLY to get a row even if previous row does not exist

    (-- Find previous row to current row.

    SELECT TOP 1 tt.*

    FROM #table tt

    WHERE tt.MRN = cur.MRN AND tt.hosp_code = cur.hosp_code -- within the same group

    and -- only rows above

    (tt.adm_datetime < cur.adm_datetime

    OR tt.adm_datetime = cur.adm_datetime AND tt.Admissions_key < cur.Admissions_key

    )

    ORDER BY tt.adm_datetime DESC, tt.Admissions_key DESC -- sort should be unique, so we added a PK column

    ) prev

    )

    SELECT c.*,

    GroupID =

    (-- Find the first row above that starts a group

    SELECT TOP 1 cc.Admissions_key

    FROM cur_prev cc

    WHERE cc.MRN = c.MRN AND cc.hosp_code = c.hosp_code -- within the same group

    AND -- only rows above, but this time INCLUDING current row!

    (cc.adm_datetime < c.adm_datetime

    OR cc.adm_datetime = c.adm_datetime AND cc.Admissions_key <= c.Admissions_key

    )

    ORDER BY CASE WHEN cc.Sequence='First' OR cc.prev_Sequence='Final' THEN 0 ELSE 1 END, -- first try to find a row that starts a group

    cc.adm_datetime DESC, cc.Admissions_key DESC -- sort should be unique, so we added a PK column

    )

    INTO #table_with_groupid

    FROM cur_prev c

    SELECT * FROM #table_with_groupid

    SELECT t.*, g.IsGroupCorrect

    FROM #table_with_groupid t

    LEFT JOIN

    (-- Find which group is correct and which is not

    SELECT tg.GroupID,

    IsGroupCorrect = CASE -- correct is group that have 'First' and 'Final' in it.

    WHEN MAX(CASE WHEN tg.Sequence='First' THEN 1 ELSE 0 END)=1 AND MAX(CASE WHEN tg.Sequence='Final' THEN 1 ELSE 0 END)=1

    and MAX(Case When tg.pre_sep_datetime IS NOT NUll then 1 else 0 end) = 1

    and MAX(Case When datediff(MINUTE,tg.pre_sep_datetime,tg.adm_datetime)< =1 then 1 else 0 end) = 1

    THEN 'C'

    ELSE 'E'

    END

    FROM #table_with_groupid tg

    --where datediff(MINUTE,tg.adm_datetime,tg.pre_sep_date)< =1

    GROUP BY tg.GroupID

    ) g on t.GroupID = g.GroupID

  • Within the CTE, where prev_Sequence is defined, you can access any column from previous row. E.g.

    prev_Adm_datetime = prev.Adm_datetime

    Try to learn, not just use the solution. For example, execute inner queries (subqueries) with "*" to get all columns and try to understand how it works. You will benefit more in your knowledge.

    _____________________________________________________
    Microsoft Certified Master: SQL Server 2008
    XDetails Addin - for SQL Developers
    blog.sqlxdetails.com - Transaction log myths
  • Thanks for your quick reply; I have done exactly what you are saying that I have added columns, also trying to learn as much as possible.

    I really appreciate your help.

    Thanks

    Umar.

  • Umar, you are welcome!

    Vedran

    _____________________________________________________
    Microsoft Certified Master: SQL Server 2008
    XDetails Addin - for SQL Developers
    blog.sqlxdetails.com - Transaction log myths
  • Solved the probelm, thank you so much for the guidance you have provided I really learned alot, never used CTE and Outer Apply before

    once again thanks

    Drop table #table

    Drop table #table_with_groupid

    -- Prepare test data

    CREATE TABLE #table

    ([Admissions_key] bigint NOT NULL PRIMARY KEY,

    MRN nvarchar(10) NOT NULL,

    hosp_code nvarchar(10) NOT NULL,

    adm_datetime datetime NOT NULL,

    sep_datetime datetime NOT NULL,

    Sequence nvarchar(10) NOT NULL

    )

    SET DATEFORMAT DMY

    INSERT INTO #table(Admissions_key,MRN, hosp_code, adm_datetime, sep_datetime,Sequence)

    VALUES

    (7099222,'0000544607','0003','22/07/2011 04:55','22/07/2011 10:44','First'),

    (7099223,'0000544607','0003','22/07/2011 10:45','25/07/2011 19:43','Middle'),

    (7099224,'0000544607','0003','25/07/2011 19:44','26/07/2011 11:29','Middle'),

    (7099225,'0000544607','0003','27/07/2011 13:30','27/07/2011 19:30','First'),

    (7099226,'0000544607','0003','27/07/2011 19:31','28/07/2011 11:30','Final'),

    (7099227,'0000559282','0003','03/07/2011 22:50','03/07/2011 23:51','First'),

    (7099228,'0000559282','0003','03/07/2011 23:53','04/07/2011 15:30','Middle'),

    (7099229,'0000559282','0003','04/07/2011 15:31','04/07/2011 17:59','Final'),

    (7099230,'0000559282','0003','05/07/2011 18:00','05/07/2011 18:05','Middle'),

    (7099231,'0000559282','0003','05/07/2011 18:06','09/07/2011 14:58','Final'),

    (7099232,'0000999271','0003','07/08/2011 01:00','07/08/2011 18:05','Middle'),

    (7099233,'0000999271','0003','07/08/2011 18:06','09/08/2011 14:58','Final'),

    (7099234,'0000999271','0003','10/08/2011 18:00','10/08/2011 18:05','First'),

    (7099235,'0000446435','0003','11/08/2011 18:06','12/08/2011 12:08','First'),

    (7099236,'0000446435','0003','12/08/2011 12:13','12/08/2011 14:58','Final')

    ;WITH cur_prev AS

    (-- Match current row with row above

    SELECT cur.*, prev_Sequence = prev.Sequence,

    CASE WHEN (DATEDIFF(minute,prev.sep_datetime,cur.adm_datetime) =1 OR cur.Sequence = 'First') THEN 'Y' ELSE 'N' END AS DateMark

    FROM #table cur

    OUTER APPLY -- we use OUTER APPLY instead of CROSS APPLY to get a row even if previous row does not exist

    (-- Find previous row to current row.

    SELECT TOP 1 tt.*

    FROM #table tt

    WHERE tt.MRN = cur.MRN AND tt.hosp_code = cur.hosp_code -- within the same group

    and -- only rows above

    (tt.adm_datetime < cur.adm_datetime

    OR tt.adm_datetime = cur.adm_datetime AND tt.Admissions_key < cur.Admissions_key

    )

    ORDER BY tt.adm_datetime DESC, tt.Admissions_key DESC -- sort should be unique, so we added a PK column

    ) prev

    )

    SELECT c.*,

    GroupID =

    (-- Find the first row above that starts a group

    SELECT TOP 1 cc.Admissions_key

    FROM cur_prev cc

    WHERE cc.MRN = c.MRN AND cc.hosp_code = c.hosp_code -- within the same group

    AND -- only rows above, but this time INCLUDING current row!

    (cc.adm_datetime < c.adm_datetime

    OR cc.adm_datetime = c.adm_datetime AND cc.Admissions_key <= c.Admissions_key

    )

    ORDER BY CASE WHEN cc.Sequence='First' OR cc.prev_Sequence='Final' THEN 0 ELSE 1 END, -- first try to find a row that starts a group

    cc.adm_datetime DESC, cc.Admissions_key DESC -- sort should be unique, so we added a PK column

    )

    INTO #table_with_groupid

    FROM cur_prev c

    SELECT * FROM #table_with_groupid

    Select * from

    (SELECT t.*, g.IsGroupCorrect

    FROM #table_with_groupid t

    LEFT JOIN

    (-- Find which group is correct and which is not

    SELECT tg.GroupID,

    IsGroupCorrect = CASE -- correct is group that have 'First' and 'Final' in it.

    WHEN MAX(CASE WHEN tg.Sequence='First' THEN 1 ELSE 0 END)=1 AND MAX(CASE WHEN tg.Sequence='Final' THEN 1 ELSE 0 END)=1

    AND Min( case when tg.datemark ='Y' Then 1 else 0 end) = 1

    THEN 'C' ELSE 'E' END

    FROM #table_with_groupid tg

    --where datediff(MINUTE,tg.adm_datetime,tg.pre_sep_date) <= 1

    GROUP BY tg.GroupID

    ) g on t.GroupID = g.GroupID) as a

  • umar.memon (3/19/2012)


    Solved the probelm, thank you so much for the guidance you have provided I really learned alot, never used CTE and Outer Apply before

    once again thanks

    Actually, you may have created a problem that you're not yet aware of. It took a total of 6 table scans (a scan is ok here. Having 6 isn't). Some of the scans have only 15 rows like the original data. Some of them have 24, 33, 39, and as high as 45 rows in the scan. It's not exponential growth but everything will take 2 to 3 times longer in those scans than you probably bargained for not to mention 6 times more I/O than a single scan. I'd actually be a bit surprised if a WHILE loop couldn't do as well for performance which isn't such a bad option at this point.

    So, let me ask... how many rows do you have to do this for, how often do you have to do it, and what is the performance of the current code for Duration, CPU, Reads, Writes, and Rowcounts? I ask because I'd like to setup a test to show you not only the performance that you can get out of a loop on this one, but also the performance you can get out of another method. I need the information above so I can setup the test correctly and do a little result checking prior to posting a solution.

    --Jeff Moden


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.

    Change is inevitable... Change for the better is not.


    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)

Viewing 8 posts - 16 through 22 (of 22 total)

You must be logged in to reply to this topic. Login to reply