SQL: How to pull particular orders based on order details? - sql

I have four tables namely customers, orders, orderDetails and Products.
Customer table
cId cName
1 James
2 Adam
3 Ed
Order table
oId cId
1 1
2 2
3 3
OrderDetails table
oId odId pId Quantity
1 1 1 50
1 2 2 45
2 3 2 52
3 4 1 44
Products table
pId PName
1 Apple
2 Orange
I want the list of customers who have never ordered Oranges. I am able to pull records of customers whose order details don't have oranges. But in one of the case, James has ordered both apples and oranges. So, the query should not pull James. I can do this with a larger query. But I want this with a smaller query where something I'm missing.
SQL
SELECT c.cId, c.cName, p.PName, od.Quantity FROM customers c
LEFT JOIN orders o ON c.cId = o.cId
LEFT JOIN orderDetails od ON o.oId = od.oId
JOIN products p ON od.pId = p.pId
WHERE od.pId != 2

I would do this using not exists:
with has_oranges as (
select o.*
from orders o join
orderlines ol
on o.oid = ol.oid
where ol.pid = 2
)
select c.*
from customers c
where not exists (select 1
from has_oranges ho
where ho.cid = c.cid
);
If you want customer information, I don't see what oid has to do with anything.
Notes:
The CTE determines who actually has oranges.
You don't need the products table, because you are using the pid.

Use NOT EXISTS
SELECT *
FROM Customers c
WHERE NOT EXISTS (
SELECT 1 FROM orders o
JOIN orderDetails od ON o.oId = od.oId
JOIN products p ON od.pId = p.pId
WHERE p.pName = 'oranges' AND c.cId = o.cId
)

