Comparing two rows in one table for changes in column

  • The "4th row" does complicate things a bit. You have to reverse the query and run it again and then get rid of the dupes... I think this will be pretty bad on performance but it does get the job done. For the record, a cursor would also have a problem with the "1st row" because of the ascending nature of the problem and the fact that you don't want the first row unless the second row is different...

    WITH

    ctePreNumber AS

    (

    SELECT ROW_NUMBER() OVER (ORDER BY K9ID, MedID, RxStartDate) AS RowNum,

    RxID, K9ID, MedID, VetID, RxStartDate, RxEndDate, Dose, Frequency

    FROM #Rx

    )

    SELECT lo.*

    FROM ctePreNumber lo

    INNER JOIN ctePreNumber hi

    ON lo.RowNum+1 = hi.RowNum

    AND lo.K9ID = hi.K9ID

    AND lo.MedID = hi.MedID

    AND lo.Frequency <> hi.Frequency

    UNION

    SELECT hi.*

    FROM ctePreNumber lo

    INNER JOIN ctePreNumber hi

    ON lo.RowNum+1 = hi.RowNum

    AND lo.K9ID = hi.K9ID

    AND lo.MedID = hi.MedID

    AND lo.Frequency <> hi.Frequency

    Results...

    RowNumRxIDK9IDMedIDVetIDRxStartDateRxEndDateDoseFrequency

    68113112009-07-05 00:00:00.0002009-07-15 00:00:00.0001003

    79113112009-07-15 00:00:00.0002010-01-27 00:00:00.0001004

    1414311112005-09-14 00:00:00.0002006-09-14 00:00:00.000803

    1515311112006-09-14 00:00:00.0002007-09-14 00:00:00.000904

    1616311112007-09-14 00:00:00.0002008-09-14 00:00:00.0001005

    1718311112008-08-14 00:00:00.0002008-09-14 00:00:00.0001256

    20206882010-03-15 00:00:00.0002010-03-17 00:00:00.0002004

    21226882010-03-17 00:00:00.0002010-04-15 00:00:00.0002505

    222161882010-03-15 00:00:00.0002010-03-17 00:00:00.0002004

    232361882010-03-17 00:00:00.0002010-04-15 00:00:00.0002505

    I didn't like the join by inequality on the dates because that formed several triangular joins which cause the 23 rows sample to explode to 105 internally processed rows for each

    --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)

  • Wow, Jeff! I am going to have to study this one long and hard! Thank you so much - I will post back with either more questions or a big, fat YES! I'm sure your code works, but I need to understand it if I am going to learn from it.

    Thanks again!

    Dobermann

  • Dobermann, Jeff, I appologize for being wrong this time. I did not realized in a timely manner the output that Dobermann needs.

    Sorry, guys :blush:

  • dmoldovan (5/12/2010)


    Dobermann, Jeff, I appologize for being wrong this time. I did not realized in a timely manner the output that Dobermann needs.

    Sorry, guys :blush:

    Nah... you're alright. I very much appreciate your humility (one of the signs of an excellent programmer) but it's actually a tougher problem than most people think because, as Dobermann said, you need both the "1st" and the "4th" item (and everything in between) for K9ID #3. Heh... I did the code pretty late at night and was tired and probably shouldn't have posted the code I did because I think performance is going to be pretty bad (4 calls to the same CTE, two self joins) in the face of any scalability. You watch... someone is going to post code that will beat the pants off my code (heh... it might be me later on if I get the chance).

    --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)

  • Dobermann (5/11/2010)


    Wow, Jeff! I am going to have to study this one long and hard! Thank you so much - I will post back with either more questions or a big, fat YES! I'm sure your code works, but I need to understand it if I am going to learn from it.

    Thanks again!

    Dobermann

    The two queries than have been unioned are identical except for which row of a pair of rows they return. The first query compares two rows from the same table (lets say lo.#3 and hi.#4) and returns lo.#3 if the two rows have a different frequency. The second query does EXACTLY the same thing with EXACTLY the same rows except it returns hi.#4.

    Of course, that's going to return some (depending on the data, a lot) of duplicates. For example, for K9ID=3, it will return the middle rows (of the four to be returned twice) (once for each query). The UNION takes care of those duplicates because UNION also does a "distinct" behind the scenes. If you want to see the duplicates, change the UNION to a UNION ALL.

    The CTE that creates a ROW_NUMBER is so that I have a known sequential value to compare against so I don't have to rely on a "Triangular Join" for dates when comparing rows. Instead, I can do the equality comparison of lo.RowNum+1 = hi.RowNum.

    For more information on the devestating effect that "Triangular Joins" can have, please see the following article...

    http://www.sqlservercentral.com/articles/T-SQL/61539/

    The bad part about my code is that I'm "calling" a CTE 4 times which means the CTE will execute 4 times just as if it were a View. An immediate and significant performance improvement can be realized by dumping the output of the CTE to a Temp Table and then using the Temp Table as the source of data... like this...

    SELECT ROW_NUMBER() OVER (ORDER BY K9ID, MedID, RxStartDate) AS RowNum,

    RxID, K9ID, MedID, VetID, RxStartDate, RxEndDate, Dose, Frequency

    INTO #PreNumber

    FROM #Rx

    SELECT lo.*

    FROM #PreNumber lo

    INNER LOOP JOIN #PreNumber hi --"LOOP" hint helps the optimizer make the right choice a LOT here

    ON lo.RowNum+1 = hi.RowNum

    AND lo.K9ID = hi.K9ID

    AND lo.MedID = hi.MedID

    AND lo.Frequency <> hi.Frequency

    UNION ALL

    SELECT hi.*

    FROM #PreNumber lo

    INNER LOOP JOIN #PreNumber hi --"LOOP" hint helps the optimizer make the right choice a LOT here

    ON lo.RowNum+1 = hi.RowNum

    AND lo.K9ID = hi.K9ID

    AND lo.MedID = hi.MedID

    AND lo.Frequency <> hi.Frequency

    Of course, even though that's a fair bit faster than the CTE verision, it's relatively slow because it still needs to look at the same table 4 times and that's going to be relatively slow even if you have some decent indexes on the new temp table (indexes would probably eliminate the need for the "Loop" hint).

    I'll see if I can come up with a WHERE EXISTS example tonight like someone else did on this thread because it might be faster still. I just can't get my head around correlated subqueries late at night or before I've loaded up on coffee in the morning. :hehe:

    --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)

  • Using your insert code, lets look at K9ID 3, just to make it simple.

    How many rows are you expecting to get back? For Dose 100, which record are you wanting to see 16 or 19? I think I know what you are asking but just want to make sure.

  • I offer this...

    ;WITH DATA

    AS

    (

    SELECT

    K9ID,

    RxStartDate,

    MedID,

    Dose,

    Frequency ,

    ROW_NUMBER() OVER ( PARTITION BY K9ID,MedID,Frequency

    ORDER BY K9ID, MedID, RxStartDate )

    AS RowNum,

    MIN(Frequency) OVER ( PARTITION BY K9ID,MedID )

    AS MinFreq,

    MAX(Frequency) OVER ( PARTITION BY K9ID,MedID )

    AS MaxFreq

    FROM #RX

    )

    SELECT

    K9ID,

    RxStartDate,

    MedID,

    Dose,

    Frequency

    FROM DATA

    WHERE RowNum = 1 AND MinFreq<>MaxFreq

    And the result....

    K9ID RxStartDate MedID Dose Frequency

    ----------- ----------- ----------- ------ ---------

    1 2009-07-05 13 100 3

    1 2009-07-15 13 100 4

    3 2005-09-14 11 80 3

    3 2006-09-14 11 90 4

    3 2007-09-14 11 100 5

    3 2008-08-14 11 125 6

    6 2010-03-15 8 200 4

    6 2010-03-17 8 250 5

    6 2010-03-15 18 200 4

    6 2010-03-17 18 250 5

    (10 row(s) affected)

    MM



    select geometry::STGeomFromWKB(0x0106000000020000000103000000010000000B0000001000000000000840000000000000003DD8CCCCCCCCCC0840000000000000003DD8CCCCCCCCCC08408014AE47E17AFC3F040000000000104000CDCCCCCCCCEC3F9C999999999913408014AE47E17AFC3F9C99999999991340000000000000003D0000000000001440000000000000003D000000000000144000000000000000400400000000001040000000000000F03F100000000000084000000000000000401000000000000840000000000000003D0103000000010000000B000000000000000000143D000000000000003D009E99999999B93F000000000000003D009E99999999B93F8014AE47E17AFC3F400000000000F03F00CDCCCCCCCCEC3FA06666666666FE3F8014AE47E17AFC3FA06666666666FE3F000000000000003D1800000000000040000000000000003D18000000000000400000000000000040400000000000F03F000000000000F03F000000000000143D0000000000000040000000000000143D000000000000003D, 0);

  • Forum Etiquette: How to post Reporting Services problems
  • [/url]
  • Forum Etiquette: How to post data/code on a forum to get the best help - by Jeff Moden
  • [/url]
  • How to Post Performance Problems - by Gail Shaw
  • [/url]

  • Just getting an internet connection again (I'm in the boonies with not so much as even dial up). Mister.Magoo, what you did gives perfect results! I have saved it and am going to play around with it and study it.

    I need to join that to a few other tables to get all the columns, but that should be a piece of cake at this point.

    Jeff, your code worked perfectly, but as you say, it uses a lot of resources. You should have seen it by the time I joined it to 5 other tables. I jumped too fast on that, as I ended up putting back in the unwanted rows in my joins. I took a 'stand back' breather from it, and I need to check my join types as they are the cause of the new issue.

    I'm glad in a way to see that it was not just a slam-bam answer that was so very, very simple, as then I would have felt very stupid! 😉

    Thanks again & I will be back with an update,

    Dobermann

  • Dobermann (5/13/2010)


    I'm glad in a way to see that it was not just a slam-bam answer that was so very, very simple, as then I would have felt very stupid! 😉

    Dobermann

    Never stupid if you've learned something and shown the gumption to make attempts at resolving your problems first and to understand suggested resolutions second. We all are learning and if you haven't already noticed, this is usually more than one way to solve your issues. Determining which is best for you and then applying that knowledge down the road for other tasks is a great feeling of accomplishment. Stick around, there is a lot to learn!

    -- You can't be late until you show up.

  • Dobermann (5/13/2010)


    Just getting an internet connection again (I'm in the boonies with not so much as even dial up). Mister.Magoo, what you did gives perfect results! I have saved it and am going to play around with it and study it.

    I need to join that to a few other tables to get all the columns, but that should be a piece of cake at this point.

    Jeff, your code worked perfectly, but as you say, it uses a lot of resources. You should have seen it by the time I joined it to 5 other tables. I jumped too fast on that, as I ended up putting back in the unwanted rows in my joins. I took a 'stand back' breather from it, and I need to check my join types as they are the cause of the new issue.

    I'm glad in a way to see that it was not just a slam-bam answer that was so very, very simple, as then I would have felt very stupid! 😉

    Thanks again & I will be back with an update,

    Dobermann

    BWAA-HHAAA!!!! Yes, I did warn about it but you joined it with 5 other tables and say MY code uses a lot of resources? 😛

    It all depends on how you do the joins and whether or not you use "Divide'n'Conquer" methods.

    --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)

  • tosscrosby (5/14/2010)


    Never stupid if you've learned something and shown the gumption to make attempts at resolving your problems first and to understand suggested resolutions second. We all are learning and if you haven't already noticed, this is usually more than one way to solve your issues. Determining which is best for you and then applying that knowledge down the road for other tasks is a great feeling of accomplishment. Stick around, there is a lot to learn!

    Hey, I love this forum! There is so much to learn here. What else is nice is that I am on PST (West coast, USA) and I am usually up most of the evenings till 3 or 4 am. Quite a few of the participants here are on the opposite time zone as where I live, so it usually means when I am up and working, most of these folks are doing the same.

    Works out great for me!

    Dobermann

  • Jeff Moden (5/14/2010)


    BWAA-HHAAA!!!! Yes, I did warn about it but you joined it with 5 other tables and say MY code uses a lot of resources? 😛

    It all depends on how you do the joins and whether or not you use "Divide'n'Conquer" methods.

    Hey, I know I dumped more on top of it and used even more resources 🙂 But that's the fun of it. :w00t:

    Previously I had thought that Eric's code worked, so I added an ORDER BY clause to it to put the dates in order to make it easier to verify and added a couple more lines of sample data:

    DECLARE @TABLE TABLE

    (

    K9ID INT,

    Drug INT,

    Dose INT,

    Freq INT,

    Date DATETIME

    );

    INSERT INTO @TABLE

    SELECT 1,3,100,5,'2008-10-5' UNION ALL

    SELECT 1,3,150,6,'2008-10-01' UNION ALL

    SELECT 1,3,75,8,'2009-8-14' UNION ALL

    SELECT 1,3,150,8,'2008-7-11' UNION ALL

    SELECT 1,3,60,7, '2008-8-6' UNION ALL

    SELECT 1,3,200,8, '2009-8-22';

    select A.*

    from @TABLE A

    where exists

    (

    select 1

    from @TABLE B

    where b.k9id = a.k9id and b.drug = a.drug and b.date < a.date

    )

    order by k9id, date, drug;

    K9IDDrugDoseFreqDate

    136072008-08-06 00:00:00.000

    1315062008-10-01 00:00:00.000

    1310052008-10-05 00:00:00.000

    137582009-08-14 00:00:00.000

    1320082009-08-22 00:00:00.000

    but sadly when I added more sample data, I can see that it really does not. Rows 4 & 5 are duplicated frequencies still.

    I will break out my "free ginzu cutting knife" and use it on what I added to your code, sparing no lines of mine from death by knife. Like I mentioned before, I am pretty sure it is in the type of joins that I did. I took your code and nested it inside (your code ) AS PITAFREQ to create a single table that I then joined to all the others.

    I will post back as I chop and clean.

    Thanks again for all your help!

    Dobermann

  • BWAA-HAAA!!! Thanks for the feedback, Dobermann. I do have one more trick up my sleeve if all else fails. I've been trying to keep this in the "well documented code" range and not so much in the "black arts" range. But we can do that if performance falls through.

    This has been fun. C'mon back when you get done whittling and let us know how you made out.

    --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)

  • Dobermann (5/14/2010)


    Hey, I know I dumped more on top of it and used even more resources 🙂 But that's the fun of it. :w00t:

    Previously I had thought that Eric's code worked, so I added an ORDER BY clause to it to put the dates in order to make it easier to verify and added a couple more lines of sample data:

    ....

    Dobermann

    DECLARE @TABLE TABLE

    (

    K9ID INT,

    Drug INT,

    Dose INT,

    Freq INT,

    Date DATETIME

    );

    INSERT INTO @TABLE

    SELECT 1,3,100,5,'2008-10-5' UNION ALL

    SELECT 1,3,150,6,'2008-10-01' UNION ALL

    SELECT 1,3,75,8,'2009-8-14' UNION ALL

    SELECT 1,3,150,8,'2008-7-11' UNION ALL

    SELECT 1,3,60,7, '2008-8-6' UNION ALL

    SELECT 1,3,200,8, '2009-8-22';

    ;WITH DATA

    AS

    (

    SELECT

    K9ID,

    Date,

    Drug,

    Dose,

    Freq ,

    ROW_NUMBER() OVER ( PARTITION BY K9ID,Drug,Freq

    ORDER BY K9ID, Drug, Date )

    AS RowNum,

    MIN(Freq) OVER ( PARTITION BY K9ID,Drug )

    AS MinFreq,

    MAX(Freq) OVER ( PARTITION BY K9ID,Drug )

    AS MaxFreq

    FROM @TABLE

    )

    SELECT

    K9ID,

    Date,

    Drug,

    Dose,

    Freq

    FROM DATA

    WHERE RowNum = 1 AND MinFreq<>MaxFreq

    As far as I can see, mine still works...:cool:

    K9ID Date Drug Dose Freq

    ----------- ----------------------- ----------- ----------- -----------

    1 2008-10-05 00:00:00.000 3 100 5

    1 2008-10-01 00:00:00.000 3 150 6

    1 2008-08-06 00:00:00.000 3 60 7

    1 2008-07-11 00:00:00.000 3 150 8

    MM



    select geometry::STGeomFromWKB(0x0106000000020000000103000000010000000B0000001000000000000840000000000000003DD8CCCCCCCCCC0840000000000000003DD8CCCCCCCCCC08408014AE47E17AFC3F040000000000104000CDCCCCCCCCEC3F9C999999999913408014AE47E17AFC3F9C99999999991340000000000000003D0000000000001440000000000000003D000000000000144000000000000000400400000000001040000000000000F03F100000000000084000000000000000401000000000000840000000000000003D0103000000010000000B000000000000000000143D000000000000003D009E99999999B93F000000000000003D009E99999999B93F8014AE47E17AFC3F400000000000F03F00CDCCCCCCCCEC3FA06666666666FE3F8014AE47E17AFC3FA06666666666FE3F000000000000003D1800000000000040000000000000003D18000000000000400000000000000040400000000000F03F000000000000F03F000000000000143D0000000000000040000000000000143D000000000000003D, 0);

  • Forum Etiquette: How to post Reporting Services problems
  • [/url]
  • Forum Etiquette: How to post data/code on a forum to get the best help - by Jeff Moden
  • [/url]
  • How to Post Performance Problems - by Gail Shaw
  • [/url]

  • You could attempt this with a JOIN and a SELECT DISTINCT, but I think that probably a WHERE EXISTS would perform better, based on the assumption that there are maybe less than 100 rows for each execution. Also make sure you have an index covering the K9ID, DRUG, and DATE columns so this is an efficient lookup.

    Hyundai Tucson Parts

  • Viewing 15 posts - 16 through 30 (of 60 total)

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