Order Line quantity sum total SQL calculation - sql

I have Order:
I have OrderLine:
I want to show which product has been sold the most.
I want to sum every quantity in orderline where it belongs to an order which has status 'completed'
Looking at the data, we see only orderID = 3 has orderstatus = Completed, therefore we only want OrderLine for OrderID = 3 - which is OrderLineID = 6 and OrderLineID = 7.
So our expected result would be ProductID 1 with Quantity 11 as follows:
ProductID|OrderLineQuantity
-------1-----|----------11-----------
My code so far produces an error:
Column 'OrderLine.ProductID' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
My code:
SELECT OrderLine.ProductID, SUM(OrderLineQuantity)FROM OrderLine Inner Join [Order] ON OrderLine.OrderID = [Order].OrderID WHERE OrderStatus = 'Completed'

You need a GROUP BY to fix the syntax problem, and then something else to get the top product:
SELECT TOP (1) ol.ProductID, SUM(ol.OrderLineQuantity)
FROM OrderLine ol Inner Join
[Order] o
ON ol.OrderID = o.OrderID
WHERE o.OrderStatus = 'Completed'
GROUP BY ol.ProductID
ORDER BY SUM(ol.OrderLineQuantity) DESC;

Related

Join three tables, select only positive amount from second table + first and latest date from third table

I need this result for each Product.
Product ID Date Amount
ID1 10.01.21 3
ID2 15.04.22 2
ID3 NULL NULL
From these 3 Tables
Products Order List Order
ID SKU ProductID OrderID Amount OrderID Date
ID1 ProductA ID1 Order1 -5 Order1 12.03.22
ID2 ProductB ID1 Order2 3 Order2 10.01.21
ID3 ProductC ID2 Order3 2 Order3 15.04.22
For each Product I only want the first positive value from the second table (Order List Table) and the latest and first order Date from table 3 (Order Table).
Following would be wrong even though its the latest, but the amount is -5 (not positive)
ID1, 12.03.22, -5
So far, I got
SELECT
Product.ID,
OrderList.Amount,
Order.Date
FROM
Product
INNER JOIN OrderList ON OrderList.ProductID = Product.ID
INNER JOIN Order ON OrderList.OrderID = Order.ID
I tried several min() or Min distinct, left join etc, but never got any result like this.
If - as mentioned in the comments - you only need a record from orderlist with a positive amount from the most recent order with a positive amount, you can use:
select first 1 p.ID, p.SKU, ol.AMOUNT, o.ORDERDATE
from ORDERS o
inner join ORDERLIST ol on o.ORDERID = ol.ORDERID
inner join PRODUCTS p on p.ID = ol.PRODUCTID
where ol.AMOUNT > 0
order by o.ORDERDATE desc
This will only return a single record, even if that latest order has more than one orderlist record with a positive amount.
If on the other hand you want all orderlist records with a positive amount from the most recent order with positive amount, you can use:
select p.ID, p.SKU, ol.AMOUNT, o.ORDERDATE
from (
select first 1 orders.ORDERID, orders.ORDERDATE
from ORDERS
where exists (
select *
from ORDERLIST
where orderlist.ORDERID = orders.ORDERID and orderlist.AMOUNT > 0)
order by orders.ORDERDATE desc
) o
inner join ORDERLIST ol on o.ORDERID = ol.ORDERID
inner join PRODUCTS p on p.ID = ol.PRODUCTID
where ol.AMOUNT > 0
With the modified requirement specified in the comments:
I actually first need ALL SKU IDs and for each SKU ID one order and
one Items from the order List, but the newest and not negative.
you can do something like this with Firebird 2.5:
with recentproduct as (
select ol.productid, max(o.orderdate) as orderdate
from orders o
inner join orderlist ol on o.orderid = ol.orderid
where ol.amount > 0
group by ol.productid
)
select p.id as productid, p.sku, ol.orderid, ol.amount
from products p
left join (
recentproduct rp
inner join orderlist ol on rp.productid = ol.productid and ol.amount > 0
inner join orders o on o.orderdate = rp.orderdate and o.orderid = ol.orderid
) on p.id = rp.productid
Be aware, this solution is not perfect: if there are multiple orders for the same product on a single date, this will return all of them. This is easier solved in Firebird 3.0 and higher using window functions.

SQL Selecting distinct data

