Return only one data from multiple row - sql

I have 4 tables called orders, order_details, products, and storages. Every products can have multiple thumbnail image that saved on storages table.
I want to return specific orders by id which return multiple row of order_details where every order_details have only one product. In every product, I want to get only one thumbnail image from storages table.
Here's what comes in my mind if I want to get a row where order_id = 1,
SELECT *
FROM orders o
JOIN order_details od ON o.id = od.order_id
JOIN products p ON p.id = od.product_id
JOIN storages s ON s.product_id = p.id --> i haven't figured out how to return only one image for every product in order_details
WHERE o.id = 1
Can somebody give me a help, I've tried to figured out this for days but still not getting it right :(
Thank you in advance.

A simple method is to use row_number():
SELECT *
FROM orders o JOIN
order_details od
ON o.id = od.order_id JOIN
products p
ON p.id = od.product_id JOIN
(SELECT s.*,
ROW_NUMBER() OVER (PARTITION BY product_id ORDER by random()) as seqnum
FROM storages s
) s
ON s.product_id = p.id
WHERE o.id = 1 AND seqnum = 1;
This returns a random image. You can replace the ORDER BY to get any image you want -- the oldest, newest, biggest, smallest or whatever.

I haven't figured out how to return only one image for every product in order_details
In Postgres, I would recommend distinct on:
select distinct on (o.id, od.product_id) *
from orders o
join order_details od on o.id = od.order_id
join products p on p.id = od.product_id
join storages s on s.product_id = p.id
order by o.id, od.product_id, s.id
This guarantees just one rows per order and product, with the storage that has the smallest id. You can filter on a given order id with a where clause, if you like.
Or maybe you want to use the primary key of the order details instead of the product (this allows twice the same product in two different order details of the same order).
select distinct on (o.id, od.id) *
from orders o
join order_details od on o.id = od.order_id
join products p on p.id = od.product_id
join storages s on s.product_id = p.id
order by o.id, od.id, s.id

Related

Selections through SQL

I am attempting to create a list of all orders that contain six specific products. The current query I'm using is:
SELECT o.order_date,
o.price AS revenue,
o.cost,
f.id AS fileid,
o.id AS orderid,
o.fk_order_status_id,
o.date_started,
o.date_completed,
p.description AS product,
pp.description AS subproduct
FROM orders o
JOIN file f
f.id = o.fk_file_id
JOIN product p
ON p.id = o.fk_product_id
JOIN product_product pp
ON pp.fk_product_id = o.fk_product_id
WHERE o.fk_product_id IN ('66','8','6','21','11')
Which pulls all orders that have ANY of those products. What I need to narrow down the results are orders that have those products altogether, not only one or two of the products. We are attempting to get counts of a Work Flow that we have recently implemented
You can use aggregation and having:
SELECT o.order_date, o.price AS revenue, o.cost,
o.id AS orderid, o.fk_order_status_id, o.date_started, o.date_completed
FROM orders o JOIN
file f
ON f.id = o.fk_file_id JOIN
product p
ON p.id = o.fk_product_id JOIn
product_product pp
ON pp.fk_product_id = o.fk_product_id
WHERE o.fk_product_id IN (66, 8, 6, 21, 11)
GROUP BY o.order_date, o.price, o.cost,
o.id, o.fk_order_status_id, o.date_started, o.date_completed
HAVING COUNT(DISTINCT o.fk_product_id) = 5; -- number of products in the list

How do we find which customers placed orders with items made in USA refer to image

Which customers placed orders with items made inside the USA?
SELECT DISTINCT, WHERE, Temporary Table, Subquery
tables to refer
I would use exists with a correlated subquery that follows the relationships like customer > order > order_item > product > supplier and filters on US suppliers:
select c.*
from customer c
where exists (
select 1
from order o
inner join order_item oi on oi.order_id = o.id
inner join product p on p.id = oi.product_id
inner join supplier s on s.id = p.supplier_id
where o.customer_id = c.id and s.country = 'USA'
)

How to create a function that totals an order with several items?

I need to create a function which will return the total of an order. I've been given three tables with the following variables
Table 1 - Order
Order_ID
Date_Placed
Date_Fulfilled
Table 2 - Order Product
Order_ID
Product_ID
Product_Quantity
Table 3 - Product
Product_ID
Price
I'm struggling to put together a coherent function. Any help would be greatly appreciated.
I've already attempted to set up the function with joins between both tables, but am unable to figure out where I should be putting my equation.
BEGIN
SELECT order.order_id, SUM(product.price * order_item.quantity)
FROM `order`
JOIN `order_item` ON order.order_id = order_product.order_id
JOIN `product` ON order_product.product_id = product.product_id;
END $$
You might be surprised, but the orders table is not needed for this query. You can just aggregate off the other two tables:
SELECT oi.order_id, SUM(p.price * oi.quantity)
FROM order_item oi JOIN
product p
ON po.product_id = p.product_id
GROUP BY oi.order_id;
You'll need to take your select statement, and group it by your order.order_id. That way you'll have one row per order, with the sum total of that order.
SELECT order.order_id, SUM(product.price * order_item.quantity) as total_price
FROM `order`
JOIN `order_item` ON order.order_id = order_product.order_id
JOIN `product` ON order_product.product_id = product.product_id
GROUP BY order.order_id
this will work:
SELECT order.order_id, SUM(product.price * order_item.quantity)
FROM order o,
JOIN order_item oi,
JOIN product p where
o.order_id = oi.order_id and
oi.product_id = p.product_id
group by order_product.product_id = product.product_id;

