Selections through SQL - 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

Related

Gross sales of top ten customers that made at least 5 purchases of some Category in a specified year

I need to get (sales.customers) | year | gross_sales of top ten customers that made at least 5 purchases of Category "Beverages" in the year 2014. I have already written these SELECT queries, but since I am new to SQL, I think I am very inefficient in writing code. This does not work properly and there is probably a simpler way of doing it. I have also pinned a picture of an ER diagram .
SELECT
T6.COMPANYNAME, YEAR, GROSS_SALES
FROM
(SELECT T1.CUSTID
FROM
(SELECT R.CUSTID, COUNT(R.CUSTID) AS NUMBEROFSALES
FROM SALES.ORDERDETAILS O
RIGHT JOIN PRODUCTION.PRODUCTS P ON P.PRODUCTID = O.PRODUCTID
RIGHT JOIN SALES.ORDERS R ON R.ORDERID = O.ORDERID
INNER JOIN SALES.CUSTOMERS C1 ON R.CUSTID = C1.CUSTID
INNER JOIN PRODUCTION.CATEGORIES C2 ON P.CATEGORYID = C2.CATEGORYID
WHERE C2.CATEGORYNAME = 'Beverages' AND YEAR(R.ORDERDATE) = 2014
GROUP BY R.CUSTID
ORDER BY SUM(R.CUSTID) DESC) T1
--HAVING COUNT(R.CUSTID) > 5
RIGHT JOIN
(SELECT R.CUSTID, SUM(O.UNITPRICE) AS MONEYSPENT
FROM SALES.ORDERDETAILS O
RIGHT JOIN PRODUCTION.PRODUCTS P ON P.PRODUCTID = O.PRODUCTID
RIGHT JOIN SALES.ORDERS R ON R.ORDERID = O.ORDERID
INNER JOIN SALES.CUSTOMERS C1 ON R.CUSTID = C1.CUSTID
INNER JOIN PRODUCTION.CATEGORIES C2 ON P.CATEGORYID = C2.CATEGORYID
WHERE C2.CATEGORYNAME = 'Beverages' AND YEAR(R.ORDERDATE) = 2014
GROUP BY R.CUSTID
ORDER BY SUM(O.UNITPRICE) DESC) T2 ON T1.CUSTID = T2.CUSTID
ORDER BY T1.NUMBEROFSALES DESC
LIMIT 10) T5
INNER JOIN
(SELECT DISTINCT(T4.COMPANYNAME), T4.CUSTID, YEAR, GROSS_SALES
FROM
(SELECT R.CUSTID AS CUSTID, YEAR(R.ORDERDATE) AS YEAR, SUM(O.UNITPRICE * O.QTY * (1 - O.DISCOUNT)) AS GROSS_SALES
FROM SALES.ORDERDETAILS O
RIGHT JOIN SALES.ORDERS R ON R.ORDERID = O.ORDERID
INNER JOIN SALES.CUSTOMERS C1 ON R.CUSTID = C1.CUSTID
GROUP BY R.CUSTID, YEAR(R.ORDERDATE)
ORDER BY YEAR(R.ORDERDATE)) T3
INNER JOIN
(SELECT C.COMPANYNAME, C.CUSTID
FROM SALES.ORDERS R
INNER JOIN SALES.CUSTOMERS C ON R.CUSTID = C.CUSTID) T4 ON T3.CUSTID = T4.CUSTID) T6 ON T5.CUSTID = T6.CUSTID
I'm not going to try and fix your code or explain the misakes there, as there are many of them. Instead based on your requirements I wrote a query that solves the problem. I show it in steps below which should make clear the process I used to solve the problem.
First how do we find the top ten customers that made 5 purchases of Beverages?
Take customer table and join to orders with beverages (inner join will exclude customers that don't meet criteria)
SELECT CUSTOMERID
FROM CUSTOMERS C
JOIN ORDERS O ON C.CUSTOMERID = O.CUSTOMERID
JOIN ORDER_DETAILS OD ON O.ORDERID = OD.ORDERID
JOIN PRODUCTS P ON OD.PRODUCTID = P.PRODUCTID
JOIN CATEGORIES C ON P.CATEGORYID = C.CATEGORYID AND C. CATEGORY_NAME = 'Beverages'
WHERE YEAR(ORDER_DATE) = 2014
GROUP BY CUSTOMERID
HAVING COUNT(ORDER_DETAILS) >= 5
Now we need the sum of order (for 2014) by customers which looks like this:
SELECT CUSTOMERID, YEAR(ORDER_DATE) AS YEAR, SUM(OD.UNIT_PRICE*OD.QUANTITY) AS TOTAL_SPEND
FROM CUSTOMERS C
JOIN ORDERS O ON C.CUSTOMERID = O.CUSTOMERID
JOIN ORDER_DETAILS OD ON O.ORDERID = OD.ORDERID
WHERE YEAR(ORDER_DATE) = 2014
GROUP BY CUSTOMERID, YEAR(ORDER_DATE)
Now we just combine these two queries like this:
SELECT CUSTOMERID, YEAR(ORDER_DATE) AS YEAR, SUM(OD.UNIT_PRICE*OD.QUANTITY) AS TOTAL_SPEND
FROM CUSTOMERS C
JOIN ORDERS O ON C.CUSTOMERID = O.CUSTOMERID
JOIN ORDER_DETAILS OD ON O.ORDERID = OD.ORDERID
JOIN (
SELECT CUSTOMERID
FROM CUSTOMERS C
JOIN ORDERS O ON C.CUSTOMERID = O.CUSTOMERID
JOIN ORDER_DETAILS OD ON O.ORDERID = OD.ORDERID
JOIN PRODUCTS P ON OD.PRODUCTID = P.PRODUCTID
JOIN CATEGORIES C ON P.CATEGORYID = C.CATEGORYID AND C. CATEGORY_NAME = 'Beverages'
WHERE YEAR(ORDER_DATE) = 2014
GROUP BY CUSTOMERID
HAVING COUNT(ORDER_DETAILS) >= 5
) as SUB ON SUB.CUSTOMERID = C.CUSTOMERID
WHERE YEAR(ORDER_DATE) = 2014
GROUP BY CUSTOMERID, YEAR(ORDER_DATE)
ORDER BY SUM(OD.UNIT_PRICE*OD.QUANTITY)
LIMIT 10
Note I did not test this but just wrote the SQL since I don't have a db to test against so there might be typos
Also Note: I'm expect it is possible to remove the sub query as it is doing a lot of the same joins the outer query is-- but we want to make sure we get the correct result and it is easier to see it is correct this way. You can also test the sub-query by itself to make sure it returns expected results.

Return only one data from multiple row

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

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'
)