You want all customers that have never ordered oranges. So select all customer IDs that ordered oranges and only show customers that are not in this data set.
select *
from customers c
where cid not in
(
select cid
from orderdetails
where pid = (select pid from products where pname = 'Orange'
);

select * from CustomerTbl where Id in (select t1.Id from CustomerTbl t1
left join OrderTbl t2 on t1.Id = t2.CustomerId
left join OrderDetailTbl t3 on t3.OrderId = t2.Id
left join ProductTbl t4 on t4.Id = t3.ProductId
where t4.Id != 2)
This will return Customers who not ordered Oranges.
This is the sqlfiddle link : http://sqlfiddle.com/#!6/c908d/6

SQL Server 2008 introduced the EXCEPT and INTERSECT keywords for doing this sort of thing. I tend to find that the queries are clearer than when using CTEs.
Microsoft Documentation
select c.cId
from Customer c
except
select o.cId
from Orders o
join OrderDetail od on o.oId = od.oId
and od.pId = 2
cId
-----------
3
You can add the name to the result set by joining to the Customer table in the second half of the query:
select c.cId, c.cName
from Customer c
except
select o.cId, c.cName
from Orders o
join OrderDetail od on o.oId = od.oId
join Customer c on c.cId = o.cId
and od.pId = 2
cId cName
----------- --------------------
3 Ed

We have to eliminate users who took orange. So in the below query i have used sub query
Select C.Cname,OH.oid,PM.Pname,OD.Quantity from Customers C
inner join OrderHeader OH ON C.cid=OH.Cid
inner join OrderDetails OD on oh.oid=od.oid
inner join ProductMast PM on PM.pid=OD.pid where OH.oid not in (select oid
from OrderDetails where pid = 2)

Related

SQL Select query for Select 3 different products

Good Evening,
I've been struggling with a select query on my database on which I want to determine the clients that have placed an order of 3 products from at least 3 different product categories.
Then I want to print the CustomerID and the number of the orders.
This is my Database diagram:
Any Answer would be helpful,
Sincerely Thanos.
I haven't tested this, but I believe it should be close to correct:
SELECT h.CustomerID, COUNT(DISTINCT h.SalesOrderID)
FROM SalesOrderHeader h
INNER JOIN SalesOrderDetail d1 ON h.SalesOrderID = d1.SalesOrderID
INNER JOIN SalesOrderDetail d2 ON h.SalesOrderID = d2.SalesOrderID
INNER JOIN SalesOrderDetail d3 ON h.SalesOrderID = d3.SalesOrderID
INNER JOIN Product p1 ON d1.ProductID = p1.ProductID
INNER JOIN Product p2 ON d2.ProductID = p2.ProductID
INNER JOIN Product p3 ON d3.ProductID = p3.ProductID
WHERE p1.ProductCategoryID <> p2.ProductCategoryID
AND p2.ProductCategoryID <> p3.ProductCategoryID
AND p1.ProductCategoryID <> p3.ProductCategoryID
GROUP BY h.CustomerID
select CustomerId, count(*)
from(
select CustomerId
from SalesOrderHeader soh
join SalesOrderDetail sod using (SalesOrderID)
join Product p using (ProductId)
group by CustomerId, SalesOrderId
having count(distinct ProductCategoryID) > 2
) a
group by CustomerId
I think this should get you the result
Select CustomerID,SalesOrderID , count(CategoryID) CatCount
From
SalesOrderHeader Head inner join SalesOrderDetail Det
on Head.SalesOrderID = Det.SalesOrderID
inner join Product Prod
on Prod.ProductID = Det.ProductID
inner join ProductCategory PCat
on Prod.CategoryID = PCat.CategoryID
Group By CustomerID,SalesOrderID
Having count(CategoryID) > 3
like for : "Can't solve this SQL query"
select CustomerID,count(*) as amount_of_order from
SalesOrder join
(
select SalesOrderID,count(distinct ProductCategoryID) CategoryCount
from SalesOrderDetail JOIN Product using (ProductId)
group by 1
) CatCount using (SalesOrderId)
group by 1
having bool_or(CategoryCount>=3) -- At least on CategoryCount>=3

SQL aggregate query with one-to-many relationship with postgres

I'm trying to aggregate a list of product skus with a query that relates through a line_items table. I've abstracted a simple example of my use case:
my expected result would look like this:
id name skus
1 mike bar sku1,sku2,sku3
2 bort baz sku4
given a schema and data like:
products
id sku
1 sku1
2 sku2
3 sku3
4 sku4
line_items
id order_id product_id
1 1 1
2 1 2
3 1 3
4 2 4
addresses
id name
1 'bill foo'
2 'mike bar'
3 'bort baz'
orders
id address_id total
1 2 66
2 3 99
here's a working query, but it's not correct, i'm getting ALL products for each order. my WHERE should be using orders.id
http://sqlfiddle.com/#!15/70cd7/3/0
however, i can't seem to use orders.id? i'm guessing i need to use a JOIN or LEFT JOIN or somehow change the order of things in my query...
http://sqlfiddle.com/#!15/70cd7/4
http://sqlfiddle.com/#!15/70cd7/12
SELECT orders.id,
addresses.name,
array_agg(DISTINCT products.sku )
FROM orders
LEFT JOIN addresses
ON orders.address_id = addresses.id
LEFT JOIN line_items
ON line_items.order_id = orders.id
LEFT JOIN products
ON products.id = line_items.product_id
GROUP BY orders.id,addresses.name
You can use a correlated subquery with a JOIN to get the list of skus for each order
SELECT
o.id,
a.name,
(SELECT array_to_string(array_agg(sku), ',') AS Skus
FROM products p
INNER JOIN line_items li
ON li.product_id = p.id
WHERE li.order_id = o.id
) AS Skus
FROM orders o
INNER JOIN addresses a
ON a.id = o.address_id
ONLINE DEMO
One solution could be
SELECT orders.id,
addresses.name,
(SELECT string_agg(sku,',') AS skus
FROM products
WHERE id IN
(SELECT DISTINCT line_items.product_id
FROM line_items
WHERE line_items.order_id = orders.id))
FROM orders
inner join addresses
on orders.address_id = addresses.id
;
SQLFiddle

SQL - Select only last order for every clients who had ordered before

There are 2 tables, and I want to select only last order for each client that has ordered before...
(cid,cname) Clients table
1 , David
2 , Tom
3 , Alex
(oid,cid,title,ordertime) Orders table
1,1,"Tshirt",2013-10-1
2,3,"Ball",2013-10-1
3,3,"Food",2013-11-20
*Acording to tables Tom never ordered before. So he will not be listed. Alex ordered 2 times before and I want only show last order.
Output must be like this :*
1,1,"Tshirt",2013-10-1, David
3,3,"Food",2013-11-20, Alex
I tried something like this code but Alex was listed 2 times. I dont understand how I can figure out.
select *
from Clients t2
left join Orders t1
on t1.cid=t2.cid
where t1.ordertime<getutcdate()
order by t1.ordertime desc**
Probably I must use Distinct or Group by but I dont understand how.
Select
x.oid,
x.cid,
x.title,
x.ordertime,
x.cname
From (
Select
o.oid,
o.cid,
o.title,
o.ordertime,
c.cname,
row_number() over (partition by o.cid order by o.ordertime desc) rn
From
Clients c
inner join
Orders o
on c.cid = o.cid
) x
Where
x.rn = 1
SELECT o.*, c.name
FROM orders o INNER JOIN clients c on o.cid = c.cid
INNER JOIN (SELECT MAX(oid) as latestOrderid, cid FROM orders GROUP BY cid) as latest
on latest.latestorderid= orders.oid
Try this:
select o.*, c.cname
from clients c
join orders o on
c.cid = o.cid
and not exists (select *
from orders o2
where o2.cid = o.cid
and o2.ordertime > o.ordertime)

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.

Fetch data from more than one tables using Group By

I am using three tables in a PostgreSql database as:
Customer(Id, Name, City),
Product(Id, Name, Price),
Orders(Customer_Id, Product_Id, Date)
and I want to execute a query to get from them "the customers that have have ordered at least two different products alnong with the products". The query I write is:
select c.*, p.*
from customer c
join orders o on o.customer_id = c.id
join product p on p.id = o.product_id
group by (c.id)
having count(distinct o.product_id)>=2
It throws the error:
"column "p.id" must appear in the GROUP BY clause or be used in an aggregate function
LINE 1: select c.*, p.*".
However if I remove the the p.* from select statement (assuming that I one does not want the products, only the customers), it runs fine. How can I get the products as well?
Update: Having ordered two or more products, a customer must appear on the output as many times as its product he has ordered. I want as output a table with 5 columns:
Cust ID | Cust Name | Cust City | Prod ID | Prod Name | Prod Price
Is it possible in SQL given that group by should be used? Shoul it be used on more than one columns on different tables?
Try this out :
SELECT distinct c.* ,p.*
FROM Customer c
JOIN
(SELECT o.customer_id cid
FROM Product P
JOIN Orders o
ON p.id= o.product_id
GROUP BY o.customer_id
HAVING COUNT(distinct o.product_id)>=2) cp
ON c.id =cp.cid
JOIN Orders o
on c.id=o.customer_id
JOIN Product p
ON o.product_id =p.id
I hope it solves your problem.
I think you can use following query for this question -
SELECT C1.*, p1.*
FROM Customer C1
JOIN Orders O1 ON O1.Customer_Id = C1.Id
JOIN Product P1 ON P1.Id = O1.Product_Id
WHERE C1.Id IN (SELECT c.Id
FROM Customer c
JOIN Orders o ON o.Customer_Id = c.Id
GROUP BY (c.Id)
HAVING COUNT(DISTINCT o.Product_Id) >= 2)