Get orders where order lines meet certain requirements - sql

I have the following simplified tables:
tblOrders
orderID date
---------------------
1 2013-10-04
2 2013-10-05
3 2013-10-06
tblOrderLines
lineID orderID ProductCategory
--------------------------------------
1 1 10
2 1 3
3 1 10
4 2 3
5 3 3
6 3 10
7 3 10
I want to select records from tblOrders ONLY if any order line has ProductCategory = 10. So, if none of the lines of a particular order has ProductCategory = 10, then do not return that order.
How would I do that?

This should do:
SELECT *
FROM tblOrders O
WHERE EXISTS(SELECT 1 FROM tblOrderLines
WHERE ProductCategory = 10
AND OrderID = O.OrderID)

You can use exists for this
Select o.*
From tblOrders o
Where exists (
Select 1
From tblOrderLines ol
Where ol.ProductCategory = 10
And ol.OrderId = o.OrderId
)

try this
SELECT DISTINCT orderId
FROM tblOrders t1
INNER JOIN tblOrderLines t2 ON t1.orderId = t2.orderId
WHERE t2.ProductCategory = 10

Try this:
SELECT *
FROM tblOrders O
JOIN tblOrderLines L
ON O.orderID = L.orderID
WHERE L.OrderID in (SELECT orderID FROM tblOrderLines WHERE ProductCategory = 10)

Related

SQL JOIN omits some fields

I have the following tables:
Product_T with columns:
ProductID,
ProductDescription
OrderLine_T with columns:
OrderID,
ProductID,
OrderedQuantity
Order_T with columns:
OrderID,
CustomerID,
Customer_T with columns:
CustomerID,
CustomerName
I want to list the product ID and description, along with the customer ID and name for the customer who has bought the most of that product and also show the total quantity ordered by that customer.
I came up with following query, to list the max quantity product per order:
SELECT o1.OrderID, o1.ProductID, SUM(o1.OrderedQuantity) AS A
FROM OrderLine_T o1
GROUP BY
o1.ProductID,
o1.OrderID
HAVING SUM(o1.OrderedQuantity) = (
SELECT MAX(s.d)
FROM (
SELECT
o1.OrderID,
o1.ProductID,
SUM(o1.OrderedQuantity) AS d
FROM OrderLine_T o1
GROUP BY
o1.ProductID,
o1.OrderID
) s
WHERE o1.ProductID = s.ProductID
)
And that gave me a correct output of:
50 20 1
48 17 5
32 14 10
59 13 2
1 10 9
2 8 2
69 7 4
4 6 3
32 5 10
55 4 2
2 3 12
1 2 18
26 1 5
But then, when I tried joining it with other tables, so I could select CustomerName and CustomerID, like so:
SELECT
o1.ProductID,
s.CustomerName,
s.CustomerID,
SUM(o1.OrderedQuantity) AS A
FROM OrderLine_T o1
INNER JOIN (
SELECT
c1.CustomerName,
c1.CustomerID,
p1.ProductID
FROM Product_T p1
INNER JOIN OrderLine_T o3 ON p1.ProductID = o3.ProductID
INNER JOIN Order_T o2 ON o3.OrderID = o2.OrderID
INNER JOIN Customer_T c1 ON o2.CustomerID = c1.CustomerID
) s ON s.ProductID = o1.ProductID
GROUP BY
o1.ProductID,
s.CustomerName,
s.CustomerID
HAVING SUM(o1.OrderedQuantity) = (
SELECT MAX(s.d)
FROM (
SELECT
o1.OrderID,
o1.ProductID,
SUM(o1.OrderedQuantity) AS d
FROM OrderLine_T o1
GROUP BY
o1.ProductID,
o1.OrderID
) s
WHERE o1.ProductID = s.ProductID
) ;
The output shrunk to:
17 Contemporary Casuals 1 5
8 Home Furnishings 3 2
7 Eastern Furniture 4 4
10 Eastern Furniture 4 9
20 Dunkins Furniture 8 1
13 Ikards 13 2
Why could that be?
It seems you should be using window functions here, such as ROW_NUMBER, along with conditional aggregation
SELECT
o.ProductID,
p.Description,
CustomerID = MAX(CASE WHEN o.rn = 1 THEN c.CustomerID END),
CustomerName = MAX(CASE WHEN o.rn = 1 THEN c.CustomerName END),
SUM(CASE WHEN o.rn = 1 THEN o.TotalQty END) AS QtyForTopCustomer
SUM(o.TotalQty) AS TotalQty
FROM (
SELECT
o.ProductID,
o.CustomerID,
TotalQty = SUM(oi.OrderedQuantity),
rn = ROW_NUMBER() OVER (PARTITION BY oi.ProductId ORDER BY SUM(oi.OrderedQuantity) DESC)
FROM OrderLine_T ol
INNER JOIN Order_T o ON o.OrderID = ol.OrderID
GROUP BY
o.ProductID,
o.CustomerID
) o
INNER JOIN Customer_T c ON c.CustomerID = o.CustomerID
INNER JOIN Product_T p ON p.ProductID = ol.ProductID
GROUP BY
o.ProductID,
p.Description;
If you only wanted the data for that one customer, you could remove the conditional aggregation and just filter by row-number
SELECT
o.ProductID,
p.Description,
o.CustomerID,
o.CustomerName,
o.TotalQty
FROM (
SELECT
p.ProductID,
p.Description,
o.CustomerID,
TotalQty = SUM(oi.OrderedQuantity),
rn = ROW_NUMBER() OVER (PARTITION BY oi.ProductId ORDER BY SUM(oi.OrderedQuantity) DESC)
FROM OrderLine_T ol
INNER JOIN Order_T o ON o.OrderID = ol.OrderID
GROUP BY
p.ProductID,
p.Description,
o.CustomerID
) o
INNER JOIN Customer_T c ON c.CustomerID = o.CustomerID
INNER JOIN Product_T p ON p.ProductID = ol.ProductID
WHERE o.rn = 1;