Let's say I have tables called Orders and OrdersInfo. Orders table looks like:
OrderID
1
2
3
And OrdersInfo
OrderID ProductID Amount
1 1 2
1 2 1
2 3 4
I'd like to get all information with the newest OrderID.
In this case the answer should be an information of OrderID 3 which is gonna be empty. My problem is that I have to group it and my information does not show up properly.
I've tried something like this:
SELECT OI.OrderID, OI.ProductID, OI.Amount
FROM OrdersInfo OI
JOIN Orders O
ON OI.OrderID = O.OrderID
GROUP BY OI.OrderID, OI.ProductID, OI.Amount
HAVING OI.OrderID = MAX(O.OrderID)
You can try this-
SELECT
O.OrderID
,OI.ProductID
,OI.Amount
FROM
(
SELECT
MAX(OrderID) AS OrderID
FROM Orders
) AS O
LEFT JOIN OrdersInfo AS OI
OI.OrderID=O.OrderID
USE LEFT JOIN and you will get NULLs for OrderID = 3
SELECT TOP 1 O.OrderID, OI.ProductID, OI.Amount
FROM Orders O
LEFT JOIN OrdersInfo OI
ON OI.OrderID = O.OrderID
ORDER BY O.OrderId DESC
USE TOP 1 for MSQL Server. LIMIT 1 for postgresql

SQL Change output of column if duplicate

I have a table which has rows for each product that a customer has purchased. I want to output a column from a SELECT query which shows the time it takes to deliver said item based on whether the customer has other items that need to be delivered. The first item takes 5 mins to deliver and all subsequent items take 2 mins to deliver e.g. 3 items would take 5+2+2=9 mins to deliver.
This is what I have at the moment(Using the Northwind sample database on w3schools to test the query):
SELECT orders.customerid,
orders.orderid,
orderdetails.productid,
CASE((SELECT Count(orders.customerid)
FROM orders
GROUP BY orders.customerid))
WHEN 1 THEN '00:05'
ELSE '00:02'
END AS DeliveryTime
FROM orders
LEFT JOIN orderdetails
ON orderdetails.orderid = orders.orderid
This outputs '00:05' for every item due to the COUNT in my subquery(I think?), any ideas on how to fix this?
Try this
SELECT orders.customerid,
orders.orderid,
orderdetails.productid,
numberorders,
2 * ( numberorders - 1 ) + 5 AS deleveryMinutes
FROM orders
INNER JOIN (SELECT orders.customerid AS countId,
Count(1) AS numberOrders
FROM orders
GROUP BY orders.customerid) t1
ON t1.countid = orders.customerid
LEFT JOIN orderdetails
ON orderdetails.orderid = orders.orderid
ORDER BY customerid
Gregory's answer works a treat and here's my attempts
-- Without each product line item listed
SELECT O.CustomerId,
O.OrderId,
COUNT(*) AS 'NumberOfProductsOrderd',
CASE COUNT(*)
WHEN 1 THEN 5
ELSE (COUNT(*) * 2) + 3
END AS 'MinutesToDeliverAllProducts'
FROM Orders AS O
INNER JOIN OrderDetails AS D ON D.OrderId = O.OrderId
GROUP BY O.CustomerId, O.OrderId
-- Without each product line item listed
SELECT O.CustomerId,
O.OrderId,
D.ProductId,
CASE
WHEN P.ProductsInOrder = 1 THEN 5
ELSE (P.ProductsInOrder * 2) + 3
END AS 'MinutesToDeliverAllProducts'
FROM Orders AS O
INNER JOIN OrderDetails AS D ON D.OrderId = O.OrderId
INNER JOIN (
SELECT OrderId, COUNT(*) AS ProductsInOrder
FROM OrderDetails
GROUP BY OrderId
) AS P ON P.OrderId = O.OrderId
GROUP BY O.CustomerId,
O.OrderId,
D.ProductId,
P.ProductsInOrder
Final code is below for anyone interested:
SELECT O.CustomerId,
O.OrderId,
Group_Concat(D.ProductID) AS ProductID,
CASE COUNT(*)
WHEN 1 THEN 5
ELSE (COUNT(*) * 2) + 3
END AS 'MinutesToDeliverAllProducts'
FROM Orders AS O
INNER JOIN OrderDetails AS D ON D.OrderId = O.OrderId
GROUP BY O.CustomerId

How filter tables in sql in group

