SQL display only MAX of my COUNT() column - sql

Have a query that shows this...
salesPersonId Total
------------- -----------
AB4 3
GT10 2
JB9 1
JS1 2
KT8 4
TC3 4
VG7 2
WC2 7
(8 row(s) affected)
My query is...
SELECT so.salesPersonId, COUNT(so.orderId) AS 'Total'
FROM salesOrder AS so
GROUP BY so.salesPersonId
GO
I wanted to do this...
SELECT so.salesPersonId, COUNT(so.orderId) AS 'Total'
FROM salesOrder AS so
WHERE MAX(COUNT(so.orderId))
GROUP BY so.salesPersonId
GO
This gives me an error, any ideas on how to show only the salesPersonId with the highest total? Here being WC2.

You can use a common table expression (or a subquery) to get the breakdown and then select all entries in your CTE where their total is equal to max total (as there may be more than one):
;WITH TotalOrders
AS
(
SELECT so.salesPersonId, COUNT(so.orderId) AS 'Total'
FROM salesOrder AS so
GROUP BY so.salesPersonId
)
SELECT *
FROM TotalOrders [TO]
WHERE [TO].Total = (SELECT MAX([TO].Total) FROM TotalOrders [TO])

WITH totalCount
AS
(
SELECT so.salesPersonId, COUNT(so.orderId) AS 'Total'
FROM salesOrder AS so
GROUP BY so.salesPersonId
),
maxCount AS
(
SELECT salesPersonId, Total,
DENSE_RANK() OVER (ORDER BY Total DESC) rn
FROM totalCount
)
SELECT salesPersonId, Total
FROM maxCount
WHERE rn = 1

Related

Can you combine 3 CTE's into one?

I have a query written with 3 CTE's and I need to get the exact same result using only 1 CTE. Can someone help me?
WITH Q1(cid, sumEP) AS
(
SELECT customerid, SUM(Extendedprice)
From "Invoices"
GROUP BY customerid
), Q2(cid, oid, mf) AS
(
SELECT DISTINCT customerid, orderid, freight
FROM "Invoices"
), Q3 AS
(
SELECT cid, SUM(mf) AS smf
FROM Q2
GROUP BY cid
)
SELECT Q1.cid, sumEP + smf AS total
FROM Q1
JOIN Q3 ON Q1.cid = Q3.cid
LIMIT 10
smf is the sum of distinct values of freight per order (for the same customer), but this you can do with sum(distinct freight)
So I would suggest this query:
WITH Q(customerid, orderid, total) AS
(
SELECT customerid, orderid, SUM(Extendedprice) + SUM(DISTINCT freight)
FROM Invoices
GROUP BY customerid, orderid
)
SELECT customerid, SUM(total) AS total
FROM Q
GROUP BY customerid
ORDER BY 2 DESC
LIMIT 10;
When there are no cases where different orders for the same customer have the same freight value, then it can be simplified to:
SELECT customerid, SUM(Extendedprice) + SUM(DISTINCT freight) AS total
FROM Invoices
GROUP BY customerid
ORDER BY 2 DESC
LIMIT 10
Note I added an order by so that you limit the results with some logic behind it -- in this case getting the top-10 by total. Change as needed.

Use CTE and UNION ALL with SQL Server 2014

My problem is that:
Create a view that shows the top 5 selling products as well as an aggregated row that shows the total sales for all other products and a Grand total row that sums all of the above.
WITH ProductTop5 AS
(
SELECT [dbo].[Product].[ProductName] AS ProductName, SUM([dbo].[SalesOrderDetail].[LineTotal]) AS TotalAmount
FROM [dbo].[Product]
JOIN [dbo].[SalesOrderDetail] ON [dbo].[Product].[ProductID] = [dbo].[SalesOrderDetail].[ProductID]
GROUP BY [dbo].[Product].[ProductName]
)
You could use ROW_NUMBER/RANK to calculate ranking of product:
WITH Product AS
(
SELECT p.[ProductName] AS ProductName,
SUM(sod.[LineTotal]) AS TotalAmount
FROM [dbo].[Product] p
JOIN [dbo].[SalesOrderDetail] sod
ON p.[ProductID] = sod.[ProductID]
GROUP BY p.[ProductName]
), ProductWithRank AS (
SELECT ProductName, Total_Amount,
ROW_NUMBER() OVER(ORDER BY Total_Amount DESC) AS rn
FROM Product
)
SELECT ProductName, TotalAmount
FROM ProductWithRank
WHERE rn <= 5
UNION ALL
SELECT 'All Others', SUM(Total_Amount)
FROM ProductWithRank
WHERE rn > 5
UNION ALL
SELECT 'Grand Total', SUM(TotalAmount)
FROM ProductWithRank;

SQL Query -- how to find lowest 2 numbers