Ms Access query count first

I have the following table:
tblOrder
----------------------------
OrderID ==>Primary Key
CustomerID ==>Number
OrderDate ==>Date
Order ==>Number
Price ==>Currency
[Order] field contain a number (like 11, 15, 17) which is a code.
I am trying to find a way, and if it is possible of course, to count [Order] per [CustomerID] that:
==> I) Placed only [Order]=15
==> ii) Made first [Order]=15 and then placed another order which IS NOT 15.
Example Table:
OrderID CustomerID OrderDate Order Price
--------------------------------------------
1 3 1/1/2018 15 100
2 2 3/2/2018 15 300
3 2 7/3/2018 11 400
4 3 2/6/2018 15 200
5 1 5/2/2018 17 300
6 1 11/7/2018 15 600
7 2 1/4/2018 11 200
OutPut Query:
CustomerID OnlyOrder=15 FirstOrder=15
---------------------------------------
1 Null Null
2 1 3
3 2 Null
Thank you in advance.
This is much easier using aggregation and having. The following gets the customers:
select customerId
from tblOrders
group by customerId
having min(iif([order] = 15, orderdate, null)) < max(iif([order] <> 15, orderdate, null)) or
(min([order]) = 15 and max(order = 15));
To count them, use a subquery:
select count(*)
from (select customerId
from tblOrders
group by customerId
having min(iif([order] = 15, orderdate, null)) < max(iif([order] <> 15, orderdate, null)) or
(min([order]) = 15 and max([order]) = 15)
) as c;
Edit:
If you want the first order to be 15 --which is not how I interpret the description -- that is also easy:
select customerId
from tblOrders
group by customerId
having min(iif([order] = 15, orderdate, null)) = min(orderdate) or
(min([order]) = 15 and max(order = 15));
You can use this to count the customer ids.
EDIT:
For the revised question, you want something like this:
select customerId,
iif(min([order]) = 15 and max(order = 15), count(*), 0) as all_first_15,
iif(min(iif([order] = 15, orderdate, null)) = min(orderdate) and min([order]) <> max([order]), count(*), 0) as first_order_15
from tblOrders
group by customerId
having min(iif([order] = 15, orderdate, null)) = min(orderdate) or
(min([order]) = 15 and max(order = 15));
Yup, that's quite a set of rules, but certainly possible.
Let's start with a subquery:
Select all customers where the first order (the order with the lowest (minimal) OrderID) is 15:
SELECT t.CustomerID
FROM tblOrder t
WHERE t.Order = 15
AND EXISTS (
SELECT 1
FROM tblOrder i
WHERE t.CustomerID = i.CustomerID
HAVING Min(i.OrderID) = t.OrderID
)
Then, we can use that to count all orders for these users:
SELECT Count(OrderID)
FROM tblOrder c
WHERE EXISTS (
SELECT 1
FROM tblOrder t
WHERE t.Order = 15
AND EXISTS (
SELECT 1
FROM tblOrder i
WHERE t.CustomerID = i.CustomerID
HAVING Min(i.OrderID) = t.OrderID
)
AND t.CustomerID = c.CustomerID
)
GROUP BY c.CustomerID
Or, if you want to split out counts by 15 and not 15:
SELECT DISTINCT o.CustomerID, (SELECT COUNT(s2.OrderID) FROM tblOrder s2 WHERE s2.CustomerID = o.CustomerID AND s2.Order = 15) As Only15Count, (SELECT COUNT(s1.OrderID) FROM tblOrder s1 WHERE s1.CustomerID = o.CustomerID AND EXISTS (SELECT 1 FROM tblOrder s3 WHERE s3.CustomerID = s1.CustomerID AND s3.Order <> 15)) As TotalCount
FROM tblOrders o
WHERE EXISTS (
SELECT 1
FROM tblOrder t
WHERE t.Order = 15
AND EXISTS (
SELECT 1
FROM tblOrder i
WHERE t.CustomerID = i.CustomerID
HAVING Min(i.OrderID) = t.OrderID
)
AND t.CustomerID = o.CustomerID
)
Note that, in contrast to your expected output, nothing will get returned if the first order isn't 15.

