Use CTE and UNION ALL with SQL Server 2014 - sql

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;

Related

How to get top 10 items based on category in joined table

I can get the top 1000 selling items from a sales table in a day (and display item info from another table) with the following query:
select
cs.item_no, sum(quantity) as quantity, i.item_type, i.item_name, i.group_name
FROM `sales` cs
left join
(
SELECT
item_no,
MIN(item_name) as item_name,
MIN(item_type) as item_type,
MIN(group_name) as group_name,
FROM
`items`
WHERE group_name!='UNKNOWN'
GROUP BY
item_no
) i
on cs.item_no = i.item_no
where date BETWEEN '2022-08-21' AND '2022-08-22'
group by item_no, i.item_type, i.item_name, i.group_name
order by sum(quantity) desc
limit 1000
The query displays top 1000 items by quantity sold and also shows the item's type, name and group_name. How can I modify this query so that I can see the top 10 items for each group_name? So instead of top 1000 items overall, I want to see the top 10 items in each group_name.
Thank you
Does below SQL works ?
Basically i created a subquery to calculate rank over partition on item group and then ordered by quantity.
A filter on rank column will display top 10 records for each group.
select item_no,item_type, item_name, group_name
FROM (
select subq.*, rank() over (partition by group_name order by group_name,quantity desc) as rn
FROM ( select
cs.item_no, sum(quantity) as quantity, i.item_type, i.item_name, i.group_name
FROM `sales` cs
left join
(
SELECT
item_no,
MIN(item_name) as item_name,
MIN(item_type) as item_type,
MIN(group_name) as group_name,
FROM
`items`
WHERE group_name!='UNKNOWN'
GROUP BY
item_no
) i
on cs.item_no = i.item_no
where date BETWEEN '2022-08-21' AND '2022-08-22'
group by item_no, i.item_type, i.item_name, i.group_name) subq) outerqry
WHERE rn<=10

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.

SQL: How to select the highest priced used item for each day

I need to produce a query that would give me the highest priced used product for each day where the total price of products sold that day exceeds 200.
SELECT *, max(price)
FROM products
WHERE products.`condition` = 'used' and products.price > 200
GROUP BY date_sold
Here is my products table http://prntscr.com/of3hjd
You could try using a join with sum for price > 200 group by date_sol
select m.date_sold, max(m.price)
from my_table m
inner join (
select date_sold, sum(price)
from my_table
group by date_sold
having sum(price)>200
) t on t.date_sold = m.date_sold
group by m.date_sold
You can use window functions for this:
select p.*
from (select p.*,
sum(price) over (partition by date_sold) as sum_price,
row_number() over (partition by date_sold, condition order by price desc) as seqnum
from products p
) p
where sum_price > 200 and
condition = 'used' and
seqnum = 1;
SELECT *, max(price) FROM products
where products.`condition` = 'used' and sum(products.price) > 200
GROUP BY day(date_sold)

SQL Select Group By Min() - but select other

I want to select the ID of the Table Products with the lowest Price Grouped By Product.
ID Product Price
1 123 10
2 123 11
3 234 20
4 234 21
Which by logic would look like this:
SELECT
ID,
Min(Price)
FROM
Products
GROUP BY
Product
But I don't want to select the Price itself, just the ID.
Resulting in
1
3
EDIT: The DBMSes used are Firebird and Filemaker
You didn't specify your DBMS, so this is ANSI standard SQL:
select id
from (
select id,
row_number() over (partition by product order by price) as rn
from orders
) t
where rn = 1
order by id;
If your DBMS doesn't support window functions, you can do that with joining against a derived table:
select o.id
from orders o
join (
select product,
min(price) as min_price
from orders
group by product
) t on t.product = o.product and t.min_price = o.price;
Note that this will return a slightly different result then the first solution: if the minimum price for a product occurs more then once, all those IDs will be returned. The first solution will only return one of them. If you don't want that, you need to group again in the outer query:
select min(o.id)
from orders o
join (
select product,
min(price) as min_price
from orders
group by product
) t on t.product = o.product and t.min_price = o.price
group by o.product;
SELECT ID
FROM Products as A
where price = ( select Min(Price)
from Products as B
where B.Product = A.Product )
GROUP BY id
This will show the ID, which in this case is 3.

Get most expensive and cheapest items from two tables

I'm trying to get the most expensive and cheapest items from two different tables.
The output should be one row with the values for MostExpensiveItem, MostExpensivePrice, CheapestItem, CheapestPrice
I was able to get the price of the most expensive and cheapest items in the two tables with following query:
SELECT
MAX(ExtrasPrice) as MostExpensivePrice, MIN(ExtrasPrice) as CheapestPrice
FROM
(
SELECT ExtrasPrice FROM Extras
UNION ALL
SELECT ItemPrice FROM Items
) foo
How can I add the names of the items (ItemName, ExtrasName) to my output? Again, there should only be one row as the output.
Try this:
SELECT TOP 1 FIRST_VALUE(Price) OVER (ORDER BY Price) AS MinPrice,
FIRST_VALUE(Name) OVER (ORDER BY Price) AS MinName,
LAST_VALUE(Price) OVER (ORDER BY Price DESC) AS MaxPrice,
LAST_VALUE(Name) OVER (ORDER BY Price DESC) AS MaxName
FROM (
SELECT ExtrasName AS Name, ExtrasPrice AS Price FROM Extras
UNION ALL
SELECT ItemName As Name, ItemPrice AS Price FROM Items) u
SQL Fiddle Demo
TOP 1 with order by clause should work for you. Try this
SELECT *
FROM (SELECT TOP 1 ExtrasPrice,ExtrasName
FROM Extras ORDER BY ExtrasPrice Asc),
(SELECT TOP 1 ItemPrice,ItemName
FROM Items ORDER BY ItemPrice Desc)
Note: Comma can be replaced with CROSS JOIN
You can use row_number() for this. If you are satisfied with two rows:
SELECT item, price
FROM (SELECT foo.*, row_number() over (order by price) as seqnum_asc,
row_number() over (order by price) as seqnum_desc
FROM (SELECT item, ExtrasPrice as price FROM Extras
UNION ALL
SELECT item, ItemPrice FROM Items
) foo
) t
WHERE seqnum_asc = 1 or seqnum_desc = 1;
EDIT:
If you have an index on "price" in both tables, then the cheapest method is probably:
with exp as (
(select top 1 item, ExtrasPrice as price
from Extras e
order by price desc
) union all
(select top 1 i.item, ItemPrice
from Items i
order by price desc
)
),
cheap as (
(select top 1 item, ExtrasPrice as price
from Extras e
order by price asc
) union all
(select top 1 i.item, ItemPrice
from Items i
order by price asc
)
)
select top 1 *
from exp
order by price desc
union all
select top 1 *
from cheap
order by price asc;
If you want this in one row, you can replace the final query with:
select e.*, c.*
from (select top 1 *
from exp
order by price desc
) e cross join
(select top 1 *
from cheap
order by price asc
) c