SQL - Using CUBE grand total per row - sql

Here is my code:
SELECT
ISNULL (CONVERT(VARCHAR, MONTH(PurchaseDate)), NULL) [Month],
ISNULL (Brand, CASE
WHEN MONTH(PurchaseDate) IS NOT NULL THEN 'Monthly SubTotal'
WHEN Brand IS NULL THEN 'Grand Total'
ELSE 'N/A'
END) [Brand], SUM(Price) [Total Amount]
FROM
[dbo].[Purchase_Items]
GROUP BY
MONTH(PurchaseDate), Brand WITH CUBE
I want to change it to Grand Total on selected box. How to code it or change the string on it.

If you first get your data (a simpler version of what you have above) you can then use that as a data source to do conversions/updates as needed.
I'm using a CTE here, but you can do it with subqueries just as well.
WITH MonthTotals AS
(SELECT
MONTH(PurchaseDate) [Month],
[Brand],
SUM(Price) [Total Amount]
FROM [dbo].[Purchase_Items]
GROUP BY MONTH(PurchaseDate), Brand WITH CUBE
)
SELECT CONVERT(VARCHAR(2), mt.[Month]) AS [Month],
CASE WHEN mt.[Month] IS NULL AND mt.[Brand] IS NULL THEN 'Grand Total'
WHEN mt.[Month] IS NULL THEN 'Grand total for ' + mt.[Brand]
WHEN mt.[Brand] IS NULL THEN 'Monthly total'
ELSE mt.[Brand] END AS [Brand]
[Total Amount]
FROM MonthTotals mt;
Note though that CUBE is usually done in SQL Server like the following - it means you can select which columns you CUBE by (or rollup, etc)
GROUP BY CUBE(MONTH(PurchaseDate), Brand)
IMPORTANT UPDATE following #MartinSmith's comment below
Martin Smith gave the advice that I should use the GROUPING function. In reviewing that function, he is 100% correct (and thankyou Martin - this is my learning for today).
For reference, the GROUPING function indicates (with a 1 or 0) whether the row is an aggregate row or not (e.g., one of the rows added by ROLLUP/CUBE/GROUPING SETs).
I also made a mistake with subtotals for months - put it in the wrong column.
Therefore, the update should be the following (note also that I have included the 'original' vales from the CUBE for month and brand as well)
WITH MonthTotals AS
(SELECT
MONTH(PurchaseDate) [Month],
[Brand],
SUM(Price) [Total Amount],
GROUPING(MONTH(PurchaseDate)) AS Agg_flag_Month,
GROUPING([Brand]) AS Agg_flag_Brand
FROM [dbo].[Purchase_Items]
GROUP BY CUBE(MONTH(PurchaseDate), Brand)
)
SELECT [Month] AS Orig_Month,
[Brand] AS Orig_Brand,
CASE WHEN Agg_flag_Month = 1 THEN 'Grand total for ' + mt.[Brand]
ELSE CONVERT(VARCHAR(2), mt.[Month])
END AS [Month],
CASE WHEN Agg_flag_Month * Agg_flag_Brand = 1 THEN 'Grand Total'
WHEN Agg_flag_Brand = 1 THEN 'Monthly total'
ELSE mt.[Brand]
END AS [Brand],
[Total Amount]
FROM MonthTotals mt;

Related

How to add a new column that summarize rows

