SQL: list items not sold in between time period - sql

The software we use has two tables for orders, Orders and OrderItems.
I am trying do run a query on the database to show which items have not sold in a period of time.
This is what I have but It's not working ( it brings up all the records)
SELECT
OrderItem.Name,
OrderItem.SKU,
[Order].OrderDate
FROM
[Order]
INNER JOIN OrderItem ON [Order].OrderID = OrderItem.OrderID
WHERE
(OrderItem.SKU NOT IN
(SELECT DISTINCT OrderItem.SKU WHERE ([Order].OrderDate BETWEEN '2014-09-08' AND '2014-01-01')))

You can actually do this with a having clause:
SELECT oi.Name, oi.SKU, max(o.OrderDate) as lastOrderDate
FROM [Order] o INNER JOIN
OrderItem oi
ON o.OrderID = oi.OrderID
GROUP BY oi.Name, oi.SKU
HAVING sum(case when o.OrderDate between '2014-01-01' and '2014-09-08' then 1 else 0 end) = 0;
If you are just looking for orders before this year, it is easier to write the having clause as:
HAVING max(o.OrderDate) < '2014-01-01'

Flip your dates. It should be [Begin Date] Between [End Date]
WHERE (OrderItem.SKU NOT IN
(SELECT DISTINCT OrderItem.SKU
WHERE ([Order].OrderDate BETWEEN '2014-01-01' AND '2014-09-08')))

How about the following:
SELECT oi.Name, oi.SKU, o.OrderDate
FROM [Order] o
INNER JOIN OrderItem oi ON o.OrderID = oi.OrderID
WHERE oi.SKU NOT IN
(
SELECT os.SKU
FROM [Order] os
INNER JOIN OrderItem ois ON os.OrderID = ois.OrderID
WHERE os.OrderDate BETWEEN '2014-01-01' AND '2014-09-08'
)
You have to join to the OrderItem table in the sub query in order to get the SKU.

Looks like your query was build 'upside down'.
SELECT DISTINCT SKU FROM OrderItem oi
WHERE NOT EXISTS
(SELECT 1 FROM Order o
JOIN OrderItem oi2 ON o.OrderID = oi2.OrderID
WHERE oi2.SKU = oi.SKU
AND o.OrderDate BETWEEN '2014-01-01' AND '2014-09-08' );

Ideally, you should have another table containing your distinct items, so you could write the following query to see which items were not sold during a certain period (and may have never been sold at all).
select i.SKU from items
where not exists (
select 1 from OrderItem oi
join Order o on o.OrderID = oi.OrderID
where oi.SKU = i.SKU
and o.OrderDate BETWEEN '2014-01-01' and '2014-09-08'
)
if you don't have such a table, you can select all products that have been ordered at some point, but not during another period
select i.SKU from (
select distinct oi.SKU from OrderItem oi
) i
where not exists (
select 1 from OrderItem oi
join Order o on o.OrderID = oi.OrderID
where oi.SKU = i.SKU
and o.OrderDate BETWEEN '2014-01-01' and '2014-09-08'
)

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

sql get newest date and price of all inventory_items

I have this sql tables, and i need to get from all inventory items the newest date from table: orders with the price paid on this date
table:orders
id
date
table:order_items
order_id
inventory_id
price
table:inventory
item_number
if i do something like that:
SELECT inventory.item_number, orders.date, order_items.price
FROM inventory
INNER JOIN order_items ON inventory.id = order_items.inventory_id
INNER JOIN orders ON order_items.order_id = orders.id
WHERE max(orders.date)
it's not working, and i get an error.
What is the correct way to do that
You need a where clause, but the MAX() cannot go there directly. Try a subquery:
SELECT i.item_number, o.date, orders.price
FROM inventory i INNER JOIN
order_items oi
ON i.id = oi.inventory_id INNER JOIN
orders o
ON oi.order_id = o.id
WHERE o.date = (SELECt max(o2.date) FROM orders o2);

How to Sum Total in Header Detail Tables