Left join when there are lots of matched rows from right table

I have two tables.
Product(id, name)
LineItem(id, product_id, order_id)
Order(id, state)
Order can have many products. One product can belong to many orders at the same time.
I would like to select Products, which don't have orders with specific statuses(i.e. 1, 2).
My query is
SELECT products.id, products.price
FROM "products"
LEFT OUTER JOIN line_items ON line_items.product_id = products.id
LEFT OUTER JOIN orders ON orders.id = line_items.order_id AND orders.status IN (1, 2)
WHERE (products.price > 0) AND (orders.id IS NULL) AND "products"."id" = $1
GROUP BY products.id, products.price [["id", 11]]
11 is an id of a product, that should not appear to the result, but it does.
I would like to select Products, which don't have orders with specific statuses(i.e. 1, 2).
SELECT * FROM products p -- I would like to select Products
WHERE NOT EXISTS( -- , which don't have
SELECT *
FROM orders o -- orders
JOIN line_items li ON li.order_id = o.id
WHERE li.product_id = p.id
AND o.status IN (1,2) -- with specific statuses(i.e. 1, 2).
);
select p.id, p.name
from products p
join lineitem l on l.product_id = p.id
join `order` o on l.order_id = o.id
group by p.id, p.name
having sum(case when o.state in (1,2) then 1 else 0 end) = 0
The idea is to start with the products table and use left join to find orders with 1 or 2. If they don't exist, then you want the product:
select p.id, p.name
from product p left join
lineitem li
on li.product_id = p.id left join
orders o -- a better name for the table
on li.order_id = o.id and
o.state in (1, 2)
where o.id is null
group by p.id, p.name;

SQL selection criteria on grouped aggregate

I'm trying to find a simple MySQL statement for the following two problems:
I have 4 tables: Employees, Customers, Orders, Products (Each entry in Orders contains a date, a reference one product, a quantity and a reference to a customer, and a reference to an Employee).
Now I'm trying to get all customers where the volume of sale (quantity * product.price) is bigger in 1996 than in 1995.
And: I want to list all Employees whose volume of sale is below the average volume of sale.
Any help would really be appreciated. I've managed to get the information using a php script but I think this can be done with some clever SQL Statements.
Can anybody help me?
Employee Table: ID# Name
Products Table: ID# NAME# PRICE
Orders Table: ODERID# CUSTOMERID # DATE # EMPLOYEE# PRODUCTID# QUANTITY
For the first part (assuming quite a bit about the schema):
SELECT Customers.ID
FROM Customers
LEFT JOIN orders AS o1 ON o1.CustomerID=Customers.ID AND YEAR(o1.DATE) = 1995
LEFT JOIN products AS p1 ON p1.id = o1.productID
LEFT JOIN orders AS o2 ON o2.CustomerID=Customers.ID AND YEAR(o2.DATE) = 1996
LEFT JOIN products AS p2 ON p2.id = o2.productID
HAVING SUM(o1.quantity* p1.price) < SUM(o2.quantity*p2.price)
I don't know the database type you're using, so I'll use sqlserver. The 'Year' function is available on most databases, so you should be able to rewrite the query for your db in question.
I think this is the query which returns all customerid's + ordertotal for the customers which have a higher total in 1996 than in 1995, but I haven't tested it. Crucial is the HAVING clause, where you can specify a WHERE kind of clause based on the grouped result.
SELECT o.CustomerId, SUM(o.Quantity * p.Price) AS Total
FROM Orders o INNER JOIN Products p
ON o.ProductId = p.ProductId
WHERE YEAR(o.Date) == 1996
GROUP BY o.CustomerId
HAVING SUM(o.Quantity * p.Price) >
(
SELECT SUM(o.Quantity * p2.Price) AS Total
FROM Orders o2 INNER JOIN Products p2
ON o2.ProductId = p.ProductId
WHERE YEAR(o.Date) == 1995
AND o2.CustomerId = o.CustomerId
GROUP BY o.CustomerId
)
something like that:
select * from customers c
where (select sum (o.quantity * p.price) from orders o, product p where o.productID = p.productID and o.dateyear = 1996 and o.customerID = c.customerID) < (select sum (o.quantity * p.price) from orders o, product p where o.productID = p.productID and o.dateyear = 1995 and o.customerID = c.customerID)
select * from employees e where (select avg (o.quantity * p.price) from orders o, product p where o.productID = p.productID and o.empID = e.EmpID) < (select avg (o.quantity * p.price) from orders o, product p where o.productID = p.productID)