T-Sql select and group by MIN()

I Have 3 tables like:
ProductCategory [1 - m] Product [1-m] ProductPrice
a simple script like this :
select pc.CategoryId ,pp.LanguageId , pp.ProductId ,pp.Price
from ProductCategory as pc
inner join Product as p on pc.ProductId = p.Id
inner join ProductPrice as pp on p.Id = pp.ProductId
order by CategoryId , LanguageId , ProductId
shows these tabular data :
CategoryId LanguageId ProductId Price
----------- ----------- ----------- ---------------------------------------
1 1 1 55.00
1 1 2 55.00
1 2 1 66.00
1 2 2 42.00
2 1 3 76.00
2 1 4 32.00
2 2 3 89.00
2 2 4 65.00
4 1 4 32.00
4 1 5 77.00
4 2 4 65.00
4 2 5 85.00
now what I need is:
for each category, get full row as is but only with the product that has the minimum price.
I just wrote a simple query that does this like :
with dbData as
(
select pc.CategoryId ,pp.LanguageId , pp.ProductId ,pp.Price
from ProductCategory as pc
inner join Product as p on pc.ProductId = p.Id
inner join ProductPrice as pp on p.Id = pp.ProductId
)
select distinct db1.*
from dbData as db1
inner join dbData as db2 on db1.CategoryId = db2.CategoryId
where db1.LanguageId = db2.LanguageId
and db1.Price = (select Min(Price)
from dbData
where CategoryId = db2.CategoryId
and LanguageId = db2.LanguageId)
and its result is correct:
CategoryId LanguageId ProductId Price
----------- ----------- ----------- ---------------------------------------
1 1 1 55.00
1 1 2 55.00
1 2 2 42.00
2 1 4 32.00
2 2 4 65.00
4 1 4 32.00
4 2 4 65.00
Is there a cooler way for doing this ?
Note: The query must be compliant with Sql-Server 2008 R2+
You could use windowed function like RANK():
WITH cte AS
(
select pc.CategoryId, pp.LanguageId, pp.ProductId, pp.Price,
rnk = RANK() OVER(PARTITION BY pc.CategoryId ,pp.LanguageId ORDER BY pp.Price)
from ProductCategory as pc
join Product as p on pc.ProductId = p.Id
join ProductPrice as pp on p.Id = pp.ProductId
)
SELECT CategoryId, LanguageId, ProductId, Price
FROM cte
WHERE rnk = 1;
LiveDemo
you can add languageid to partition if you need product prices per categoryid and languageid
select top 1 with ties pc.CategoryId ,pp.LanguageId , pp.ProductId ,pp.Price
from ProductCategory as pc
inner join Product as p on pc.ProductId = p.Id
inner join ProductPrice as pp on p.Id = pp.ProductId
order by row_number() over (partition by pc.categoryid order by price)
You are not using the Product table in your query, so it doesn't seem necessary. I would right this as:
select ppc.*
from (select pc.CategoryId, pp.LanguageId , pp.ProductId, pp.Price,
row_number() over (partition by pc.CategoryId order by pp.Price) as seqnum
from ProductCategory pc inner join
ProductPrice pp
on pc.ProductId = pp.ProductId
) ppc
where seqnum = 1
order by CategoryId, LanguageId, ProductId;

Get top n occurences based on related table value

