I can’t calculate subtotal and total using GROUP BY ROLLUP
syntax in a SQL Server 2019 database:
CREATE TABLE [dbo].[G_Dashboard]
(
[orderId] int NULL,
[C_CO] varchar(4) NULL,
[D_CO] varchar(50) NULL,
[Total] int NULL,
[A_RG] int NULL,
[B_RG] int NULL,
[C_RG] int NULL,
[D_RG] int NULL,
[E_RG] int NULL,
[DateHour] datetime NULL,
)
INSERT INTO [dbo].[G_Dashboard] ([C_CO], [D_CO], [Total], [A_RG], [B_RG], [C_RG], [D_RG], [E_RG], [DateHour], [orderId])
VALUES (N'710', N'CGL', N'4', N'3', N'0', N'1', N'0', N'0', N'2025-05-26 17:51:00.000', N'1')
INSERT INTO [dbo].[G_Dashboard] ([C_CO], [D_CO], [Total], [A_RG], [B_RG], [C_RG], [D_RG], [E_RG], [DateHour], [orderId])
VALUES (N'810', N'PLR', N'8', N'8', N'0', N'0', N'0', N'0', N'2025-05-26 17:51:00.000', N'2')
INSERT INTO [dbo].[G_Dashboard] ([C_CO], [D_CO], [Total], [A_RG], [B_RG], [C_RG], [D_RG], [E_RG], [DateHour], [orderId])
VALUES (N'830', N'CTN', N'10', N'10', N'0', N'0', N'0', N'0', N'2025-05-26 17:51:00.000', N'2')
INSERT INTO [dbo].[G_Dashboard] ([C_CO], [D_CO], [Total], [A_RG], [B_RG], [C_RG], [D_RG], [E_RG], [DateHour], [orderId])
VALUES (N'E10', N'BLG', N'2', N'1', N'0', N'1', N'0', N'0', N'2025-05-26 17:51:00.000', N'3')
INSERT INTO [dbo].[G_Dashboard] ([C_CO], [D_CO], [Total], [A_RG], [B_RG], [C_RG], [D_RG], [E_RG], [DateHour], [orderId])
VALUES (N'E40', N'MDN', N'2', N'1', N'0', N'1', N'0', N'0', N'2025-05-26 17:51:00.000', N'3')
I need this result set returned:
C | C_CO | orderid | SalesTotal |
---|---|---|---|
7 | 710 | 1 | 4 |
7 | 7 | subtotal | 4 |
8 | 810 | 2 | 8 |
8 | 830 | 2 | 10 |
8 | 8 | subtotal | 18 |
E | E10 | 3 | 2 |
E | E40 | 3 | 2 |
E | E | subtotal | 4 |
total | 26 |
Instead I get this: DB FIDDLE
SELECT
LEFT(C_CO, 1) C,
C_CO,
orderid,
SUM(Total) AS SalesTotal
FROM
[dbo].[G_Dashboard]
GROUP BY
C_CO,
ROLLUP(LEFT(C_CO, 1), orderid);
How do I resolve this problem?
I would use GROUPING SETS
for this. You can then use the GROUPING
function to see if the value is being grouped on, to adjust the value for the orderid
column, and the order of your results. For ease, I also move the expression for C
into a CROSS APPLY
.
This then results in the following query:
SELECT V.C AS C,
ISNULL(C_CO,V.C) AS C_CO,
CASE WHEN GROUPING(G.C_CO) = 0 THEN CONVERT(varchar(20),G.orderId)
WHEN GROUPING(V.C) = 0 THEN 'SubTotal'
ELSE 'Total'
END AS Orderid,
SUM(G.Total) AS SalesTotal
FROM [dbo].[G_Dashboard] G
CROSS APPLY(VALUES(LEFT(G.C_CO, 1)))V(C)
GROUP BY GROUPING SETS((G.C_CO, G.orderId, V.C), --Effectively not aggregated, but would be one row per orderid and C_CO
(V.C), --Grouped at prefix level; the SubTotal
()) --No columns are grouped on, the Total
ORDER BY GROUPING(V.C),
V.C;