I need to create a query that finds the lowest 2 values for each unique item in a table -- I am trying to find the first 2 shipments of each item.
So if the shipping table has:
ID ---- Date --- PartID
1 ---- 1/1 ---- 1
2 ---- 1/2 ---- 2
3 ---- 1/2 ---- 1
4 ---- 1/3 ---- 1
I would want rows 1, 2, and 3 returned as they are the first and second shipment of each item.
I can create a query that gets the lowest 2 values:
Select Min(ShipmentID) as SID
from dbo.Shipment
UNION
Select Min(ShipmentID) as SID
from dbo.Shipment
where (ShipmentID >
(Select Min(ShipmentID)
from dbo.Shipment))
but when I add in other information I only get the lowest for each item, not both:
Select Min(ShipmentID) as SID, AddressIDBilling
from dbo.Shipment
Group by AddressIDBilling
UNION
Select Min(ShipmentID) as SID, AddressIDBilling
from dbo.Shipment
where (ShipmentID >
(Select Min(ShipmentID)
from dbo.Shipment))
Group By AddressIDBilling
Order By AddressIDBilling
-- returns only 1 row for each AddressID, not the 2 records that I would want.
If SQL server, use a CTE and a row_number()
with CTE as
(
select PartID, Date, row_number() over(partition by PartID order by Date) as PartOrd
from MyTable
)
select PartID, Date, PartOrd
from CTE
where PartOrd <=2
The normal way of doing this uses window functions, in this case rank() or row_number() (depending on how you want to handle ties):
select s.*
from (select s.*,
row_number() over (partition by partid order by date asc) as seqnum
from dbo.shipment s
) s
where seqnum <= 2;

SQL Server Group By Complex Query

In SQL Server, suppose we have a SALES_HISTORY table as below.
CustomerNo PurchaseDate ProductId
1 20120411 12
1 20120330 13
2 20120312 14
3 20120222 16
3 20120109 16
... and many records for each purchase of each customer...
How can I write the appropriate query for finding:
For each customer,
find the product he bought at MOST,
find the percentage of this product over all products he bought.
The result table must have columns like:
CustomerNo,
MostPurchasedProductId,
MostPurchasedProductPercentage
Assuming SQL Server 2005+, you can do the following:
;WITH CTE AS
(
SELECT *,
COUNT(*) OVER(PARTITION BY CustomerNo, ProductId) TotalProduct,
COUNT(*) OVER(PARTITION BY CustomerNo) Total
FROM YourTable
), CTE2 AS
(
SELECT *,
RN = ROW_NUMBER() OVER(PARTITION BY CustomerNo
ORDER BY TotalProduct DESC)
FROM CTE
)
SELECT CustomerNo,
ProductId MostPurchasedProductId,
CAST(TotalProduct AS NUMERIC(16,2))/Total*100 MostPurchasedProductPercent
FROM CTE2
WHERE RN = 1
You still need to deal when you have more than one product as the most purchased one. Here is a sqlfiddle with a demo for you to try.
Could do a lot prettier, but it works:
with cte as(
select CustomerNo, ProductId, count(1) as c
from SALES_HISTORY
group by CustomerNo, ProductId)
select CustomerNo, ProductId as MostPurchasedProductId, (t.c * 1.0)/(select sum(c) from cte t2 where t.CustomerNo = t2.CustomerNo) as MostPurchasedProductPercentage
from cte t
where c = (select max(c) from cte t2 where t.CustomerNo = t2.CustomerNo)
SQL Fiddle

Grab top 5 rows and combine the rest SQL Server

I have a column for group name and a column for amount spent.
I need to sum the amounts group them based on the group name and then grab the highest five. After that, I need to combine the the rest into it's own group w/ a total of their amount spent. This is what i have right now
SELECT groupName, SUM(amount) AS theAmountSpent
FROM purchases
GROUP BY groupName
ORDER BY theAmountSpent DESC
This groups and orders them, but i dont know how to then grab the remaining groups to combine them. Any help would be appreciated.
Alternate CTE-approach using row_number() (SQL Server 2005+):
WITH cte AS (
SELECT ROW_NUMBER() OVER (ORDER BY (SUM(amount)) DESC) AS num,
groupName, SUM(amount) AS theAmountSpent
FROM purchases
GROUP BY groupName
)
SELECT groupName, theAmountSpent FROM cte WHERE num BETWEEN 1 AND 5 --top 5
UNION ALL
SELECT 'Sum rest', SUM(theAmountSpent) FROM cte WHERE num > 5 -- sum of rest
If I'm understanding you correctly, this should do it:
SELECT top 5 groupName, SUM(amount) AS theAmountSpent
into #tempSpent FROM purchases
GROUP BY groupName
ORDER BY theAmountSpent DESC
Select * from #tempSpent -- get the top 5
--get sum for the rest
SELECT SUM(amount) AS theAmountSpent
FROM purchases
where groupName not in (select groupName from #tempSpent)
Drop table #tempSpent
Another idea from Larsts code:
WITH cte
AS
(
SELECT case
when ROW_NUMBER() OVER (ORDER BY (SUM(amount)) DESC) <=5
then ROW_NUMBER() OVER (ORDER BY (SUM(amount)) DESC)
else 6 end AS num
, groupName
, SUM(amount) AS theAmountSpent
FROM purchases
GROUP BY groupName
)
SELECT num
, max(groupName)
, sum(theAmountSpent )
FROM cte
group by num