I have a table Orders (Id, OrderDate, CreatorId) and a table OrderLines (Id, OrderId, OwnerIdentity, ProductId, Amount)
Scenario is as follows: Someone opens up an Order and other users can then place their product orders on that order. Those users are the OwnerId of OrderLines.
I need to retrieve the top 3 latest orders that a user has placed an order on and display all of his orders placed, to give him an insight in his personal recent orders.
So my end result would be something like
OrderId | ProductId | Amount
----------------------------
1 | 1 | 2
1 | 7 | 1
1 | 2 | 5
4 | 4 | 3
4 | 1 | 2
8 | 4 | 1
8 | 9 | 2
Select o.Id as OrderId, ol.ProductId, ol.Amount from Orders o
inner join OrderLines ol
on o.Id = ol.OrderId where o.Id in
(Select top 3 OrderId from Orders where OwnerId = #OwnerId)
Order By o.OrderDate desc
You can add date time column to OrderLines table to query latest personal orders and then update the code by moving "order by OrderDate desc" section to sub select query.
select * from
(
select OrderId, ProductId, Amount
row_number() over (partition by OrderID order by Orders.OrderDate) as rn
from OrderLines
join Orders
on OrderLines.OrderId = Orders.Id
where OwnerIdentity = x
) lskdfj
where rn <= 3
Try the below query:
SELECT OL.OrderId, OL.ProductID, OL.Amount
FROM OrderLines OL WHERE OL.OrderId IN
(
SELECT TOP 3 O.OrderID FROM orders O LEFT JOIN OrderLines OL2
ON OL2.orderId=O.OrderID
WHERE OL2.OwnerIdentity =...
ORDER BY O.OrderDate DESC
) AND WHERE OL.OwnerIdentity =...
;WITH cte AS (
SELECT ol.OrderId, ol.ProductId, ol.Amount,
ROW_NUMBER()OVER (PARTITION BY ol.OrderId ORDER BY o.OrderDate DESC) rn
FROM OrderLines ol
JOIN Orders o ON ol.OrderId = o.Id
WHERE OwnerIdentity = #OwnerId
)
SELECT OrderId, ProductId, Amount
FROM cte
WHERE rn <= 3

Assistance needed with join query

I have two very simple tables in Oracle 9G:
Customer
userid | firstName | lastName | email
-------+-----------+----------+---------------------
1 user1 User1 user1#mail.in
2 user2 User2 user2#mail.in
Order
orderiD | userId | OrderType | Order_Date | Amount
--------+--------+-----------+------------+-------
1 1 0 12/12/2009 1
2 1 1 13/12/2009 2
3 1 1 14/12/2009 3
4 2 0 12/12/2009 4
5 2 1 16/12/2009 2
6 1 0 14/12/2009 5
7 2 1 17/12/2009 4
8 2 0 10/12/2010 2
I want to select all users which have orders of type 0
select *
from Customer c
inner join Order o on c.userid = o.userid
where o.orderType = '0'
The result is:
orderiD | userId | OrderType | Order_Date | Amount
--------+--------+-----------+------------+--------
1 1 0 12/12/2009 1
4 2 0 12/12/2009 4
6 1 0 14/12/2009 5
8 2 0 10/12/2010 2
Now I need to modify this query to bring only last purchase date for each user id and get the result like this:
orderiD | userId | OrderType | Order_Date | Amount
--------+--------+-----------+------------+--------
6 1 0 14/12/2009 5
8 2 0 10/12/2010 2
How should I modify my query to get this result?
SELECT *
FROM order o
WHERE o.orderType ='0'
AND o.order_date =
( SELECT MAX(o2.order_date)
FROM order o2
WHERE o2.userid = o.userid
AND o2.orderType = '0'
)
or
SELECT o.*
FROM order o
JOIN
( SELECT userid
, MAX(order_date) AS lastPurchaseDate
FROM order
WHERE o.orderType ='0'
GROUP BY userid
) AS grp
ON grp.userid = o.userid
AND grp.lastPurchaseDate = o.order_date
Try this:
select *
from customer c
, order o
where c.userid = o.userid
and o.orderType ='0'
and o.order_date = (
select max(o2.order_date)
from order o2
where o2.userid = o.userid
)
No subquery required, just aggregate functions.
select c.userId, o.OrderType,
max(o.Order_Date),
max(o.orderiD) keep (dense_rank last order by order_date),
max(o.Amount) keep (dense_rank last order by order_date)
from Customer c inner join Order o
on c.userid = o.userid where o.orderType ='0'
group by c.userId, o.OrderType
SELECT *
FROM Order o
INNER JOIN
(SELECT MAX(o.id) AS maxid
FROM Customer c
INNER JOIN Order o ON c.userid = o.userid
WHERE o.orderType ='0'
GROUP BY c.userid) x ON x.maxid = o.id