Reputation: 223
I've been having difficulties with the following query. I've been trying to optimize it and perhaps make it more readable. Let's say I have 3 tables orders_returned, orders_completed, orders_delivered with matching columns oder_id, customer_id. Depending on selected options, I might need to retrieve orders which were delivered, then returned and finally completed (same order_id occurs in all three tables) which have the same customer_id. Also I might only need to retrieve only delivered and returned orders in which case I would omit AND order_id IN (SELECT order_id FROM ORDERS_COMPLETED)
from the WHERE clause. For example, Get delivered and returned orders by customers John and Tim
As of now my query looks like this:
SELECT order_id
FROM
(
SELECT order_id, customer_id
FROM ORDERS_RETURNED
UNION
SELECT order_id, customer_id
FROM ORDERS_COMPLETED
UNION
SELECT order_id, customer_id
FROM ORDERS_DELIVERED
)
WHERE
customer_id IN ('customer1', 'customer2', ...)
AND order_id IN (SELECT order_id FROM ORDERS_RETURNED)
AND order_id IN (SELECT order_id FROM ORDERS_COMPLETED)
AND order_id IN (SELECT order_id FROM ORDERS_DELIVERED)
I'm still learning SQL and would like to see if there are better options.
EDIT: I am using Oracle database. There is also Orders table which has distinct order_ids and some other irrelevant columns. It does not store customer_ids. Also, the order might occur in one table or in two of them only, so joins, I think, are of no use here.
Upvotes: 0
Views: 264
Reputation: 21973
you could do this with something like:
with data
as (select customer_id,
order_id,
nvl(max(case status when 'RETURNED' then 'Y' end), 'N') returned,
nvl(max(case status when 'COMPLETED' then 'Y' end), 'N') completed,
nvl(max(case status when 'DELIVERED' then 'Y' end), 'N') delivered
from (select 'RETURNED' status, order_id, customer_id
from orders_returned
union all
select 'COMPLETED' status, order_id, customer_id
from orders_completed
union all
select 'DELIVERED' status, order_id, customer_id
from orders_delivered)
group by customer_id, order_id)
select *
from data
where returned = 'Y'
and delivered = 'Y'
and customer_id in ('xx', 'xxx') ;
or
with data
as (select customer_id,
order_id,
max(returned) returned,
max(completed) completed,
max(delivered) delivered
from (select 'Y' returned, null completed, null delivered, order_id, customer_id
from orders_returned
union all
select null, 'Y', null, order_id, customer_id
from orders_completed
union all
select null, null, 'Y', order_id, customer_id
from orders_delivered)
group by customer_id, order_id)
select *
from data where returned = 'Y' and delivered = 'Y' and customer_id in ('xx', 'xxx');
eg: http://sqlfiddle.com/#!4/3e2fb/2
Upvotes: 0
Reputation: 62841
Since you have an Order table, I presume you are also storing the CustomerId in that table as well. Assuming so, try this:
SELECT DISTINCT O.OrderId
FROM Orders O
LEFT JOIN Orders_Completed OC ON O.OrderId = OC.OrderId
LEFT JOIN Orders_Delivered OD ON O.OrderId = OD.OrderId
LEFT JOIN Orders_Returned ORE ON O.OrderId = ORE.OrderId
WHERE O.CustomerId IN (...)
AND OD.OrderId IS NOT NULL AND ORE.OrderId IS NOT NULL AND OC.OrderId IS NULL
This particular query will return you all distinct orders where customer in (...) where the order has been delivered and returned, but not completed. Toggle the use of the IS NULL and IS NOT NULL to get your desired output.
Good luck.
Upvotes: 2
Reputation: 14292
You should use Joins instead of Inner/Nested queries.
Try below instead ::
SELECT A.order_id, A.customer_id FROM ORDERS_RETURNED A
INNER JOIN ORDERS_COMPLETED B ON A.order_id = B.order_id AND A.customer_id = B.customer_id
INNER JOIN ORDERS_DELIVERED C ON A.order_id = C.order_id AND A.customer_id = C.customer_id
Where A.customer_id IN ('customer1', 'customer2', ...)
Upvotes: 0