Can this oracle query be tuned anymore?

I'm trying to minimize the cost of this query as much as possible without creating any indexes.
This is the original query with a cost of 599:
SELECT DISTINCT OL.PRODUCT_ID
FROM ORDERS O JOIN ORDER_LINES OL ON (O.ORDER_ID = OL.ORDER_ID)
JOIN PRODUCTS P ON (OL.PRODUCT_ID = P.PRODUCT_ID)
JOIN CUSTOMERS C ON (C.CUSTOMER_ID = O.CUSTOMER_ID)
WHERE C.CUSTOMER_ID = 474871
OR UPPER(C.FIRST_NAME) = 'EDGAR';
This is what I've done so far. The cost is now 344:
SELECT OL.PRODUCT_ID
FROM ORDER_LINES OL
WHERE EXISTS
(SELECT ORDER_ID
FROM ORDERS
WHERE CUSTOMER_ID = 474871
AND ORDER_ID = OL.ORDER_ID)
OR EXISTS
(SELECT ORDER_ID
FROM ORDERS
WHERE CUSTOMER_ID IN
(SELECT CUSTOMER_ID
FROM CUSTOMERS
WHERE UPPER(FIRST_NAME) = 'EDGAR')
AND ORDER_ID = OL.ORDER_ID);
Is there anything that stands out that I may try to drive down the cost more?
Here is a screen shot of the explain plan:
Screenshot of ERD:
Looking at the cost is misleading and may lead you to make changes that aren't actually beneficial. To quote Tom Kyte, "You cannot compare the cost of 2 queries with each other. ... they might as well be random numbers."
The best way to check query performance is to actually time the query, ideally with realistic data. You should also be wary of premature optimization. Your first query is pretty straight-forward; I would stick with it unless a performance issue manifests.
SELECT OL.PRODUCT_ID
FROM ORDER_LINES OL
WHERE OL.ORDER_ID IN
(SELECT ORDER_ID FROM ORDERS
WHERE CUSTOMER_ID IN (SELECT CUSTOMER_ID FROM CUSTOMERS
WHERE CUSTOMER_ID = 474871 OR UPPER(FIRST_NAME) = 'EDGAR')
);
I suppose there is an index on OL.ORDER_ID (now you have FULL SCAN of ORDER_LINES)
I'm curious if the engine is smart enough to apply the where clause before the joins... If it's doing it after the join, then the results it has to scan are larger than they need to be... What happens if you move the limiting criteria to the join so it HAS to be evaluated before the join occurs. (fully expect this to be 599 or less. just don't know if it will be less...
SELECT OL.PRODUCT_ID
FROM CUSTOMERS C
INNER JOIN ORDERS O
ON (C.CUSTOMER_ID = O.CUSTOMER_ID)
AND (C.customer_ID = 47871 OR upper(C.First_name) = 'EDGAR')
INNER JOIN ORDER_LINES OL ON (O.ORDER_ID = OL.ORDER_ID)
INNER JOIN PRODUCTS P ON (OL.PRODUCT_ID = P.PRODUCT_ID)
GROUP BY OL.Product_ID
I wonder if the OR is causing the problem....
if you run it w/o the or how much cost is reduced
and then what happens if you union the two sets instead of using an or.
SELECT OL.PRODUCT_ID
FROM CUSTOMERS C
INNER JOIN ORDERS O
ON (C.CUSTOMER_ID = O.CUSTOMER_ID)
INNER JOIN ORDER_LINES OL ON (O.ORDER_ID = OL.ORDER_ID)
INNER JOIN PRODUCTS P ON (OL.PRODUCT_ID = P.PRODUCT_ID)
WHERE C.customer_ID = 47871
UNION
SELECT OL.PRODUCT_ID
FROM CUSTOMERS C
INNER JOIN ORDERS O
ON (C.CUSTOMER_ID = O.CUSTOMER_ID)
INNER JOIN ORDER_LINES OL ON (O.ORDER_ID = OL.ORDER_ID)
INNER JOIN PRODUCTS P ON (OL.PRODUCT_ID = P.PRODUCT_ID)
WHERE upper(C.First_name) = 'EDGAR')

sql - aggregation issue

I have two tables in my DB - products & orders. An order can only be of one kind of product.
Here's the basic idea:
What I'm trying to do is a query that given a copmany_id returns all the products (from that company) that have less than 10 orders (including 0)
my query looks like this:
SELECT p.*
FROM product p,
order o
WHERE p.company_id =?
AND o.product_id = p.id
GROUP BY p.id
HAVING Count(o.id) < 10
ORDER BY p.id DESC
The query works fine for products which have 0 < orders but doesn't return ones with 0 orders. What do I need to do to return them as well?
You're INNER JOINING your two tables, which means that only those products are returned for which there is at least one order.
You will need to LEFT OUTER JOIN the order table:
SELECT p.*
FROM product p
LEFT OUTER JOIN order o
ON o.product_id = p.id
WHERE p.company_id = ?
GROUP BY p.id
HAVING Count(o.id) < 10
ORDER BY p.id DESC
A left outer join will return every record on the left-hand side of the JOIN operation at least once, regardless if there is a matching record to the right-hand side of the JOIN operation.
try left outer join
SELECT p.*
FROM product p
LEFT OUTER JOIN order o
ON o.product_id = p.id
WHERE p.company_id =?
GROUP BY p.id
HAVING Count(o.id) < 10
ORDER BY p.id DESC
See this example for left outer join