I have two issues :
I used 'Rollup' function to add Totals per Month and Year and I would like to change 'NULL' into grand_total as in the attached screenshot
I dont know how to add a new column that will summarize values starting from the second row
Please see attached screenshot of the results I need to receive and an example for a code from my side with a screenshot of the source output : [1]: https://i.stack.imgur.com/6B70o.png
[1]: https://i.stack.imgur.com/E2x8K.png
Select Year(Modifieddate) AS Year,
MONTH(modifieddate) as Month,
Sum(linetotal) as Sum_price
from Sales.SalesOrderDetail
Group by rollup( Year(Modifieddate),MONTH(modifieddate))
Thanks in advance,
I think this will work:
Select Year(Modifieddate) AS Year,
coalesce(convert(varchar(255), month(modifieddate)), 'Grand Total') as Month,
Sum(linetotal) as Sum_price,
sum(sum(linetotal)) over (partition by Year(Modifieddate)
order by coalesce(month(modifieddate), 100)
) as ytd_sum_price
from Sales.SalesOrderDetail
Group by rollup( Year(Modifieddate), month(modifieddate))
The coalesce() in the order by is to put the summary row last for the cumulative sum.
Like this:
Select Year(Modifieddate) AS Year, MONTH(modifieddate) as Month, Sum(linetotal) as Sum_price
from Sales.SalesOrderDetail
Group by rollup( Year(Modifieddate),MONTH(modifieddate))
UNION
Select Year(Modifieddate) AS Year, 'grand_total' as Month, Sum(linetotal) as Sum_price
from Sales.SalesOrderDetail
Group by Year(Modifieddate)
-- SQL SERVER
SELECT t.OrderYear
, CASE WHEN t.OrderMonth IS NULL THEN 'Grand Total' ELSE CAST(t.OrderMonth AS VARCHAR(20)) END b
, t.MonthlySales
, MAX(t.cum_total) cum_total
FROM (SELECT
YEAR(OrderDate) AS OrderYear,
MONTH(OrderDate) AS OrderMonth,
SUM(SubTotal) AS MonthlySales,
SUM(SUM(SubTotal)) OVER (ORDER BY YEAR(OrderDate), MONTH(OrderDate) ROWS UNBOUNDED PRECEDING) cum_total
FROM Sales.SalesOrderHeader
GROUP BY GROUPING SETS ((YEAR(OrderDate), MONTH(OrderDate)))) t
GROUP BY GROUPING SETS ((t.OrderYear
, t.OrderMonth
, t.MonthlySales), t.OrderYear);
Please check this url https://dbfiddle.uk/?rdbms=sqlserver_2019&sample=adventureworks&fiddle=e6cd2ba8114bd1d86b8c61b1453cafcf
To build one #GordonLinoff's answer, you are really supposed to use the GROUPING() function to check whether you are dealing with the grouping column. This behaves better in the face of nullable columns.
Select case when grouping(Year(Modifieddate)) = 0
then Year(Modifieddate)
else 'Grand Total' end AS Year,
case when grouping(month(modifieddate)) = 0
then convert(varchar(255), month(modifieddate))
else 'Grand Total' end as Month,
Sum(linetotal) as Sum_price,
sum(sum(linetotal)) over (
partition by
grouping(Year(Modifieddate)),
grouping(month(modifieddate)),
Year(Modifieddate)
order by month(modifieddate)
) as ytd_sum_price
from Sales.SalesOrderDetail
Group by rollup( Year(Modifieddate), month(modifieddate));

mssql: add column with the same value for all rows to search results

