How to UNION 2 CTE results in SQL - sql-server

--UNION gives me error
;WITH TopSelling
AS
(
SELECT p.Name AS ProductName,SUM(LineTotal) AS TotalAmount
FROM Product AS p INNER JOIN SalesOrderDetail AS sod
ON p.ProductID = sod.ProductID
GROUP BY p.Name
)
SELECT ProductName, TotalAmount
FROM TopSelling
ORDER BY TotalAmount DESC
OFFSET 0 ROWS
FETCH NEXT 5 ROWS ONLY
UNION ALL
SELECT 'Grand Total' AS ProductName, sum(TotalAmount) AS TotalAmount
FROM TopSelling

;WITH TopSelling
AS
(
SELECT p.Name AS ProductName,SUM(LineTotal) AS TotalAmount
FROM Product AS p INNER JOIN SalesOrderDetail AS sod
ON p.ProductID = sod.ProductID
GROUP BY p.Name
),
topSelling1 as
(
SELECT ProductName, TotalAmount
FROM TopSelling
ORDER BY TotalAmount DESC
OFFSET 0 ROWS
FETCH NEXT 5 ROWS ONLY
),
total as
(
SELECT sum(TotalAmount) AS TotalAmount
FROM TopSelling;
)
select ProductName, TotalAmount
FROM TopSelling1
UNION ALL
SELECT 'Grand Total' AS ProductName, TotalAmount
FROM Total;

So don't use it. Use GROUPING SETS instead:
SELECT COALESCE(p.Name, 'Grand Total') AS ProductName, -- The lazy way
SUM(LineTotal) AS TotalAmount
FROM Product p INNER JOIN
SalesOrderDetail sod
ON p.ProductID = sod.ProductID
GROUP BY GROUPING SETS ( (p.Name), () )
ORDER BY TotalAmount DESC;
EDIT:
Oh, I see, you want the first five rows and then the overall total.
Use a subquery:
WITH TopSelling AS
(SELECT p.Name as ProductName, SUM(LineTotal) as TotalAmount
FROM Product p INNER JOIN
SalesOrderDetail sod
ON p.ProductID = sod.ProductID
GROUP BY p.Name
)
SELECT ProductName, TotalAmount
FROM ((SELECT TOP 5 ProductName, TotalAmount, 1 as Priority
FROM TopSelling
ORDER BY TotalAmount DESC
) UNION ALL
(SELECT 'Grand Total' AS ProductName, sum(TotalAmount) AS TotalAmount, 2 as Priority
FROM TopSelling
)
) t
ORDER BY Priority, TotalAmount DESC;
I find TOP 5 more natural in this case than OFFSET/FETCH. The latter, of course, is more sensible for paging.

remove the alias form the second select
;WITH TopSelling
AS
(
SELECT p.Name AS ProductName,SUM(LineTotal) AS TotalAmount
FROM Product AS p INNER JOIN SalesOrderDetail AS sod
ON p.ProductID = sod.ProductID
GROUP BY p.Name
)
SELECT ProductName, TotalAmount
FROM TopSelling
ORDER BY TotalAmount DESC
OFFSET 0 ROWS
FETCH NEXT 5 ROWS ONLY
UNION ALL
SELECT 'Grand Total' , sum(TotalAmount)
FROM TopSelling

Related

select last row after various aggregation functions

I have this query:
select b.Project_Id,b.Id,
FIRST_VALUE(i.Number) OVER(PARTITION BY b.id ORDER BY i.Date) as FirstNumDDT
,sum( ir.Qty) OVER(PARTITION BY b.id ORDER BY i.date) as sumqty
from InvoiceRow ir
inner join Invoice i on i.id=Invoice_Id
inner join BillOfMaterial b on b.id=ir.BOM_Id
and a few part of the result is:
Project_Id
Id
FirstNumDDT
sumqty
16088
1986620
21803
1
16088
1986620
21803
4
I need only the last row. How can I filter?
Thanks
Use TOP(N) in combination with an ORDER BY clause.
SELECT TOP(1)
b.Project_Id,b.Id,
FIRST_VALUE(i.Number) OVER(PARTITION BY b.id ORDER BY i.Date) as FirstNumDDT,
SUM(ir.Qty) OVER(PARTITION BY b.id ORDER BY i.date) as sumqty
from InvoiceRow ir
INNER JOIN Invoice i on i.id=Invoice_Id
INNER JOIN BillOfMaterial b on b.id=ir.BOM_Id
ORDER BY sumqty DESC
If you want allow to retrieve all values that have the maximum sumqty values, you need to use TOP(1) WITH TIES instead.
SELECT TOP(1) WITH TIES
b.Project_Id,b.Id,
FIRST_VALUE(i.Number) OVER(PARTITION BY b.id ORDER BY i.Date) as FirstNumDDT,
SUM(ir.Qty) OVER(PARTITION BY b.id ORDER BY i.date) as sumqty
from InvoiceRow ir
INNER JOIN Invoice i on i.id=Invoice_Id
INNER JOIN BillOfMaterial b on b.id=ir.BOM_Id
ORDER BY sumqty DESC

