Save SQL Query Results in Variable - sql

I have the following SQL Query, but it is very repetitive and so I am wondering if this can be cleaned up by storing the results of a SQL Query in a variable.
What I have is (which works, can be tested here)
SELECT ProductName FROM (
SELECT ProductName, SUM(Quantity) AS ProductQuantity FROM
(SELECT CustomerID FROM Customers AS C
WHERE Country = 'Germany') AS CG
INNER JOIN Orders AS O
ON CG.CustomerID = O.CustomerID
INNER JOIN OrderDetails AS OD
ON O.OrderID = OD.OrderID
INNER JOIN Products as P
ON OD.ProductID = P.ProductID
GROUP BY ProductName)
WHERE ProductQuantity = (
SELECT MAX(ProductQuantity) FROM (
SELECT ProductName, SUM(Quantity) AS ProductQuantity FROM
(SELECT CustomerID FROM Customers AS C
WHERE Country = 'Germany') AS CG
INNER JOIN Orders AS O
ON CG.CustomerID = O.CustomerID
INNER JOIN OrderDetails AS OD
ON O.OrderID = OD.OrderID
INNER JOIN Products as P
ON OD.ProductID = P.ProductID
GROUP BY ProductName))
And as you could tell the following code is repeated twice, is there any way I can do this without repeating this code:
(SELECT ProductName, SUM(Quantity) AS ProductQuantity FROM
(SELECT CustomerID FROM Customers AS C
WHERE Country = 'Germany') AS CG
INNER JOIN Orders AS O
ON CG.CustomerID = O.CustomerID
INNER JOIN OrderDetails AS OD
ON O.OrderID = OD.OrderID
INNER JOIN Products as P
ON OD.ProductID = P.ProductID
GROUP BY ProductName)

You can use CTE to avoid looping, maybe like this:
with cte as (
SELECT ProductName, SUM(Quantity) AS ProductQuantity FROM
(SELECT CustomerID FROM Customers AS C
WHERE Country = 'Germany') AS CG
INNER JOIN Orders AS O
ON CG.CustomerID = O.CustomerID
INNER JOIN OrderDetails AS OD
ON O.OrderID = OD.OrderID
INNER JOIN Products as P
ON OD.ProductID = P.ProductID
GROUP BY ProductName
)
SELECT ProductName
FROM cte
where ProductQuantity = (SELECT MAX(ProductQuantity) FROM cte)

Related

Find Top 5 Customers for Beverages based on their total purchase value SQL

Here is the link to the Data Set.
https://www.w3schools.com/sql/trysql.asp?filename=trysql_asc
I have been trying to solve this but couldn't find a way to get the total purchase value while grouping with the customer table
I would recommend using a Common Table Expression (CTE) as, in my experience, it helps with scalability/maintenance down the road and easily enables you to see what the data is under the hood if you wanted to simply run the CTE itself.
I join the Customer to the Order to get the OrderID
I join the Order to OrderDetails to get the ProductID and Order Quantity
I join the OrderDetails to Products to get the Price
I join the Categories to filter for just Beverages
All this is wrapped as a CTE (similar to a subquery), on top of which I can now aggregate at the Customer level and sequence by Order Value in a descending fashion.
with beverage_orders_cte as(
SELECT c.CustomerName, o.OrderID
, od.OrderDetailID, od.ProductID, od.Quantity
, p.ProductName, p.Price
, od.Quantity * p.Price as OrderVal
,cat.CategoryName FROM Customers c
inner join Orders o
on c.CustomerID = o.CustomerID
inner join OrderDetails od
on o.OrderID = od.OrderID
inner join Products p
on od.ProductID = p.ProductID
inner join Categories cat
on p.CategoryID = cat.CategoryID and cat.CategoryID = 1
)
select CustomerName, SUM(OrderVal) as Revenue
From beverage_orders_cte
Group by CustomerName
Order by Revenue desc
Limit 5
Hope this helps, good luck.
Something like that?
SELECT c.customerid,
Sum(p.price)
FROM customers AS c
INNER JOIN orders AS o
ON o.customerid = c.customerid
INNER JOIN orderdetails AS od
ON od.orderid = o.orderid
INNER JOIN products AS p
ON p.productid = od.productid
GROUP BY c.customerid
ORDER BY Sum(p.price) DESC
LIMIT 5
Just following on from your quantity comment...
SELECT c.customerid,
Sum(p.price),
Sum(p.price * od.quantity)
FROM customers AS c
INNER JOIN orders AS o
ON o.customerid = c.customerid
INNER JOIN orderdetails AS od
ON od.orderid = o.orderid
INNER JOIN products AS p
ON p.productid = od.productid
GROUP BY c.customerid
ORDER BY Sum(p.price) DESC
LIMIT 5
I think this is the best optimized code.
Please try with this.
SELECT CustomerID, Count(Quantity * Price) AS Total
FROM Orders, OrderDetails, Products
Where Orders.OrderID = OrderDetails.OrderID AND Products.ProductID = OrderDetails.ProductID
Group by CustomerID
ORDER BY Total DESC
LIMIT 5