I have my query:
SELECT [Shipment Date], [Amount] as [Running Costs], Sum([Amount]) OVER
(ORDER BY [Shipment Date]) as [Total Running Costs]
FROM...
This gets me 3 columns:
Shipment Date | Running Costs | Total Running Costs
I would like to add a fourth column to this query which has the same value for all rows, and the same number of rows as my original query results.
I know you could add for example '999'as Something to the search results, but how can I do the same for a sum of another column (example: Imagine the total sum of the a column in another table is 1500, and I want to have 1500 for all rows in the fourth column. Something like select sum(column_name)?
The database engine is MSSQL.
You can use a nested query
SELECT [Shipment Date], [Amount] as [Running Costs], [Total Running Costs], SUM([Total Running Costs] OVER ())
FROM
(
SELECT [Shipment Date], [Amount] as [Running Costs], Sum([Amount]) OVER
(ORDER BY [Shipment Date]) as [Total Running Costs]
FROM...
)
Nested window function should also work
SUM(SUM([Running costs]) OVER (ORDER BY [Shipment Date])) OVER ()

Getting percentages of counts in SQL Server

I am building an SQL Server query that gets the number of leads that were generated from a certain sources by month. This is the query that tells me the monthly count. But I want to add a column that shows what those leads are for that month as a total of all leads for that month. I'm not clear on how to do this. Any help?
SELECT FORMAT([ProspectData].[dbo].[Real Estate.KPC.Leads.2018-08-08].[Created Date]
, 'yyyy-MM') AS 'YYYY-MM'
, 'Kiosk-Mall' AS 'Lead Source'
, COUNT(*) AS 'Monthly Total From That Lead Source'
FROM [ProspectData].[dbo].[Real Estate.KPC.Leads.2018-08-08]
WHERE [ProspectData].[dbo].[Real Estate.KPC.Leads.2018-08-08].[Lead Source] =
'Kiosk-Mall'
GROUP BY FORMAT([ProspectData].[dbo].[Real Estate.KPC.Leads.2018-08-08].[Created Date], 'yyyy-MM')
ORDER BY FORMAT([ProspectData].[dbo].[Real Estate.KPC.Leads.2018-08-08].[Created Date], 'yyyy-MM');
You can use conditional aggregation -- basically moving the WHERE condition to a CASE expressions in the argument to an aggregation function:
SELECT FORMAT(l.[Created Date], 'yyyy-MM') AS YYYYMM,
'Kiosk-Mall' AS Lead_Source,
SUM(CASE WHEN l.[Lead Source] = 'Kiosk-Mall' THEN 1 ELSE 0 END) AS [Monthly Total From That Lead Source],
AVG(CASE WHEN l.[Lead Source] = 'Kiosk-Mall' THEN 1.0 ELSE 0 END) AS proportion_of_total
FROM [ProspectData].[dbo].[Real Estate.KPC.Leads.2018-08-08] l
GROUP BY FORMAT(l.[Created Date], 'yyyy-MM')
ORDER BY YYYYMM
Notes:
Table aliases make the query easier to write and to read.
It is better to choose column aliases that do not need to be escaped (i.e. no spaces, no punctuation).

SQL Rollup to Sum Totals of a Grouped Table

I have a table that is grouped by 'Group Type'. What I need is for the last row to display the sum of the values in the above rows in the Total row.
Below is the code currently but I am not sure how to sum multiple fields at once.
Actual
Expected Result
Do I need to add another CASE statement to
select
CASE when GROUP_TYPE is null then 'Total' ELSE GROUP_TYPE END as 'Group Type',
Sum_Amount, TotalER, [Discount Report]
from
(select GROUP_TYPE, sum(cast(AMOUNT as float)) as Sum_Amount FROM [dbo].[a]
where cast(ACTIVITY_DATE as date) between #startdate and #enddate
group by GROUP_TYPE with rollup) a
left join
(select [Charge Group] , sum(cast([Earned Revenue] as float)) as
TotalER, sum(cast(Discount as float)) as 'Discount Report'
from [dbo].[er] group by [Charge Group]) er
on
a.GROUP_TYPE = er.[Charge Group]
select sum(cast([er] as float)) from [dbo].[er]
I would do a union all before the aggregation. This makes it easier to specify the multiple aggregation sets:
select coalesce(group_type, 'Total') as group_type, -- the easy way
sum(amount) as sum_amount, sum(er) as totaler, sum(discount) as discount
from ((select group_type, cast(amount as float) as Amount, 0 as ER, 0 as discount
from [dbo].a
where cast(ACTIVITY_DATE as date) between #startdate and #enddate
) union all
(select [Charge Group], 0, cast([Earned Revenue] as float) as er, cast(Discount as float)
from [dbo].er
where cast(ACTIVITY_DATE as date) between #startdate and #enddate
)
) aer
group by grouping sets ( (group_type), () );

How to combine rows into one, MS SQL Server management Studio

I think this is a tricky one, How do you combine all of these rows into one? Is it possible? Maybe using temp tables, the values on the left from combo onwards are null but could contain a value. Any ideas? Let me know if you've got any questions.
I would like to show just one line for this unit, i.e/ RecordNumber Reg_string Parts Materials Labour Combo Smart Total Estimate. Record number does not need to be selected in the query but might help when building the script. This is my script so far.
RecordNumber Reg_string Parts Materials Labour Combo Smart Total Estimate
1 AB12ABC NULL NULL NULL 222.27 0 222.27
2 AB12ABC NULL NULL NULL 76.7 0 76.7
3 AB12ABC NULL NULL NULL 48.1 0 48.1
4 AB12ABC NULL NULL NULL 49.4 0 49.4
5 AB12ABC NULL NULL 12 NULL 1 13
6 AB12ABC NULL NULL NULL 255 NULL 255
7 AB12ABC NULL NULL NULL 255 NULL 255
I would like the output to be like this please:
RecordNumber Reg_string Parts Materials Labour Combo Smart Total Estimate
1 AB12ABC NULL NULL 12 906.47 1 919.47
My script is this if it helps:
SELECT UnitData_Vehicle.Reg_String AS Registration, UnitData_Vehicle.VehID_String AS [Vendor Code], UnitData_Vehicle.Manu_String AS Make,
UnitData_Vehicle.ManuNS_String AS [Make NS], SUM(CONVERT(money, UnitData_Vehicle_CompCond.Materials_String)) AS [Materials Price],
CONVERT(money, UnitData_Vehicle_CompCond.Labour_String) AS [Labour Price], CONVERT(money, UnitData_Vehicle_CompCond.Combo_String)
AS [Combination Price], CONVERT(money, UnitData_Vehicle_CompCond.PartSpend_String) AS [Parts Price], CONVERT(money,
UnitData_Vehicle_CompCond.SmartRep_String) AS [Smart Price], CONVERT(money, UnitData_Vehicle_CompCond.Estimate_String)
AS [Estimate EX VAT]
FROM UnitData_Vehicle INNER JOIN
UnitData_Vehicle_CompCond ON UnitData_Vehicle.InspectionResultId = UnitData_Vehicle_CompCond.InspectionResultId
WHERE (UnitData_Vehicle.InspectionProcedureName LIKE '%- inspection') AND (DATEDIFF(day, UnitData_Vehicle.InspectionDateTime, GETDATE()) = 2)
AND Reg_String = 'AB12ABC'
GROUP BY UnitData_Vehicle.EstimDate_DateTime, UnitData_Vehicle.Reg_String, UnitData_Vehicle.VehID_String, UnitData_Vehicle.Manu_String,
UnitData_Vehicle.ManuNS_String, CONVERT(money, UnitData_Vehicle_CompCond.Labour_String), CONVERT(money,
UnitData_Vehicle_CompCond.Combo_String), CONVERT(money, UnitData_Vehicle_CompCond.PartSpend_String), CONVERT(money,
UnitData_Vehicle_CompCond.SmartRep_String), CONVERT(money, UnitData_Vehicle_CompCond.Estimate_String)
SELECT
UnitData_Vehicle.Reg_String AS Registration,
UnitData_Vehicle.VehID_String AS [Vendor Code],
UnitData_Vehicle.Manu_String AS Make,
UnitData_Vehicle.ManuNS_String AS [Make NS],
SUM(CONVERT(money, UnitData_Vehicle_CompCond.Materials_String)) AS [Materials Price],
SUM(CONVERT(money, UnitData_Vehicle_CompCond.Labour_String)) AS [Labour Price],
SUM(CONVERT(money, UnitData_Vehicle_CompCond.Combo_String)) AS [Combination Price],
SUM(CONVERT(money, UnitData_Vehicle_CompCond.PartSpend_String)) AS [Parts Price],
SUM(CONVERT(money, UnitData_Vehicle_CompCond.SmartRep_String)) AS [Smart Price],
SUM(CONVERT(money, UnitData_Vehicle_CompCond.Estimate_String)) AS [Estimate EX VAT]
FROM UnitData_Vehicle INNER JOIN UnitData_Vehicle_CompCond
ON UnitData_Vehicle.InspectionResultId = UnitData_Vehicle_CompCond.InspectionResultId
WHERE (UnitData_Vehicle.InspectionProcedureName LIKE '%- inspection') AND (DATEDIFF(day, UnitData_Vehicle.InspectionDateTime, GETDATE()) = 2) AND Reg_String = 'AB12ABC'
GROUP BY UnitData_Vehicle.EstimDate_DateTime, UnitData_Vehicle.Reg_String, UnitData_Vehicle.VehID_String, UnitData_Vehicle.Manu_String,UnitData_Vehicle.ManuNS_String
Your query is huge and hard to read, but what you need to essentially do is SUM the columns add add a "GROUP BY Reg_string" to the end of your query, obviously do not include the "RecordNumber" in your output as it can not be grouped.
Your query isn't very easy to read, but this is essentially what you'd have to do if all of your fields were in the same table, just amend it to do what you need regarding table joins etc...
SELECT Reg_String),
SUM(Parts) As Parts,
SUM(Materials) AS Materials,
SUM(Labour) AS Labour,
SUM(Combo) AS Combo,
SUM(Smart) AS Smart,
SUM(TotalEstimate) AS TotalEstimate
FROM MyTable
WHERE Reg_String = 'AB12ABC'
GROUP BY Reg_String
If I am not committing mistake , Total Estimate = Labour + Combo + Smart ?
Below is the query
SELECT Reg_String,
SUM(Parts) As Parts,
SUM(Materials) AS Materials,
SUM(Labour) AS Labour,
SUM(Combo) AS Combo,
SUM(Smart) AS Smart,
SUM(Labour + Combo + Smart) AS TotalEstimate
FROM MyTable
GROUP BY Reg_String
SELECT MAX(Reg_String),
SUM(Parts) As Parts,
SUM(Materials) AS Materials,
SUM(Labour) AS Labour,
SUM(Combo) AS Combo,
SUM(Smart) AS Smart,
SUM(Labour + Combo + Smart) AS TotalEstimate
FROM MyTable
GROUP BY Reg_String
use MAX(Reg_String) to display Reg_String also since max will return same result because query is grouping by Reg_String