Home > database >  Add Total Row in SQL Query (Pivot)
Add Total Row in SQL Query (Pivot)

Time:02-06

We are using a asset management software and unfortunatly unable to get the proper support in DIY implementation. I was looking to get an asset report (Laptop Count based on model and state).

With the help of google I manage to create a pivot for the required report. Now, I am stuck with the last part of the task. which is to get a total row for asset state e.g. (In - Store, New - In Store) at the bottom.

This is the Query and I am unable to understand how to add the total row.

SELECT 
  *, 
  ISNULL([In Use], 0.)   ISNULL([Used - In Store], 0.)   ISNULL([In Store], 0.)   ISNULL([New - In Store], 0.)   ISNULL([Damaged], 0.)   ISNULL([Faulty], 0.) AS TOTAL 
FROM 
  (
    SELECT 
      max("product"."COMPONENTNAME") AS "Product", 
      max("state"."DISPLAYSTATE") AS "Asset State", 
      count("resource"."RESOURCENAME") AS "Asset Count" 
    FROM 
      "Resources" "resource" 
      LEFT JOIN "ComponentDefinition" "product" ON "resource"."COMPONENTID" = "product"."COMPONENTID" 
      LEFT JOIN "ResourceState" "state" ON "resource"."RESOURCESTATEID" = "state"."RESOURCESTATEID" 
      LEFT JOIN "ResourceOwner" "rOwner" ON "resource"."RESOURCEID" = "rOwner"."RESOURCEID" 
      LEFT JOIN "ResourceAssociation" "rToAsset" ON "rOwner"."RESOURCEOWNERID" = "rToAsset"."RESOURCEOWNERID" 
      LEFT JOIN "SDUser" "sdUser" ON "rOwner"."USERID" = "sdUser"."USERID" 
      LEFT JOIN "AaaUser" "aaaUser" ON "sdUser"."USERID" = "aaaUser"."USER_ID" 
    WHERE 
      product.COMPONENTNAME LIKE ('Thinkpad%') 
    GROUP BY 
      "product"."COMPONENTNAME", 
      "state"."DISPLAYSTATE"
  ) d pivot (
    sum("Asset Count") for "Asset State" in (
      [In Use], [Used - In Store], [In Store], 
      [New - In Store], [Damaged], [Faulty]
    )
  ) piv

enter image description here

CodePudding user response:

You could use 'UNION' to join two queries together to give you a total row; it'd be something like:

SELECT *, ISNULL([In Use], 0.)   ISNULL([Used - In Store], 0.)   ISNULL([In Store], 0.)   ISNULL([New - In Store], 0.)   ISNULL([Damaged], 0.)   ISNULL([Faulty], 0.) AS TOTAL
FROM (
SELECT max("product"."COMPONENTNAME") AS "Product", max("state"."DISPLAYSTATE") AS "Asset State", count("resource"."RESOURCENAME") AS "Asset Count" FROM "Resources" "resource"
LEFT JOIN "ComponentDefinition" "product" ON "resource"."COMPONENTID"="product"."COMPONENTID"
LEFT JOIN "ResourceState" "state" ON "resource"."RESOURCESTATEID"="state"."RESOURCESTATEID"
LEFT JOIN "ResourceOwner" "rOwner" ON "resource"."RESOURCEID"="rOwner"."RESOURCEID" LEFT JOIN "ResourceAssociation" "rToAsset" ON "rOwner"."RESOURCEOWNERID"="rToAsset"."RESOURCEOWNERID" LEFT JOIN "SDUser" "sdUser" ON "rOwner"."USERID"="sdUser"."USERID" LEFT JOIN "AaaUser" "aaaUser" ON "sdUser"."USERID"="aaaUser"."USER_ID"
WHERE product.COMPONENTNAME LIKE 'ThinkPad %'
GROUP BY "product"."COMPONENTNAME","state"."DISPLAYSTATE")d

UNION

SELECT "Totals:", ISNULL([In Use], 0.)   ISNULL([Used - In Store], 0.)   ISNULL([In Store], 0.)   ISNULL([New - In Store], 0.)   ISNULL([Damaged], 0.)   ISNULL([Faulty], 0.) AS TOTAL
FROM (
SELECT max("product"."COMPONENTNAME") AS "Product", max("state"."DISPLAYSTATE") AS "Asset State", count("resource"."RESOURCENAME") AS "Asset Count" FROM "Resources" "resource"
LEFT JOIN "ComponentDefinition" "product" ON "resource"."COMPONENTID"="product"."COMPONENTID"
LEFT JOIN "ResourceState" "state" ON "resource"."RESOURCESTATEID"="state"."RESOURCESTATEID"
LEFT JOIN "ResourceOwner" "rOwner" ON "resource"."RESOURCEID"="rOwner"."RESOURCEID" LEFT JOIN "ResourceAssociation" "rToAsset" ON "rOwner"."RESOURCEOWNERID"="rToAsset"."RESOURCEOWNERID" LEFT JOIN "SDUser" "sdUser" ON "rOwner"."USERID"="sdUser"."USERID" LEFT JOIN "AaaUser" "aaaUser" ON "sdUser"."USERID"="aaaUser"."USER_ID"
WHERE product.COMPONENTNAME LIKE 'ThinkPad %'
GROUP BY "state"."DISPLAYSTATE")d

