SQL Selecting distinct data - sql

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

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 Query for finding Orders having same order lines

How I can write a query that can find me orders having same order lines (details)?
Sample Data
Table: Order
ORDER_ID
--------
A
B
C
D
Table: OrderDetails
OrderID ProductID
------------------
A ProductX
A ProductY
A ProductZ
B ProductX
B ProductY
C ProductZ
D ProductX
D ProductY
D ProductZ
Now I want to pass ProductX,ProductY,ProductZ and get back A and D.
Can this be done in one query?
Maybe something like this is what you want?
SELECT DISTINCT Orders.OrderID
FROM Orders
INNER JOIN OrderDetails ON Orders.OrderID = OrderDetails.OrderID
WHERE OrderDetails.ProductID IN ('ProductX', 'ProductY', 'ProductZ')
GROUP BY Orders.OrderID
HAVING COUNT(*) = 3
Also note that Order is a reserved keyword and should not be used as a table name.
SELECT OrderId FROM
(SELECT DISTINCT o.OrderId, p.Product
FROM Orders o
INNER JOIN OrderDetails p
ON o.OrderId = p .OrderId
WHERE p.Product IN ('ProductX', 'ProductY', 'ProductZ') ) tab
GROUP BY OrderId
HAVING COUNT(*) = 3
I have done the required thing without using the Order table..
SELECT id from ProductT
group by id
having count(*)=3;
SQL Fiddle
Try this
SELECT P.OrderID
FROM Order1 P JOIN OrderDetails D ON
P.OrderID = D.OrderID
WHERE P.OrderID IN (SELECT OrderID FROM OrderDetails WHERE ProductID IN ('ProductX', 'ProductY', 'ProductZ'))
Having Count(P.OrderID)=3
GROUP BY P.OrderID
FIDDLE DEMO

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

SQL Server: fill Null Fields from Not Null Column for multiple rows having same id

I built following Select query to get below results
SELECT Orders.OrderID, OrderDetails.ProductCode, OrderDetails.Coupon
From Orders, OrderDetails
WHERE Orders.OrderID=OrderDetails.OrderID
Results
Order ID Product Code Coupon
22 A
22 B XYZ
22 C
23 D 123
24 E
I want it to display like this:
Order ID Product Code Coupon
22 A XYZ
22 B XYZ
22 C XYZ
23 D 123
24 E
so that it fills empty coupons from not empty coupon field where order id matches.
Your help will be greatly appreciated. Thanks.
SELECT
t1.OrderID,
t1.ProductCode,
MAX(ISNULL(t2.Coupon,'')) as CouponCode,
t1.CustomerName
--Here you have select list by using alias 't'
--don't forget it to add in group by clause
FROM
(
select O.OrderID,OD.ProductCode,OD.CouponCode as Coupon,C.CustomerName
--Here add the list of columns
from Orders O
inner join OrderDetails OD on O.OrderID=OD.OrderID
Inner join customers C on O.CustomerID=C.CustomerID
)t1
INNER JOIN (
select O.OrderID
from Orders O
inner join OrderDetails OD on O.OrderID=OD.OrderID
Inner join customers C on O.CustomerID=C.CustomerID
)t2 ON
CAST(t1.OrderID AS VARCHAR)=
CAST(t2.OrderID AS VARCHAR)
GROUP BY t1.OrderID,
t1.ProductCode,
t1.CustomerName
--Add the extra fields.
order by t1.OrderID
SQL Fiddle with Your Data
Assuming Coupon is unique for each OrderID.
SELECT o1.OrderID, o2.Coupon
FROM OrderDetails o1 LEFT JOIN
(SELECT DISTINCT OrderID, Coupon FROM OrderDetails WHERE NOT Coupon IS NULL) AS o2 ON o1.OrderID = o2.OrderID
This is kind of ugly but does the job:
SELECT o.OrderID, od1.ProductCode, COALESCE(od1.Coupon,od2.Coupon)
From
Orders o
inner join
OrderDetails od1
on
o.OrderID=od1.OrderID
left join
(select OrderID,MAX(Coupon) as Coupon from OrderDetails
where Coupon is not null group by OrderID) od2
on
o.OrderID=od2.OrderID
It's using GROUP BY and MAX to ensure that there's only one row in od2 for each OrderID value, even if multiple rows in OrderDetails already have Coupon set.

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'