sqlsql-serverpivot-table

Crosstab query get price of last date from table


I have three table:

Person

Table of Person

ProductGroup

Table of ProductGroup

Price of each ProductGroup for Person with date

Pricing

I want below result that get price of last date of each ProductGroup

Table of Result

Help me please.

I get result by create table and then set cell by cell

But I want this in one query


Solution

  • For logic to select the latest Price for each ProductGroupID and PersonID combination, see Get top 1 row of each group. The general technique is to use the ROW_NUMBER() window function to number the rows within each group, and then filter for row-number = 1.

    After you have that, it is just a matter of performing a PIVOT operation.

    SELECT PVT.[GROUP C], PVT.[GROUP B], PVT.[GROUP A], PVT.FullName, PVT.PersonId
    FROM (
        SELECT PG.ProductGroupName, PGP.Price, P.FullName, P.PersonId
        FROM (
            SELECT
                *,
                ROW_NUMBER()
                    OVER(PARTITION BY ProductGroupID, PersonID ORDER BY PriceDate DESC)
                    AS RowNum
            FROM ProductGroupPrice
        ) PGP
        JOIN ProductGroup PG
            ON PG.ProductGroupID = PGP.ProductGroupID
        JOIN person P
            ON P.PersonID = PGP.PersonID
        WHERE PGP.RowNum = 1
    ) Q
    PIVOT (
        MAX(Q.Price)
        FOR Q.ProductGroupName IN ([GROUP C], [GROUP B], [GROUP A])
    ) PVT
    ORDER BY PVT.PersonId
    

    An alternative to using PIVOT is conditional aggregation, where you define select items like MAX(CASE WHEN <condition> THEN <value> END) AS <column>.

    SELECT
        MAX(CASE WHEN PG.ProductGroupName = 'GROUP C' THEN PGP.Price END) AS [GROUP C],
        MAX(CASE WHEN PG.ProductGroupName = 'GROUP B' THEN PGP.Price END) AS [GROUP B],
        MAX(CASE WHEN PG.ProductGroupName = 'GROUP A' THEN PGP.Price END) AS [GROUP A],
        P.FullName,
        P.PersonId
    FROM (
        SELECT
            *,
            ROW_NUMBER()
                OVER(PARTITION BY ProductGroupID, PersonID ORDER BY PriceDate DESC)
                AS RowNum
        FROM ProductGroupPrice
    ) PGP
    JOIN ProductGroup PG
        ON PG.ProductGroupID = PGP.ProductGroupID
    JOIN person P
        ON P.PersonID = PGP.PersonID
    WHERE PGP.RowNum = 1
    GROUP BY P.FullName, P.PersonId
    ORDER BY P.PersonId
    

    Intermediate results:

    ProductGroupName Price FullName PersonId
    Group A 250 Karel 1
    Group A 120 Ali 3
    Group A 100 Emran 4
    Group B 150 Karel 1
    Group B 200 Jak 2
    Group C 150 Karel 1
    Group C 100 Jak 2
    Group C 105 Hana 5

    Final results:

    GROUP C GROUP B GROUP A FullName PersonId
    150 150 250 Karel 1
    100 200 null Jak 2
    null null 120 Ali 3
    null null 100 Emran 4
    105 null null Hana 5

    See this db<>fiddle for a demo of both approaches.