Home > Mobile >  Creating fields for max value at each level within segment/type combinations
Creating fields for max value at each level within segment/type combinations

Time:02-03

I hope I can convey this clearly, been wracking my brain and can't figure it out. We have monthly historical data in which we calculate median spend for each type and level combination for our two segments.

I want to create two fields that display the most recent med spend value for that level by each segment and type combination.

I started with this, but I know it needs more.

        select Segment, Type, (select max([med spend]) from source where level = 'Gold') as 'Gold Spend',
    (select max([med spend]) from source where level = 'Silver') as 'Silver Spend'
        from source a
where a.date = (select max(b.date) from source b
where b.segment = a.segment and b.type = a.type)

Source Table

Date Segment Type Level Med Spend
December 2022 A 0 Gold 1303
December 2022 A 1 Gold 1500
December 2022 A 0 Silver 1000
December 2022 A 1 Silver 1111
November 2022 A 0 Gold 500
November 2022 A 1 Gold 600
November 2022 A 0 Silver 450
November 2022 A 1 Silver 110
December 2022 B 0 Gold 210
December 2022 B 1 Gold 145
December 2022 B 0 Silver 540
December 2022 B 1 Silver 360
November 2022 B 0 Gold 777
November 2022 B 1 Gold 888
November 2022 B 0 Silver 125
November 2022 B 1 Silver 123

Desired Output

Segment Type Silver Spend Gold Spend
A 0 1000 1303
A 1 1111 1000
B 0 540 210
B 1 360 145

CodePudding user response:

you can use an aggregation with GROUP BY

CREATE TABLE table1
    ([Date] varchar(13), [Segment] varchar(1), [Type] int, [Level] varchar(6), [Med Spend] int)
;
    
INSERT INTO table1
    ([Date], [Segment], [Type], [Level], [Med Spend])
VALUES
    ('December 2022', 'A', 0, 'Gold', 1303),
    ('December 2022', 'A', 1, 'Gold', 1500),
    ('December 2022', 'A', 0, 'Silver', 1000),
    ('December 2022', 'A', 1, 'Silver', 1111),
    ('November 2022', 'A', 0, 'Gold', 500),
    ('November 2022', 'A', 1, 'Gold', 600),
    ('November 2022', 'A', 0, 'Silver', 450),
    ('November 2022', 'A', 1, 'Silver', 110),
    ('December 2022', 'B', 0, 'Gold', 210),
    ('December 2022', 'B', 1, 'Gold', 145),
    ('December 2022', 'B', 0, 'Silver', 540),
    ('December 2022', 'B', 1, 'Silver', 360),
    ('November 2022', 'B', 0, 'Gold', 777),
    ('November 2022', 'B', 1, 'Gold', 888),
    ('November 2022', 'B', 0, 'Silver', 125),
    ('November 2022', 'B', 1, 'Silver', 123)
;
16 rows affected
SELECT
[Segment], [Type],
  SUM(CASE WHEN [Level] = 'Silver' THEN  [Med Spend] ELSE 0 END) Siver_spent,
  SUM(CASE WHEN [Level] = 'Gold' THEN  [Med Spend] ELSe 0 END) Gold_spent
FROM table1
GROUP BY [Segment], [Type]
Segment Type Siver_spent Gold_spent
A 0 1450 1803
B 0 665 987
A 1 1221 2100
B 1 483 1033

fiddle

CodePudding user response:

A windowed ROW_NUMBER() function can be used to prioritize your spend values within each Segment/Type/Level context. From there you can group the results and use "conditional aggregation" to select the top value for each.

One change needed before this can work is to have a sortable date column in your source. A "Monthname-YYYY" column will not work.

SELECT Segment, Type,
    MAX(CASE WHEN Level = 'Silver' AND rn = 1 THEN [Med Spend] END) AS [Silver Spend],
    MAX(CASE WHEN Level = 'Gold' AND rn = 1 THEN [Med Spend] END) AS [Gold Spend]
FROM (
    SELECT T.*,
        ROW_NUMBER() OVER(PARTITION BY T.Segment, T.Type, T.Level ORDER BY T.Date DESC) AS rn
    FROM table1 T
) TN
GROUP BY Segment, Type
ORDER BY Segment, Type

Or, if you prefer, you can use a Common Table Expression (CTE) instead of a subquery:

; WITH CTE1 AS (
    SELECT T.*,
        ROW_NUMBER() OVER(PARTITION BY T.Segment, T.Type, T.Level ORDER BY T.Date DESC) AS rn
    FROM table1 T
)
SELECT Segment, Type,
    MAX(CASE WHEN Level = 'Silver' AND rn = 1 THEN [Med Spend] END) AS [Silver Spend],
    MAX(CASE WHEN Level = 'Gold' AND rn = 1 THEN [Med Spend] END) AS [Gold Spend]
FROM CTE1
GROUP BY Segment, Type
ORDER BY Segment, Type

Results:

Segment Type Silver Spend Gold Spend
A 0 1000 1303
A 1 1111 1500
B 0 540 210
B 1 360 145

The "1500" value is different from the OP expected results, but I believe these results are correct given the requested criteria.

See this db<>fiddle.

  • Related