How to select records only when their status has been changed?

  • I have a product table and my task is to select only the products that had been paid for, but later their status has been changed.

    I need to report the original paid date, the most recent status, and most recent updated date. Here is the sample table:

    CREATE TABLE #Products (primKey int, productId int, productName varchar(100), productStatus varchar(50), logDate datetime )

    Insert into #Products(primKey, productId, productName, productStatus, logDate)

    Values

    (1, 201, 'pen', 'received', '01/01/2011'),

    (2, 201, 'pen', 'sold', '01/02/2011'),

    (3, 201, 'pen', 'paid', '01/03/2011'),

    (4, 201, 'pen', 'returned', '01/04/2011'),

    (5, 201, 'pen', 'refurbished', '01/05/2011'),

    (6, 202, 'pencil', 'received', '01/06/2011'),

    (7, 202, 'pencil', 'sold', '01/07/2011'),

    (8, 202, 'pencil', 'paid', '01/08/2011'),

    (9, 201, 'pen', 'sold', '01/09/2011'),

    (10, 201, 'pen', 'paid', '01/10/2011')

    /* temp table records */

    Select * From #Products order by productId

    /* The desired outcomes would be showing only the Record 3 and 10.

    This is a "fake" query to get the results:

    */

    Select * From #Products Where primKey in (3, 10) order by productId

  • Hmm... well there's only one date in that table, how do you plan to get the original paid date and the most recent change date? And based on the expected results both the ones listed have a status of paid, weren't the requirements records whose status was paid but is now changed?

  • Why should 10 be selected? Its status is paid, but that status has not changed.



    Alvin Ramard
    Memphis PASS Chapter[/url]

    All my SSC forum answers come with a money back guarantee. If you didn't like the answer then I'll gladly refund what you paid for it.

    For best practices on asking questions, please read the following article: Forum Etiquette: How to post data/code on a forum to get the best help[/url]

  • Alvin Ramard (6/9/2015)


    Why should 10 be selected? Its status is paid, but that status has not changed.

    How are you determining whether or not the status value has changed? This question is also good for the original poster, as it is key to the solution.

    Steve (aka sgmunson) πŸ™‚ πŸ™‚ πŸ™‚
    Rent Servers for Income (picks and shovels strategy)

  • Sorry for the confusion. Let me try my best to clarify the question a little.

    Some products we sold were returned by customers, and we'd refurbish them and put them on shelf again. In the original temp table, the first PAID status is considered as initial completed transaction. In theory, a product can be sold and returned multiple times. The management would like to know that once a product is sold and paid, what is the most recent status for that product, which could be anything ranging from RECEIVED to PAID.

    The issue is due to that in the original DB design, there is no RETURNED status.

    Thanks all.

  • jay-125866 (6/10/2015)


    Sorry for the confusion. Let me try my best to clarify the question a little.

    Some products we sold were returned by customers, and we'd refurbish them and put them on shelf again. In the original temp table, the first PAID status is considered as initial completed transaction. In theory, a product can be sold and returned multiple times. The management would like to know that once a product is sold and paid, what is the most recent status for that product, which could be anything ranging from RECEIVED to PAID.

    The issue is due to that in the original DB design, there is no RETURNED status.

    Thanks all.

    I'm not sure this is going to work, but take a look at the query and the result:

    CREATE TABLE #Products (primKey int, productId int, productName varchar(100), productStatus varchar(50), logDate datetime )

    Insert into #Products(primKey, productId, productName, productStatus, logDate)

    Values

    (1, 201, 'pen', 'received', '01/01/2011'),

    (2, 201, 'pen', 'sold', '01/02/2011'),

    (3, 201, 'pen', 'paid', '01/03/2011'),

    (4, 201, 'pen', 'returned', '01/04/2011'),

    (5, 201, 'pen', 'refurbished', '01/05/2011'),

    (6, 202, 'pencil', 'received', '01/06/2011'),

    (7, 202, 'pencil', 'sold', '01/07/2011'),

    (8, 202, 'pencil', 'paid', '01/08/2011'),

    (9, 201, 'pen', 'sold', '01/09/2011'),

    (10, 201, 'pen', 'paid', '01/10/2011')

    SELECT P.*

    FROM #Products AS P

    LEFT OUTER JOIN #Products AS P2

    ON P.productId = P2.productId

    AND P.primKey <> P2.primKey

    AND P.logDate < P2.logDate

    AND P2.productStatus = 'paid'

    LEFT OUTER JOIN #Products AS P3

    ON P.productId = P3.productId

    AND P.primKey <> P3.primKey

    AND P.logDate > P3.logDate

    AND P3.productStatus = 'paid'

    WHERE P.productStatus = 'paid'

    AND COALESCE(P2.productId, P3.productId) IS NOT NULL;

    DROP TABLE #Products;

    What do you do when you sell more than one pen ? Does a product sold have a serial number or other unique identifier? ProductId might just indicate that you sold a pen, but you might sell them 100 at a time...

    Steve (aka sgmunson) πŸ™‚ πŸ™‚ πŸ™‚
    Rent Servers for Income (picks and shovels strategy)

  • I hope this is a homework question. I have trouble imagining the data presented as realistic.



    Alvin Ramard
    Memphis PASS Chapter[/url]

    All my SSC forum answers come with a money back guarantee. If you didn't like the answer then I'll gladly refund what you paid for it.

    For best practices on asking questions, please read the following article: Forum Etiquette: How to post data/code on a forum to get the best help[/url]

  • Thank you for your reply, Steve.

    I've tried your suggestion, and it didn't work for me. I think your query is somehow based on status, so if I remove the last row in sample table, it returns nothing.

  • jay-125866 (6/9/2015)


    ...select only the products that had been paid for, but later their status has been changed.

    I need to report the original paid date, the most recent status, and most recent updated date

    That would be rows 3, 8 and 10.

    Edit: scrub that, it's 3 and 10.

    jay-125866 (6/10/2015)


    ...In the original temp table, the first PAID status is considered as initial completed transaction. In theory, a product can be sold and returned multiple times. The management would like to know that once a product is sold and paid, what is the most recent status for that product, which could be anything ranging from RECEIVED to PAID...

    An accurate and complete specification isn't necessary to get the job done but it does save a lot of time. This query satisfies your first requirement and kinda satisfies your second, maybe:

    SELECT primKey, productId, productName, productStatus, logDate

    FROM #Products

    WHERE productStatus = 'paid'

    UNION

    SELECT primKey, productId, productName, productStatus, logDate

    FROM (SELECT *, RN = ROW_NUMBER() OVER(PARTITION BY productId ORDER BY logDate DESC) FROM #Products) d

    WHERE rn = 1

    ORDER BY productId, logDate

    β€œWrite the query the simplest way. If through testing it becomes clear that the performance is inadequate, consider alternative query forms.” - Gail Shaw

    For fast, accurate and documented assistance in answering your questions, please read this article.
    Understanding and using APPLY, (I) and (II) Paul White
    Hidden RBAR: Triangular Joins / The "Numbers" or "Tally" Table: What it is and how it replaces a loop Jeff Moden

  • Try this:

    ;WITH PaidProducts AS (

    SELECT *,

    rn1 = ROW_NUMBER() OVER (PARTITION BY productId ORDER BY logDate DESC),

    rn2 = CASE ProductStatus WHEN 'paid' THEN ROW_NUMBER() OVER (PARTITION BY productId, ProductStatus ORDER BY logDate) ELSE NULL END

    FROM #Products

    )

    SELECT primKey, productId, productName, productStatus, logDate

    FROM PaidProducts p

    WHERE (rn1 = 1 OR rn2 = 1)

    AND rn1 <> rn2

    ORDER BY productId, logDate

    β€œWrite the query the simplest way. If through testing it becomes clear that the performance is inadequate, consider alternative query forms.” - Gail Shaw

    For fast, accurate and documented assistance in answering your questions, please read this article.
    Understanding and using APPLY, (I) and (II) Paul White
    Hidden RBAR: Triangular Joins / The "Numbers" or "Tally" Table: What it is and how it replaces a loop Jeff Moden

  • Thank you, Chris. Your solution works about right.

    The issue remaining is that the Product Pencil is also shown in the result. It's not supposed to be there since there is no actions after initial completed sales.

    Again, thanks to all!

  • jay-125866 (6/10/2015)


    Thank you, Chris. Your solution works about right.

    The issue remaining is that the Product Pencil is also shown in the result. It's not supposed to be there since there is no actions after initial completed sales.

    Again, thanks to all!

    I think I now understand what you're looking for, so here's an updated attempt that produces the correct result:

    CREATE TABLE #Products (primKey int, productId int, productName varchar(100), productStatus varchar(50), logDate datetime )

    Insert into #Products(primKey, productId, productName, productStatus, logDate)

    Values

    (1, 201, 'pen', 'received', '01/01/2011'),

    (2, 201, 'pen', 'sold', '01/02/2011'),

    (3, 201, 'pen', 'paid', '01/03/2011'),

    (4, 201, 'pen', 'returned', '01/04/2011'),

    (5, 201, 'pen', 'refurbished', '01/05/2011'),

    (6, 202, 'pencil', 'received', '01/06/2011'),

    (7, 202, 'pencil', 'sold', '01/07/2011'),

    (8, 202, 'pencil', 'paid', '01/08/2011'),

    (9, 201, 'pen', 'sold', '01/09/2011'),

    (10, 201, 'pen', 'paid', '01/10/2011');

    WITH PAID AS (

    SELECT P.*

    FROM #Products AS P

    WHERE P.productStatus = 'paid'

    AND EXISTS (

    SELECT 1

    FROM #Products AS P2

    WHERE P2.productId = P.productId

    AND P2.primKey <> P.primKey

    AND P2.logDate > P.logDate

    )

    )

    SELECT *

    FROM PAID

    UNION ALL

    SELECT P3.*

    FROM #Products AS P3

    INNER JOIN PAID AS P1

    ON P3.productId = P1.productId

    AND P3.primKey <> P1.primKey

    AND P3.logDate = (

    SELECT MAX(P4.logDate)

    FROM #Products AS P4

    WHERE P4.productId = P1.productId

    AND P4.primKey <> P1.primKey

    AND P4.logDate > P1.logDate);

    DROP TABLE #Products;

    Let me know...

    Steve (aka sgmunson) πŸ™‚ πŸ™‚ πŸ™‚
    Rent Servers for Income (picks and shovels strategy)

  • This might work too using sgmunson's setup data:

    WITH AddRowNumber AS

    (

    SELECT *

    ,rn1=ROW_NUMBER() OVER (PARTITION BY productid ORDER BY logDate DESC)

    FROM #Products

    ),

    StatusAfterPaid AS

    (

    SELECT productId

    FROM

    (

    SELECT productId

    ,rn2=rn1 -

    ROW_NUMBER() OVER

    (

    PARTITION BY productid

    ORDER BY CASE ProductStatus WHEN 'paid' THEN 0 ELSE 1 END, logDate DESC

    )

    FROM AddRowNumber

    ) a

    WHERE rn2 < 0

    GROUP BY productID

    )

    SELECT b.*

    FROM StatusAfterPaid a

    CROSS APPLY

    (

    SELECT *

    FROM AddRowNumber b

    WHERE a.productID = b.productID AND

    (rn1 = 1 OR productStatus = 'paid')

    ) b;


    My mantra: No loops! No CURSORs! No RBAR! Hoo-uh![/I]

    My thought question: Have you ever been told that your query runs too fast?

    My advice:
    INDEXing a poor-performing query is like putting sugar on cat food. Yeah, it probably tastes better but are you sure you want to eat it?
    The path of least resistance can be a slippery slope. Take care that fixing your fixes of fixes doesn't snowball and end up costing you more than fixing the root cause would have in the first place.

    Need to UNPIVOT? Why not CROSS APPLY VALUES instead?[/url]
    Since random numbers are too important to be left to chance, let's generate some![/url]
    Learn to understand recursive CTEs by example.[/url]
    [url url=http://www.sqlservercentral.com/articles/St

  • jay-125866 (6/10/2015)


    Thank you, Chris. Your solution works about right.

    The issue remaining is that the Product Pencil is also shown in the result. It's not supposed to be there since there is no actions after initial completed sales.

    Again, thanks to all!

    Try the second one.

    β€œWrite the query the simplest way. If through testing it becomes clear that the performance is inadequate, consider alternative query forms.” - Gail Shaw

    For fast, accurate and documented assistance in answering your questions, please read this article.
    Understanding and using APPLY, (I) and (II) Paul White
    Hidden RBAR: Triangular Joins / The "Numbers" or "Tally" Table: What it is and how it replaces a loop Jeff Moden

  • This is great, Thanks, Steve!

Viewing 15 posts - 1 through 15 (of 15 total)

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