I have two tables.
Customer | OrderItems
CustomerID CustomerName | OrderItemID OrderID CustomerID Status
1 ABC | 1 1 1 Started
2 1 1 Started
| 3 1 1 NotStarted
Now I want to get the record of all the customer where the status of orderItems is Completed. Means in this case the order is incomplete.
so If I want to get the status of incomplete orders it should give me for customer 1 is order1.
even though the items are started of 1st two but still I want to get that Incomplete.
Not sure if I understood you correctly, but this should do the job:
select distinct c.CustomerId, oi.OrderId
from Customer c
inner join OrderItems oi on c.CustomerID = oi.CustomerID
where c.OrderId not in (select o.OrderId from OrderItems o where o.Status <> 'Started')
select OrderID, CustomerID,
SUM(case Status when 'Started' Then 0 Else 1) NS
from OrderItems
having NS > 0;
If you only want the customers with NO INCOMPLETE ORDERS, then this should do the trick:
SELECT C.CustomerID, C.CustomerName
FROM Customer AS C
WHERE (((C.CustomerID) Not In
(SELECT DISTINCT [O].CustomerID
FROM OrderItems AS O
WHERE ((([O].Status)="NotStarted")))));
I miss something though: I think there should be an ORDER table with information regarding the order. Data in your OrderItems table would be inconsistent if you changed the customerID for one record let's say for OrderItemID = 3 the clientID is 2. Are there two different clients for the same order? I suppose that you didn't handle all the information and that there is an Order table.
Regards,
select OrderID, CustomerID,
SUM(case Status when 'NotStarted' Then 1 Else 0) NS
from OrderItems
group by OrderID, CustomerID
having NS > 0;
select c.customerId,c.Customername,O.OrderID, from Customer c left join OrderItem O on C.customerID = o.OrderID where o.Status = 'started'

Optimize SQL query for canceled orders

Here is a subset of my tables:
orders:
- order_id
- customer_id
order_products:
- order_id
- order_product_id (unique key)
- canceled
I want to select all orders (order_id) for a given customer(customer_id), where ALL of the products in the order are canceled, not just some of the products. Is there a more elegantly or efficient way of doing it than this:
select order_id from orders
where order_id in (
select order_id from orders
inner join order_products on orders.order_id = order_products.order_id
where order_products.customer_id = 1234 and order_products.canceled = 1
)
and order_id not in (
select order_id from orders
inner join order_products on orders.order_id = order_products.order_id
where order_products.customer_id = 1234 and order_products.canceled = 0
)
If all orders have at least one row in order_products, Try this
Select order_id from orders o
Where Not Exists
(Select * From order_products
Where order_id = o.order_id
And cancelled = 1)
If the above assumption is not true, then you also need:
Select order_id from orders o
Where Exists
(Select * From order_products
Where order_id = o.order_id)
And Not Exists
(Select * From order_products
Where order_id = o.order_id
And cancelled = 1)
The fastest way will be this:
SELECT order_id
FROM orders o
WHERE customer_id = 1234
AND
(
SELECT canceled
FROM order_products op
WHERE op.order_id = o.order_id
ORDER BY
canceled DESC
LIMIT 1
) = 0
The subquery will return 0 if and only if there had been some products and they all had been canceled.
If there were no products at all, the subquery will return NULL; if there is at least one uncanceled product, the subquery will return 1.
Make sure you have an index on order_products (order_id, canceled)
Something like this? This assumes that every order has at least one product, otherwise this query will return also orders without any products.
select order_id
from orders o
where not exists (select 1 from order_products op
where canceled = 0
and op.order_id = o.order_id
)
and o.customer_id = 1234
SELECT customer_id, order_id, count(*) AS product_count, sum(canceled) AS canceled_count
FROM orders JOIN order_products
ON orders.order_id = order_products.order_id
WHERE customer_id = <<VALUE>>
GROUP BY customer_id, order_id
HAVING product_count = canceled_count
You can try something like this
select orders.order_id
from #orders orders inner join
#order_products order_products on orders.order_id = order_products.order_id
where order_products.customer_id = 1234
GROUP BY orders.order_id
HAVING SUM(order_products.canceled) = COUNT(order_products.canceled)
Since we don't know the database platform, here's an ANSI standard approach. Note that this assumes nothing about the schema (i.e. data type of the cancelled field, how the cancelled flag is set (i.e. 'YES',1,etc.)) and uses nothing specific to a given database platform (which would likely be a more efficient approach if you could give us the platform and version you are using):
select op1.order_id
from (
select op.order_id, cast( case when op.cancelled is not null then 1 else 0 end as tinyint) as is_cancelled
from #order_products op
) op1
group by op1.order_id
having count(*) = sum(op1.is_cancelled);