Multiple aggregate functions in SQL Server

I need a query for [Contribution]. I used this query:
with ttt as
(
select
(DYG.U_StyleId)[DYG Style]
,Max(O1.CardCode) [Party Group Code],
MAX(O1.CardName) [Party Group Name]
,MAX(OR1.DocDate) [Date]
,sum(CONVERT(NUMERIC(15,2),(RDR1.PriceBefDi*RDR1.Quantity))) [JobAmount]
,CONVERT(NUMERIC(15,2),SUM(RDR1.Quantity)) [Mtr]
,CONVERT(NUMERIC(15,2),SUM(RDR1.U_Pcs))[Pcs]
,(select sum(RDR1.PriceBefDi*RDR1.Quantity) from RDR1) tqty
from
ORDR OR1
left join RDR1 on RDR1.DocEntry = OR1.DocEntry
left join OITM on RDR1.ItemCode = oitm.ItemCode
LEFT JOIN OCRD ON OCRD.CardCode = OR1.CardCode
LEFT JOIN OCRG ON OCRG.GroupCode = OCRD.GroupCode
LEFT JOIN OCRD O1 ON O1.U_BCode = OCRD.U_GrpCod
LEFT JOIN
( SELECT U_StyleId FROM RDR1 WHERE U_StyleId in
('BLOOM','BLOOMING','DYD','DYD-R','DYED','Ex.CLR.','RAINBOW'))
DYG ON DYG.U_StyleId = RDR1.U_StyleId
group by
DYG.U_StyleId
)
select
Style, [Party Group Code],
[Party Group Name], JobAmount,
(sum(JobAmount) / tqty * 100) [Contribution],
[Date], [Pcs]
from
ttt
group by
Style
I need Sum of last jobamount to divide it with above tqty.
But it shows this error.
'Column 'ttt.Party Group Code' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.'
Please help me with the query to get right [Contribution] amount.
Try this:
select Style,[Party Group Code],[Party Group Name],JobAmount,[Date],[Pcs],
100.0 * (sum(JobAmount) OVER (PARTITION BY Style))/tqty [Contribution]
from ttt;

How do I get a max record for each week in SQL Server 2012?

I'm looking to get a query that returns the info for the largest order, week by week.
My very basic concept is something like this:
select
datepart(ww, order_date), order_id, order_revenue, order_margin
from
orders
where
order_date >= 1/1/2016
and order_id in (select max order for each week)
Basically I just want the week #, order id, order revenue, and order margin for the largest order margin record for each week.
You need to use a "Windowing Function"
https://www.simple-talk.com/sql/learn-sql-server/window-functions-in-sql-server/
select *
from
( select datepart(ww, order_date), order_date, order_id, order_revenue, order_margin
, row_number() over (partition by datepart(ww, order_date) order by order_margin desc) as rn
from orders
where order_date >= 1/1/2016
) tt
where tt.rn = 1
order by order_date desc
And I think you need to single quote the date
This is a slightly different method; it has a lot more details, but that's because I designed it to run in AdventureWorks (don't know how to tell which version) so I could test the functionality.
USE AdventureWorks
;WITH OrderDetails
AS (
SELECT soh.SalesOrderID, CAST(soh.OrderDate AS DATE) AS OrderDate,
SUM(sod.OrderQty * (sod.UnitPrice - pch.StandardCost)) AS OrderMargin
FROM Sales.SalesOrderHeader soh
LEFT JOIN Sales.SalesOrderDetail sod
ON soh.SalesOrderID = sod.SalesOrderID
LEFT JOIN Production.ProductCostHistory pch
ON sod.ProductID = pch.ProductID
WHERE soh.OrderDate >= pch.StartDate AND soh.OrderDate <= pch.EndDate
GROUP BY soh.SalesOrderID, soh.OrderDate
),
MaxOrder
AS (
SELECT DATEPART(yy,OrderDate) AS Year, DATEPART(ww,OrderDate) AS Week,
MAX(OrderMargin) AS MaxOrderMargin
FROM OrderDetails
GROUP BY DATEPART(yy,OrderDate), DATEPART(ww,OrderDate)
)
SELECT mo.Year, mo.Week, od.SalesOrderID, '$'+FORMAT(mo.MaxOrderMargin,'#,0.00') AS OrderMargin
FROM MaxOrder mo
INNER JOIN OrderDetails od
ON mo.MaxOrderMargin = od.OrderMargin
WHERE DATEPART(yy,od.OrderDate) = mo.Year AND DATEPART(ww,od.OrderDate) = mo.Week
ORDER BY Year, Week
This has slightly improved functionality over the previous example in one regard only, and that is because it will include ALL the orders with that margin, even if there are multiple. I would note, however, that this can be achieved using RANK() instead of ROW_NUMBER(). Show here using a CTE:
USE AdventureWorks
;WITH OrderDetails
AS (
SELECT soh.SalesOrderID, CAST(soh.OrderDate AS DATE) AS OrderDate,
SUM(sod.OrderQty * (sod.UnitPrice - pch.StandardCost)) AS OrderMargin
FROM Sales.SalesOrderHeader soh
LEFT JOIN Sales.SalesOrderDetail sod
ON soh.SalesOrderID = sod.SalesOrderID
LEFT JOIN Production.ProductCostHistory pch
ON sod.ProductID = pch.ProductID
WHERE soh.OrderDate >= pch.StartDate AND soh.OrderDate <= pch.EndDate
GROUP BY soh.SalesOrderID, soh.OrderDate
),
OrderRank
AS (
SELECT DATEPART(yy,OrderDate) AS Year, DATEPART(ww,OrderDate) AS Week,
SalesOrderID, '$'+FORMAT(OrderMargin,'#,0.00') AS OrderMargin,
RANK() OVER (PARTITION BY DATEPART(yy,OrderDate), DATEPART(ww,OrderDate) ORDER BY OrderMargin DESC) AS MRank
FROM OrderDetails
),
MRank
AS (
SELECT Year, Week, SalesOrderID, OrderMargin
FROM OrderRank
WHERE MRank = 1
)
SELECT * FROM MRank