Gross sales of top ten customers that made at least 5 purchases of some Category in a specified year

I need to get (sales.customers) | year | gross_sales of top ten customers that made at least 5 purchases of Category "Beverages" in the year 2014. I have already written these SELECT queries, but since I am new to SQL, I think I am very inefficient in writing code. This does not work properly and there is probably a simpler way of doing it. I have also pinned a picture of an ER diagram .
SELECT
T6.COMPANYNAME, YEAR, GROSS_SALES
FROM
(SELECT T1.CUSTID
FROM
(SELECT R.CUSTID, COUNT(R.CUSTID) AS NUMBEROFSALES
FROM SALES.ORDERDETAILS O
RIGHT JOIN PRODUCTION.PRODUCTS P ON P.PRODUCTID = O.PRODUCTID
RIGHT JOIN SALES.ORDERS R ON R.ORDERID = O.ORDERID
INNER JOIN SALES.CUSTOMERS C1 ON R.CUSTID = C1.CUSTID
INNER JOIN PRODUCTION.CATEGORIES C2 ON P.CATEGORYID = C2.CATEGORYID
WHERE C2.CATEGORYNAME = 'Beverages' AND YEAR(R.ORDERDATE) = 2014
GROUP BY R.CUSTID
ORDER BY SUM(R.CUSTID) DESC) T1
--HAVING COUNT(R.CUSTID) > 5
RIGHT JOIN
(SELECT R.CUSTID, SUM(O.UNITPRICE) AS MONEYSPENT
FROM SALES.ORDERDETAILS O
RIGHT JOIN PRODUCTION.PRODUCTS P ON P.PRODUCTID = O.PRODUCTID
RIGHT JOIN SALES.ORDERS R ON R.ORDERID = O.ORDERID
INNER JOIN SALES.CUSTOMERS C1 ON R.CUSTID = C1.CUSTID
INNER JOIN PRODUCTION.CATEGORIES C2 ON P.CATEGORYID = C2.CATEGORYID
WHERE C2.CATEGORYNAME = 'Beverages' AND YEAR(R.ORDERDATE) = 2014
GROUP BY R.CUSTID
ORDER BY SUM(O.UNITPRICE) DESC) T2 ON T1.CUSTID = T2.CUSTID
ORDER BY T1.NUMBEROFSALES DESC
LIMIT 10) T5
INNER JOIN
(SELECT DISTINCT(T4.COMPANYNAME), T4.CUSTID, YEAR, GROSS_SALES
FROM
(SELECT R.CUSTID AS CUSTID, YEAR(R.ORDERDATE) AS YEAR, SUM(O.UNITPRICE * O.QTY * (1 - O.DISCOUNT)) AS GROSS_SALES
FROM SALES.ORDERDETAILS O
RIGHT JOIN SALES.ORDERS R ON R.ORDERID = O.ORDERID
INNER JOIN SALES.CUSTOMERS C1 ON R.CUSTID = C1.CUSTID
GROUP BY R.CUSTID, YEAR(R.ORDERDATE)
ORDER BY YEAR(R.ORDERDATE)) T3
INNER JOIN
(SELECT C.COMPANYNAME, C.CUSTID
FROM SALES.ORDERS R
INNER JOIN SALES.CUSTOMERS C ON R.CUSTID = C.CUSTID) T4 ON T3.CUSTID = T4.CUSTID) T6 ON T5.CUSTID = T6.CUSTID
I'm not going to try and fix your code or explain the misakes there, as there are many of them. Instead based on your requirements I wrote a query that solves the problem. I show it in steps below which should make clear the process I used to solve the problem.
First how do we find the top ten customers that made 5 purchases of Beverages?
Take customer table and join to orders with beverages (inner join will exclude customers that don't meet criteria)
SELECT CUSTOMERID
FROM CUSTOMERS C
JOIN ORDERS O ON C.CUSTOMERID = O.CUSTOMERID
JOIN ORDER_DETAILS OD ON O.ORDERID = OD.ORDERID
JOIN PRODUCTS P ON OD.PRODUCTID = P.PRODUCTID
JOIN CATEGORIES C ON P.CATEGORYID = C.CATEGORYID AND C. CATEGORY_NAME = 'Beverages'
WHERE YEAR(ORDER_DATE) = 2014
GROUP BY CUSTOMERID
HAVING COUNT(ORDER_DETAILS) >= 5
Now we need the sum of order (for 2014) by customers which looks like this:
SELECT CUSTOMERID, YEAR(ORDER_DATE) AS YEAR, SUM(OD.UNIT_PRICE*OD.QUANTITY) AS TOTAL_SPEND
FROM CUSTOMERS C
JOIN ORDERS O ON C.CUSTOMERID = O.CUSTOMERID
JOIN ORDER_DETAILS OD ON O.ORDERID = OD.ORDERID
WHERE YEAR(ORDER_DATE) = 2014
GROUP BY CUSTOMERID, YEAR(ORDER_DATE)
Now we just combine these two queries like this:
SELECT CUSTOMERID, YEAR(ORDER_DATE) AS YEAR, SUM(OD.UNIT_PRICE*OD.QUANTITY) AS TOTAL_SPEND
FROM CUSTOMERS C
JOIN ORDERS O ON C.CUSTOMERID = O.CUSTOMERID
JOIN ORDER_DETAILS OD ON O.ORDERID = OD.ORDERID
JOIN (
SELECT CUSTOMERID
FROM CUSTOMERS C
JOIN ORDERS O ON C.CUSTOMERID = O.CUSTOMERID
JOIN ORDER_DETAILS OD ON O.ORDERID = OD.ORDERID
JOIN PRODUCTS P ON OD.PRODUCTID = P.PRODUCTID
JOIN CATEGORIES C ON P.CATEGORYID = C.CATEGORYID AND C. CATEGORY_NAME = 'Beverages'
WHERE YEAR(ORDER_DATE) = 2014
GROUP BY CUSTOMERID
HAVING COUNT(ORDER_DETAILS) >= 5
) as SUB ON SUB.CUSTOMERID = C.CUSTOMERID
WHERE YEAR(ORDER_DATE) = 2014
GROUP BY CUSTOMERID, YEAR(ORDER_DATE)
ORDER BY SUM(OD.UNIT_PRICE*OD.QUANTITY)
LIMIT 10
Note I did not test this but just wrote the SQL since I don't have a db to test against so there might be typos
Also Note: I'm expect it is possible to remove the sub query as it is doing a lot of the same joins the outer query is-- but we want to make sure we get the correct result and it is easier to see it is correct this way. You can also test the sub-query by itself to make sure it returns expected results.

How can I combine two queries into one

Using this query:
select C.CustomerID, P.ProductName, count(*) as Ordered
from Customers as C
join Orders as O on C.CustomerID = O.CustomerID
join [Order Details] as OD on O.OrderID = OD.OrderID
join Products as P on OD.ProductID = P.ProductID
group by C.CustomerID, P.ProductName
I can select customer's bought product's name and Ordered
CustomerID|ProductName |Ordered
--------------------------------
ANTON |Alice Mutton|1
BERGS |Alice Mutton|1
BLONP |Alice Mutton|1
BOLID |Alice Mutton|1
BONAP |Alice Mutton|1
And using this:
select P.ProductName, count(*) as Ordered
from Products as P
join [Order Details] as OD on P.ProductID = OD.ProductID
group by P.ProductName
I can select how many times each product has been ordered:
ProductName |Ordered
-------------------------
Alice Mutton |37
Aniseed Syrup |12
Boston Crab Meat |41
Camembert Pierrot|51
Carnarvon Tigers |27
Now, I want to combine theese two queries. I want to see how much each product was ordered by single Customer and what is total amount of orders of this product. How can I do that in one query?
Are you looking for something like this?:
select a.*, b.ordered from (
select C.CustomerID, P.ProductName, count(*) as Ordered
from Customers as C
join Orders as O on C.CustomerID = O.CustomerID
join [Order Details] as OD on O.OrderID = OD.OrderID
join Products as P on OD.ProductID = P.ProductID
group by C.CustomerID, P.ProductName)a
left join
(select P.ProductName, count(*) as Ordered
from Products as P
join [Order Details] as OD on P.ProductID = OD.ProductID
group by P.ProductName)b
on a.ProductName=b.ProductName
To this:
select C.CustomerID, P.ProductName, count(*) as Ordered
from Customers as C
join Orders as O on C.CustomerID = O.CustomerID
join [Order Details] as OD on O.OrderID = OD.OrderID
join Products as P on OD.ProductID = P.ProductID
group by C.CustomerID, P.ProductName
add something like this:
union
select sum(0) as CustomerID, P.ProductName, count(*) as Ordered
from Orders as O
join [Order Details] as OD on O.OrderID = OD.OrderID
join Products as P on OD.ProductID = P.ProductID
group by P.ProductName
Basically, you can put the items for the total orders and the customer orders into temp tables and then join them, as shown below. I chose Name to join on, but that may need to be adjusted, it is hard to tell.
I like these more than sub queries, I think they are typically faster, but you could do a sub query in the join as well. Similar concept. This probably wont give you the final result you're looking for, but hopefully it will give you a good start.
select P.ProductName, count(*) as Ordered
into #totalOrdered
from Products as P
join [Order Details] as OD on P.ProductID = OD.ProductID
group by P.ProductName
select C.CustomerID, P.ProductName, count(*) as Ordered
into #CustomerOrders
from Customers as C
join Orders as O on C.CustomerID = O.CustomerID
join [Order Details] as OD on O.OrderID = OD.OrderID
join Products as P on OD.ProductID = P.ProductID
group by C.CustomerID, P.ProductName
select *
From #CustomerOrders c
join #totalOrdered o on c.ProductName = o.ProductName

Is it possible to get one row by grouping more than one column

I have a query as below. DB from http://www.w3schools.com/sql/default.asp
SELECT count(distinct C.CustomerID),C.Country
FROM Customers C
inner join Orders O
on C.CustomerID = O.CustomerID
inner join OrderDetails D
on O.OrderID = D.OrderID
inner join Products P
on D.ProductID = P.ProductID
group by C.Country,P.CategoryID
order by C.Country
Here is the result from above.
But I want to get one row per country(as below pic) by counting CustomerIDs where any CustomerIDs are in the same country and have a same CategoryID as well. So I have to group by 2 columns. Is there any way to do it? Could you please kindly suggest me?
Thank you.
That's quite simple. Just remove P.CategoryID in your GROUP BY clause.
SELECT COUNT(DISTINCT C.CustomerID), C.Country
FROM Customers C
INNER JOIN Orders O
ON C.CustomerID = O.CustomerID
INNER JOIN OrderDetails D
ON O.OrderID = D.OrderID
INNER JOIN Products P
ON D.ProductID = P.ProductID
GROUP BY C.Country
ORDER BY C.Country;
Update
Following your comment, this should be correct approach then:
SELECT T.Country, SUM(T.Cnt)
FROM (
SELECT COUNT(DISTINCT C.CustomerID) AS Cnt, C.Country
FROM Customers C
INNER JOIN Orders O
ON C.CustomerID = O.CustomerID
INNER JOIN OrderDetails D
ON O.OrderID = D.OrderID
INNER JOIN Products P
ON D.ProductID = P.ProductID
GROUP BY C.Country, P.CategoryID
) AS T
GROUP BY T.Country
ORDER BY T.Country;

2 Questions on Northwind SQL

I have 2 questions about the Northwind SQL Server sample database that I don't know how to solve
Show CustomerID for all customers who have at least three different products, but never use both products in the same category.
Code I tried for this question:
SELECT
CustomerID, p.ProductID,ProductName, CategoryID
FROM
(Orders o
JOIN
[Order Details] od ON o.OrderID = od.OrderID)
JOIN
Products p ON od.ProductID = p.ProductID
Show CustomerID for customers who have orders from all categories.
I've been stuck on these queries for hours, please help guys!
This is link for Northwind sample database: https://northwinddatabase.codeplex.com/
For #2, you could use something like this:
SELECT
c.CustomerID, COUNT(DISTINCT p.CategoryID)
FROM
dbo.Customers c
INNER JOIN
dbo.Orders o ON o.CustomerID = c.CustomerID
INNER JOIN
dbo.[Order Details] od ON od.OrderID = o.OrderID
INNER JOIN
dbo.Products p ON p.ProductID = od.ProductID
GROUP BY
c.CustomerID
HAVING
COUNT(DISTINCT p.CategoryID) = (SELECT COUNT(*) FROM dbo.Categories)
This works for question #1:
SELECT c.CustomerID,
od.productid,
p.ProductName,
COUNT(od.productid),
ct.Category
FROM [Order Details] od
INNER JOIN [dbo].[Products] p on od.ProductID = p.ProductID
INNER JOIN [dbo].[Categories] ct on p.CategoryID = c.CategoryID
INNER JOIN [dbo].[Orders] o on od.OrderID = o.OrderID
INNER JOIN [dbo].[Customers] c on o.CustomerID = c.CustomerID
GROUP BY od.productid,
ct.CategoryID,
p.ProductName,
c.CustomerID
HAVING COUNT(od.productid) > 3
ORDER BY COUNT(od.productid) desc
;