CodePudding user response:

You can use GROUP BY ... WITH CUBE to automatically generate totals in both dimensions. This will add a totals row and will also eliminate the need to explicitly calculate the totals column.

SELECT piv.*
FROM (
    SELECT 
      ISNULL(product."COMPONENTNAME", 'Totals:') AS "Product", 
      ISNULL(state."DISPLAYSTATE", 'Total') AS "Asset State", 
      count(resource."RESOURCENAME") AS "Asset Count" 
    FROM 
      "Resources" "resource" 
      LEFT JOIN "ComponentDefinition" "product" ON "resource"."COMPONENTID" = "product"."COMPONENTID" 
      LEFT JOIN "ResourceState" "state" ON "resource"."RESOURCESTATEID" = "state"."RESOURCESTATEID" 
      LEFT JOIN "ResourceOwner" "rOwner" ON "resource"."RESOURCEID" = "rOwner"."RESOURCEID" 
      LEFT JOIN "ResourceAssociation" "rToAsset" ON "rOwner"."RESOURCEOWNERID" = "rToAsset"."RESOURCEOWNERID" 
      LEFT JOIN "SDUser" "sdUser" ON "rOwner"."USERID" = "sdUser"."USERID" 
      LEFT JOIN "AaaUser" "aaaUser" ON "sdUser"."USERID" = "aaaUser"."USER_ID" 
    WHERE 
      product.COMPONENTNAME LIKE ('Thinkpad%') 
    GROUP BY 
      product."COMPONENTNAME", 
      state."DISPLAYSTATE"
      WITH CUBE
) d
pivot (
    sum("Asset Count") for "Asset State" in (
      [In Use], [Used - In Store], [In Store], 
      [New - In Store], [Damaged], [Faulty],
      [Total])
) piv
ORDER BY
    CASE WHEN piv.Product = 'Totals:' THEN 2 ELSE 1 END,
    piv.Product

ISNULL() is used to assign labels to the grouped product and status names, which would otherwise be null. [Total] has also been added to the pivot list and removed from the final select list. You may need to edit the select list to add ISNULL() functions if you want to replace null values with zeros. (E.g., ISNULL([In Use], 0) AS [In Use], ...)

Results:

Product In Use Used - In Store In Store New - In Store Damaged Faulty Total
Thinkpad 101 2 2 3 1 1 1 10
Thinkpad 102 1 null null null null null 1
Thinkpad 103 null null null null 1 1 2
Totals: 3 2 3 1 2 2 13

You can also code a similar result using GROUP BY ... WITH ROLLUP together with "conditional aggregation" as a replacement for the pivot.

SELECT
    ISNULL(product.COMPONENTNAME, 'Total') AS Product,
    COUNT(CASE WHEN state.DISPLAYSTATE = 'In Use' THEN 1 END) AS [In Use],
    COUNT(CASE WHEN state.DISPLAYSTATE = 'Used - In Store' THEN 1 END) AS [Used - In Store],
    COUNT(CASE WHEN state.DISPLAYSTATE = 'In Store' THEN 1 END) AS [In Store],
    COUNT(CASE WHEN state.DISPLAYSTATE = 'New - In Store' THEN 1 END) AS [New - In Store],
    COUNT(CASE WHEN state.DISPLAYSTATE = 'Damaged' THEN 1 END) AS [Damaged],
    COUNT(CASE WHEN state.DISPLAYSTATE = 'Faulty' THEN 1 END) AS [Faulty],
    COUNT(*) AS Total
FROM 
      "Resources" "resource" 
      LEFT JOIN "ComponentDefinition" "product" ON "resource"."COMPONENTID" = "product"."COMPONENTID" 
      LEFT JOIN "ResourceState" "state" ON "resource"."RESOURCESTATEID" = "state"."RESOURCESTATEID" 
      LEFT JOIN "ResourceOwner" "rOwner" ON "resource"."RESOURCEID" = "rOwner"."RESOURCEID" 
      LEFT JOIN "ResourceAssociation" "rToAsset" ON "rOwner"."RESOURCEOWNERID" = "rToAsset"."RESOURCEOWNERID" 
      LEFT JOIN "SDUser" "sdUser" ON "rOwner"."USERID" = "sdUser"."USERID" 
      LEFT JOIN "AaaUser" "aaaUser" ON "sdUser"."USERID" = "aaaUser"."USER_ID" 
    WHERE 
      product.COMPONENTNAME LIKE ('Thinkpad%') 
GROUP BY D.COMPONENTNAME WITH ROLLUP
ORDER BY
    CASE WHEN GROUPING(product.COMPONENTNAME) = 0 THEN 1 ELSE 2 END,
    product.COMPONENTNAME

Results:

Product In Use Used - In Store In Store New - In Store Damaged Faulty Total
Thinkpad 101 2 2 3 1 1 1 10
Thinkpad 102 1 0 0 0 0 0 1
Thinkpad 103 0 0 0 0 1 1 2
Total 3 2 3 1 2 2 13

See this db<>fiddle for examples of both using simplified test data.

  • Related