select count over partition by

I am learning window functions in sql server. I am using AdventrueWorks2012 database for practice. I want to calculate total number of sales and purchases for each item in the store.
The classic solution can be like
SELECT ProductID,
Quantity,
(SELECT Count(*)
FROM AdventureWorks.Purchasing.PurchaseOrderDetail
WHERE PurchaseOrderDetail.ProductID = p.ProductID) TotalPurchases,
(SELECT Count(*)
FROM AdventureWorks.Sales.SalesOrderDetail
WHERE SalesOrderDetail.ProductID = p.ProductID) TotalSales
FROM (SELECT DISTINCT ProductID,
Quantity
FROM AdventureWorks.Production.ProductInventory) p
Trying to convert to window functions gives me wrong results:
SELECT DISTINCT d.ProductID,
Quantity,
Count(d.ProductID)
OVER(
PARTITION BY d.ProductID) TotalPurchases,
Count(d2.ProductID)
OVER(
PARTITION BY d2.ProductID) TotalSales
FROM (SELECT DISTINCT ProductID,
Quantity
FROM AdventureWorks.Production.ProductInventory) p
INNER JOIN AdventureWorks.Purchasing.PurchaseOrderDetail d
ON p.ProductID = d.ProductID
INNER JOIN AdventureWorks.Sales.SalesOrderDetail d2
ON p.ProductID = d2.ProductID
ORDER BY d.ProductID
Why this is wrong? How can I correct it?
You should change INNER JOIN to LEFT JOIN
Because when you inner join, result will miss productid which from ProductInventory table does not have PurchaseOrderDetail or SalesOrderDetail.

SQL Server: How to obtain the SUM of the top 5 orders by country

This is my first time around here :p
I have SQL Server practice.
Based on the country where Northwind customers reside, show the sum of the 5 highest purchase orders by country. The results should be presented in two columns: country, amount
I tried:
SELECT
vt.ShipCountry, vt.suma
FROM
(SELECT
o.ShipCountry,
SUM( UnitPrice * Quantity * (1-discount)) as suma,
RANK() OVER (PARTITION BY SUM(UnitPrice * Quantity * (1-discount)) ORDER BY shipCountry DESC) AS Rank
FROM
orders o
JOIN
[Order Details] od ON o.OrderID = od.OrderID
GROUP BY
o.ShipCountry) as vt
WHERE
Rank <= 5
GROUP BY
vt.ShipCOUNTRY, vt.suma
but, it retrieves me the sum of all orders per country, only want the top 5 per country
Here's another one, same issue.
SELECT
ShipCountry, rk, amount
FROM
(SELECT
o.ShipCountry,
SUM(UnitPrice * Quantity * (1-discount)) amount,
DENSE_RANK() OVER(PARTITION BY o.ShipCountry ORDER BY SUM(UnitPrice * Quantity * (1-discount)) DESC) AS rk
FROM
Orders o
JOIN
[Order Details] od ON o.OrderID = od.OrderID
GROUP BY
o.shipCountry) AS L
WHERE
rk <= 5;
The two queries have the same behaviour
Try this:
-- first, sum up the total amount of each order
;WITH OrderDetails AS
(
SELECT
o.OrderID,
TotalOrderAmount = SUM(UnitPrice * Quantity * (1 - discount))
FROM
orders o
INNER JOIN
[Order Details] od ON o.OrderID = od.OrderID
GROUP BY
o.OrderID
),
-- secondly, join the "ShipCountry" to the order totals,
-- and define a ROW_NUMBER() for each country, based on
-- total order amount
OrderPerCountry AS
(
SELECT
o.ShipCountry,
od.TotalOrderAmount,
RowNum = ROW_NUMBER() OVER(PARTITION BY o.ShipCountry ORDER BY od.TotalOrderAmount DESC)
FROM
OrderDetails od
INNER JOIN
dbo.Orders o ON o.OrderID = od.OrderID
)
SELECT *
FROM OrderPerCountry
WHERE RowNum <= 5
This should do the trick for you - I hope!

Resources