Please, I need your help, I'm new to the subject, so I have tow tables Order table and Order Detail table. I want to sum order amount in the order table and group by the product id exists in the order detail table. I'm getting repeated rows.
select p.productName
SUM(o.Amount - o.discount) as OrdersTotal
FROM dbo.order o
inner join
OrderDetail d
ON o.orderid = d.orderid
inner join
dbo.product p
ON d.productid = p.productid
where o.orderdate >= #fromdate
and o.orderdate <= #todate
group by p.productname, o.discount
the result I'm getting is as follow
product name orders total
------------ ------------
product A 150
product A 20
product B 45
product B 13
so please, how can I fix this result? Thanks
You should only group on the product name, so that all records with the same product name are included in the sum of the amount:
select p.productName
SUM(o.Amount) as OrdersTotal
FROM dbo.order o
inner join
OrderDetail d
ON o.orderid = d.orderid
inner join
dbo.product p
ON d.productid = p.productid
where o.orderdate >= #fromdate
and o.orderdate <= #todate
group by p.productname

How do I make the GROUP BY clause in this query run?

Write a SELECT statement that returns three columns:
EmailAddress, OrderID, and the order total for each customer.
To do this, you can group the result set by the EmailAddress and OrderID columns.
In addition, you must calculate the order total from the columns in the OrderItems table.
SELECT c.EmailAddress, oi.OrderID, (oi.ItemPrice * oi.Quantity) -
oi.DiscountAmount
FROM Customers c JOIN Orders o ON c.CustomerID = o.CustomerID
JOIN OrderItems oi ON o.OrderID = oi.OrderID
GROUP BY c.EmailAddress, oi.OrderID
You are looking for GROUP BY and SUM():
SELECT c.EmailAddress, oi.OrderID,
SUM(oi.ItemPrice * oi.Quantity - oi.DiscountAmount)
FROM Customers c JOIN
Orders o
ON c.CustomerID = o.CustomerID JOIN
OrderItems oi
ON o.OrderID = oi.OrderID
GROUP BY c.EmailAddress, oi.OrderID;
Column must appear in the GROUP BY clause or be used in an aggregate function. Your column
(oi.ItemPrice * oi.Quantity) - oi.DiscountAmount
does not satisfy this requirement. I assume what you want to do is
sum((oi.ItemPrice * oi.Quantity) - oi.DiscountAmount)
?

SQL to find customers that have ordered at least once a month

I need to query customers that have ordered at least once a month during a specified year or over multiple years. I don't want to divide how many orders they have and see an average of at least 12 a year. I need to know that they actually ordered at least once in every month during that range.
What I have so far is below, but it seems like there should be a better way to do this. Is there a better way to write this query?
with CustomersThatOrderAtLeastOnceAMonth as (
select c.username, c.id
from customer c
where exists (select 1 from orders o where c.id = o.CustomerId and o.orderdate between '2015-01-01' and '2015-02-01')
and exists (select 1 from orders o where c.id = o.CustomerId and o.orderdate between '2015-02-01' and '2015-03-01')
and exists (select 1 from orders o where c.id = o.CustomerId and o.orderdate between '2015-03-01' and '2015-04-01')
and exists (select 1 from orders o where c.id = o.CustomerId and o.orderdate between '2015-04-01' and '2015-05-01')
and exists (select 1 from orders o where c.id = o.CustomerId and o.orderdate between '2015-05-01' and '2015-06-01')
and exists (select 1 from orders o where c.id = o.CustomerId and o.orderdate between '2015-06-01' and '2015-07-01')
and exists (select 1 from orders o where c.id = o.CustomerId and o.orderdate between '2015-07-01' and '2015-08-01')
and exists (select 1 from orders o where c.id = o.CustomerId and o.orderdate between '2015-08-01' and '2015-09-01')
and exists (select 1 from orders o where c.id = o.CustomerId and o.orderdate between '2015-09-01' and '2015-10-01')
and exists (select 1 from orders o where c.id = o.CustomerId and o.orderdate between '2015-10-01' and '2015-11-01')
and exists (select 1 from orders o where c.id = o.CustomerId and o.orderdate between '2015-11-01' and '2015-12-01')
and exists (select 1 from orders o where c.id = o.CustomerId and o.orderdate between '2015-12-01' and '2016-01-01')
)
How about:
select customerid
from orders o
where o.orderdate >= '2015-01-01' and o.orderdate < '2016-01-01'
group by customerid
having count(distinct year(orderdate)*100 + month(orderdate)) = 12;
If you have an index on orders(customerid, orderdate), I'm not sure if this is faster. It might depend on the distribution of the data. Clearly, if you had 1,000,000 customers and only 1 ordered in all months and that was the only order in January, then your code would probably be faster.