Subquery "Finding customer who has the most purchases" - sql

I'm having trouble creating a query with a sub query to find the one customer in my DB who has made the most purchases. I need to list his/her full name, product name, price and quantity. Here is what I have so far
select first_name ||' '|| last_name "FullName", pt.name p.price, sum(ps.quantity)
from customers c
join purchases ps on c.customer_id = ps.customer_id
join products p on p.product_id = ps.product_id
join product_types pt on p.product_type_id = pt.product_type_id;
I need to use these three tables
Customers Table
Customer_ID
First_Name
Last_Name
DOB
Phone
Purchases Table
Product_ID
Customer_ID
Quantity
Products Table
Product_ID
Product_Type_ID
Name
Description
Price
Product Types Table
Product_Type_ID
Name
I am confused as where I should place the sub query (in the select row, from, having or where clause), if the arithmetic function should be placed in the select outer query or sub query. I know there are Nested subqueries, Correlated subqueries, Multiple-column subqueries, Multiple-row subqueries, Single-row subqueries. By the way, I am trying to do this in Oracle.
Here is an image with my result, except I removed sum from quantity column. Also, updated link.
(http://i1294.photobucket.com/albums/b618/uRsh3RRaYm0nD/Capture100_zps1f951b07.jpg)
I'm sorry, I forgot to include a fourth table, as you can see there are two name columns, in products table and product type table. The difference is that in products table "Name" is the specific name of the product, as shown in my link. The product type "Name" column is the more general name of the product, such as books, dvds, cds, etc. I need to include the product type "Name column in my query not product's name column. Therefore, the end result should look something like this
FullName ProductTypeName Price Quantity
John Brown Book Sumof4books 4
John Brown DVD Sumof2DVDs 2
John Brown Magazine Sumof1Mag 1

Here's one way to do it. It uses an analytic function to order customers by the total quantity of purchases: row_number() over (order by sum(quantity) desc). If there's more than one person with the same quantity, this will pick out only one.
It then takes this customer id and joins the rest of the tables in the obvious way to get the break down by product type.
Select
c.FullName,
pt.name,
Sum(p.price * ps.quantity) price,
sum(ps.quantity) quantity
From (
Select
c.Customer_ID,
c.first_name ||' '|| c.last_name FullName,
row_number() over (order by Sum(Quantity) desc) r
From
Purchases ps
Inner Join
Customers c
On ps.Customer_ID = c.Customer_ID
Group By
c.Customer_ID,
c.first_name ||' '|| c.last_name
) c
Inner Join
Purchases ps
On c.Customer_ID = ps.Customer_ID
Inner Join
Products p
On ps.Product_ID = p.Product_ID
Inner Join
Product_Types pt
On p.Product_Type_ID = pt.Product_Type_ID
Where
c.r = 1
Group By
c.FullName,
pt.name
Example Fiddle
For the second problem (show the customer who has the highest quantity for each product type, together with what they've spent on that product type)
Select
c.FullName,
c.name,
c.price,
c.quantity
From (
Select
c.first_name ||' '|| c.last_name FullName,
pt.name,
sum(p.price * ps.quantity) price,
sum(ps.quantity) quantity,
row_number() over (partition by pt.name order by Sum(Quantity) desc) r
From
Purchases ps
Inner Join
Customers c
On ps.Customer_ID = c.Customer_ID
Inner Join
Products p
On ps.Product_ID = p.Product_ID
Inner Join
Product_Types pt
On p.Product_Type_ID = pt.Product_Type_ID
Group By
c.first_name ||' '|| c.last_name,
pt.name
) c
Where
c.r = 1
Example Fiddle

Here is the general idea. You can adapt it for your database tables.
select fred, barney, maxwilma
from bedrock join
(select max(wilma) maxwilma
from bedrock
group by fred ) flinstone on wilma = maxwilma

SELECT CLIENT.CLIENTNO, CLIENT.CNAME, SUM(PURCHASE.AMOUNT) AS AMOUNT
FROM CLIENT
INNER JOIN PURCHASE
ON CLIENT.CLIENTNO = PURCHASE.CLIENTNO
WHERE CLIENT.CLIENTNO IN (
SELECT CLIENTNO
FROM (
SELECT PURCHASE.CLIENTNO, SUM(PURCHASE.AMOUNT) AS AMOUNT
FROM PURCHASE
GROUP BY PURCHASE.CLIENTNO
ORDER BY AMOUNT DESC
)
WHERE ROWNUM = 1)
GROUP BY CLIENT.CLIENTNO, CLIENT.CNAME;

select first_name ||' '|| last_name "FullName",name,quantity from customers,purchases,products where products.product_id = purchases.product_id and purchases.customer_id = customers.customer_id order by quantity;
This is the query you want

Related

Count between multiple tables

I have these 3 tables with the following structures:
Products
product_id | category_id | product_name
Customers
customer_id | customer_name
Orders
order_id | customer_id | product_id
I need to write a SQL query that will print the top customers regarding the number of categories they have bought products from (the best customer is the one that has bought from the most categories).
Can anyone show me how to do that?
I tried like this, but I get the following error "not a GROUP BY expression":
select
(select customer_name
from customers
where customers.customer_id = orders.customer_id) as name,
(select count(category_id)
from products
where products.product_id = orders.product_id)
from
orders
group by
customer_id
order by
count(customer_id) desc;
I managed to make the top regarding how many products the customers bought. The solution I used is:
select
(select customer_name from customers
where customers.customer_id = orders.customer_id) as name,
count(product_id) as "number of ordered products"
from
orders
group by
customer_id
order by
count(product_id) desc;
Nested queries for this? Sheesh...
Just join them already.
And then order by the unique categories
SELECT c.customer_name
, COUNT(DISTINCT p.category_id) AS TotalOrderedCategories
FROM Orders o
LEFT JOIN Customers c ON c.customer_id = o.customer_id
LEFT JOIN Products p ON p.product_id = o.product_id
GROUP BY c.customer_name
ORDER BY COUNT(DISTINCT p.category_id) DESC
Then depending on your RDBMS flavor, add a TOP or a LIMIT.

What customers who bought the biggest amount of a product in one order?

I have 2 tables:
CUSTOMERS (ID, FIRSTNAME, LASTNAME, ADDRESS);
ORDERS (ID, PRODUCT_NAME, PRODUCT_PRICE, DATE_ORDER DATE, ID_CUSTOMER, AMOUNT);
Get the first and last names of the customers who bought the biggest amount of a product in one order.
The orders without customer should not be considered. Please sort by FIRSTNAME and LASTNAME
SELECT firstname, lastname
FROM customers
LEFT JOIN orders ON customers.id = orders.id_customer
GROUP BY customers.firstname
HAVING (SELECT MAX(orders.amount) FROM orders);
What I'm doing wrong? thx
If you want one customer with the largest value of amount, then simplest method is order by and then fetching only one row:
select c.firstname, c.lastname
from customers c join
orders o
on c.id = o.id_customer
order by o.amount desc
fetch first 1 row only;
If you want the biggest amount per product, then window functions are the right approach
select pc.*
from (select o.product, c.firstname, c.lastname, o.amount,
rank() over (partition by o.product order by o.amount desc) as seqnum
from customers c join
orders o
on c.id = o.id_customer
) pc
where seqnum = 1;

I need my query to return where it will return where an item was purchased by more than 3 customers. However, my query keeps returning Null

I have confirmed that there is instances where an item was purchased more than 3 times by 3 SEPERATE customers. However, my code keeps returning Null. I have tried several different variations of the code below, but I get either Null or an error message stating "Ambiguous column name 'Customer_ID'."
I have also tried aliasing it, but with no luck. Where am I going wrong?
SELECT First_Name, Last_Name, Country, Address, State, Zip, Product_Name
FROM Orders
JOIN Customer ON Orders.Customer_ID = Customer.Customer_ID
JOIN Amazon_Inventory ON Orders.Inventory_ID = Amazon_Inventory.Inventory_ID
JOIN Shipment ON Amazon_Inventory.Shipment_ID = Shipment.Shipment_ID
JOIN Product ON Shipment.Product_ID = Product.Product_ID
JOIN Product_Listing ON Product.Listing_ID = Product_Listing.Listing_ID
WHERE ORDER_ID IN
(SELECT Customer_ID, Inventory_ID
FROM Orders
GROUP BY Customer_ID, Inventory_ID
HAVING COUNT (Order_ID) >3 AND COUNT (INVENTORY_ID) >3);
The error is on IN subquery,you can't use IN to compare multiple fields.
If I understand correct you might expect this.
You could provide some sample data and expect result.I will edit my answer.
SELECT First_Name, Last_Name, Country, Address, State, Zip, Product_Name
FROM Orders
JOIN Customer ON Orders.Customer_ID = Customer.Customer_ID
JOIN Amazon_Inventory ON Orders.Inventory_ID = Amazon_Inventory.Inventory_ID
JOIN Shipment ON Amazon_Inventory.Shipment_ID = Shipment.Shipment_ID
JOIN Product ON Shipment.Product_ID = Product.Product_ID
JOIN Product_Listing ON Product.Listing_ID = Product_Listing.Listing_ID
WHERE Customer.Customer_ID IN
(SELECT Customer_ID
FROM Orders
GROUP BY Customer_ID
HAVING COUNT (1) >3 );
You are almost there. I would suggest you to use alias to your tables (that would solve the ambiguous errors), and also, you are using an IN referring to a SELECT with more than 1 column being selected (A tip: I would use an EXISTS instead of a IN whenever possible - but that's not the case - or at least it doesn't seems like the case with the info provided).
So, using aliases I would end up with something like this:
SELECT
*
/* Place appropriate alias in each field selected => First_Name, Last_Name, Country, Address, State, Zip, Product_Name */
FROM Orders AS OR
JOIN Customer AS CU ON Orders.Customer_ID = Customer.Customer_ID
JOIN Amazon_Inventory AS AI ON Orders.Inventory_ID = Amazon_Inventory.Inventory_ID
JOIN Shipment AS SH ON Amazon_Inventory.Shipment_ID = Shipment.Shipment_ID
JOIN Product AS PR ON Shipment.Product_ID = Product.Product_ID
JOIN Product_Listing AS PL ON Product.Listing_ID = Product_Listing.Listing_ID
WHERE
EXISTS
(SELECT 1 FROM Orders OREX
WHERE OREX.Inventory_ID = OR.Inventory_ID
GROUP BY OREX.Customer_ID, OREX.Inventory_ID
HAVING COUNT(1)>3);
I'm thinking something like this:
SELECT
c.Customer_ID, i.Inventory_ID,
c.First_Name, c.Last_Name, c.Country, c.Address, c.State, c.Zip,
p.Product_Name
FROM Orders o
JOIN Customer c ON o.Customer_ID = c.Customer_ID
JOIN Amazon_Inventory i ON o.Inventory_ID = i.Inventory_ID
JOIN Shipment s ON i.Shipment_ID = s.Shipment_ID
JOIN Product p ON s.Product_ID = p.Product_ID
JOIN Product_Listing l ON p.Listing_ID = l.Listing_ID
)
WHERE Inventory_ID IN (
SELECT Inventory_ID
FROM Orders JOIN Customer ON Orders.Customer_ID = Customer.Customer_ID
GROUP BY Inventory_ID
HAVING COUNT (DISTINCT Customer_ID) > 3
);

Display Product Name and City where that product sold in largest quantity

I'm trying to get a query to display the product name and city where the product had the highest quantity sold. Here is the code I'm working with:
SELECT DISTINCT
(s.city),
MAX(t.quantity),
p.Name
FROM [DS715-Cameron-Erwin].dbo.Tb_Transactions AS t,
[DS715-Cameron-Erwin].dbo.Tb_Product AS p,
[DS715-Cameron-Erwin].dbo.Tb_Supplier AS s
WHERE p.prod_id = t.prod_id
AND s.Supp_ID = t.Supp_ID
GROUP BY t.Prod_ID,
p.name,
s.city
ORDER BY p.name, s.city
This is giving me the highest quantity sold for each product in each city.
Sample Data
From the screenshot there are multiple records for each product (Airplane, Auto, Boat...). I'm trying to get a single record for each product where ever the highest quantity was purchased.
So, the top record would only show for Airplane because the most orders were from there.
You want to use the ROW_NUMBER() OVER functionality to order by the quantity and then select the one with the biggest quantity over each product.
SELECT
city,
quantity,
name
FROM
(
SELECT S.city,
T.quantity,
P.name,
ROW_NUMBER() OVER
( PARTITION BY
P.name
ORDER BY t.Quantity DESC
) as RowNum
FROM
Tb_Transactions T
INNER JOIN
Tb_Product P
ON
P.prod_id = T.prod_id
INNER JOIN
Tb_Supplier S
ON
S.supp_id = T.supp_id
) a
WHERE
RowNum = 1
http://sqlfiddle.com/#!6/628458/5
For this, I would use a CTE (also I would use the explicit INNER JOIN syntax):
;With CTE
As
(
Select
s.city
, t.quantity
, p.Name
, Row_Number Over (Partition By P.Name, s.city Order By t.Quantity Desc) as RN
From [DS715-Cameron-Erwin].dbo.Tb_Transactions as t
Inner Join [DS715-Cameron-Erwin].dbo.Tb_Product as p
On p.prod_id = t.prod_id
Inner Join [DS715-Cameron-Erwin].dbo.Tb_Supplier as s
On s.Supp_ID = t.Supp_ID
)
Select
city
, quantity
, Name
From CTE
Where RN = 1

question about SQL query

I'm working on a small project involving oracle database,
and I have the following tables:
CUSTOMER ( Cid, CName, City, Discount )
PRODUCT ( Pid, PName, City, Quantity, Price )
ORDERS ( OrderNo, Month, Cid, Aid, Pid, OrderedQuantity, Cost )
How can retrieve the names of all customers who ordered all the products?
For example if customer x ordered product1, product2 and product3 (which are all the products the company offers) he will be selected. And if customer y only ordered product 1 and 2 but not 3 he will not be selected.
How can I achieve this?
You want "relational division".
select *
from customer c
where not exists( -- There are no product
select 'x'
from product p
where not exists( -- the customer did not buy
select 'x'
from orders o
where o.cid = c.cid
and o.pid = p.id));
or
select c.cid
,c.name
from customer c
join orders o using(cid)
group
by c.id
,c.name
having count(distinct o.pid) = (select count(*) from product);
Here is a great article by Joe Celko that shows several ways of implementing relational division (and variations): Divided We Stand: The SQL of Relational Division
You can use group by and use a having clause to demand that the customer has ordered all products there are:
select c.CName
from Customers c
join Orders o
on o.Cid = c.Cid
group by
c.Cid
, c.CName
having count(distinct o.Pid) = (select count(*) from products)
IMHO more readable than the "relational divison" approach, but less efficient.