Missing Right Parenthesis issue - sql

This is the Query I have written
Select C.CUST_NAME,P.PROD_DESCRIP from Customer C
JOIN (Ord O,OrderItem OT, Product P) ON (C.CUST_ID = O.CUST_ID AND O.ORD_ID = OT.ORD_ID AND OT.PROD_ID = P.PROD_ID) GROUP BY C.CUST_NAME ORDER BY OT.ORDITEM_QTY DESC
But the issue is it giving me Right Parenthesis Missing issue

Although that join syntax is allowed in some databases, it is really much clearer to split out the joins:
Select C.CUST_NAME, P.PROD_DESCRIP
from Customer C JOIN
Ord O
on C.CUST_ID = O.CUST_ID JOIN
OrderItem OT
on O.ORD_ID = OT.ORD_ID JOIN
Product P
ON OT.PROD_ID = P.PROD_ID
GROUP BY C.CUST_NAME
ORDER BY OT.ORDITEM_QTY DESC;
By the way, this probably isn't doing what you think it does. It is returning a customer name along with an arbitrary prod_descrip. It is then ordering this result by an arbitrary quantity -- perhaps from the same or a different row.
If you want to get the customer name along with the product with the maximum quantity for that customer, you can do this:
Select C.CUST_NAME,
substring_index(group_concat(P.PROD_DESCRIP order by OT.ORDITEM_QTY desc), ',', 1) as PROD_DESCRIP
from Customer C JOIN
Ord O
on C.CUST_ID = O.CUST_ID JOIN
OrderItem OT
on O.ORD_ID = OT.ORD_ID JOIN
Product P
ON OT.PROD_ID = P.PROD_ID
GROUP BY C.CUST_NAME;
Note: If PROD_DESCRIP could have a comma then you will want to use a different separator character.
EDIT:
The above is the MySQL solution. In Oracle, you would do:
select CUST_NAME, PROD_DESCRIP
from (Select C.CUST_NAME, P.PROD_DESCRIP,
row_number() over (partition by C.CUST_NAME order by OT.ORDITEM_QTY desc) as seqnum
from Customer C JOIN
Ord O
on C.CUST_ID = O.CUST_ID JOIN
OrderItem OT
on O.ORD_ID = OT.ORD_ID JOIN
Product P
ON OT.PROD_ID = P.PROD_ID
) t
where seqnum = 1;
This is actually the preferred standard SQL solution. It will work in most databases (SQL Server, Oracle, Postgres, DB2, and Teradata).

SELECT C.CUST_NAME, P.PROD_DESCRIP
FROM Customer C
INNER JOIN Ord O ON C.CUST_ID = O.CUST_ID
INNER JOIN OrderItem OT ON O.ORD_ID = OT.ORD_ID
INNER JOIN Product P ON OT.PROD_ID = P.PROD_ID
GROUP BY C.CUST_NAME
ORDER BY OT.ORDITEM_QTY DESC

SELECT C.CUST_NAME,P.PROD_DESCRIP
FROM Customer C
JOIN Ord O
ON C.CUST_ID = O.CUST_ID
JOIN OrderItem OT
ON O.ORD_ID = OT.ORD_ID
JOIN Product P
ON OT.PROD_ID = P.PROD_ID
GROUP BY C.CUST_NAME
ORDER BY OT.ORDITEM_QTY DESC

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.

How to select distinct items without having to use in group by clause?

I am trying to find sum of some columns using SQL like this:
select distinct c.customer,
c.customer_id,
sum(d.delay) as delay,
sum(d.delayed_amount) as delay_amt,
pd.product
from product pd
inner join mfg_company mfg on pd.product_id=mfg.product_id
inner join store s on mfg.store_id = s.store_id
inner join customer c on s.customer = c.customer_id
join delay_detail d on pd.product_id = d.material
where d.product_mfg_id = 466
group by c.customer,customer_id
order by c.customer,c.customer_id
The problem is mfg_company has duplicate product_id's(multiple mappings) ,So when I am trying to find the sum it's including those duplicates too.
Using product_id in group by clause doesn't help the result I want to see.So how to join only on distinct product_id's?
You can try below query if this helps -
select distinct c.customer
,c.customer_id
,sum(d.delay) as delay
,sum(d.delayed_amount) as delay_amt
,pd.product
from product pd
inner join (select distinct product_id
,store_id
from mfg_company) mfg on pd.product_id=mfg.product_id
inner join store s on mfg.store_id = s.store_id
inner join customer c on s.customer = c.customer_id
join delay_detail d on pd.product_id = d.material
where d.product_mfg_id = 466
group by c.customer,customer_id
order by c.customer,c.customer_id
I think the solution to your problem is to pre-aggregate the delays. It is entirely unclear if you want the product in the result set. Assuming you do not:
select c.customer, c.customer_id,
sum(d.delay) as delay, sum(d.delay_amt) as delay_amt
from product pd join
mfg_company mfg
on pd.product_id = mfg.product_id join
store s
on mfg.store_id = s.store_id
customer c
on s.customer = c.customer_id join
(select d.material, sum(d.delay) as delay, sum(d.delayed_amount) as delay_amt
from delay_detail d
group by d.material
) d
on pd.product_id = d.material
where d.product_mfg_id = 466
group by c.customer, customer_id
order by c.customer, c.customer_id;
Note that using select distinct with group by is almost never needed.

Postgresql returning the most popular genre of product per customer

I have a query that is supposed to return a list of customers with the most popular product type for each customer. I have have a query that sums up each product purchased in all given product types and lists them in descending order per customer
SELECT c.customer_name as cname, ptr.product_type as pop_gen, sum(od.quantity) as li
FROM product_type_ref as ptr
INNER JOIN product as p
on p.product_type_ref_id = ptr.product_type_ref_id
INNER JOIN order_detail as od
on od.product_id = p.product_id
INNER JOIN order as o
on o.order_id = od.order_id
INNER JOIN customer as c
on c.customer_id = o.customer_id
GROUP BY cname, pop_gen
ORDER BY cname, li DESC
which returns this data:
'andy','Drama',1000
'andy','Action',250
'andy','Comedy',100
'bebe','Drama',250
'bebe','Action',100
'bebe','Comedy',25
'buster','Action',825
'buster','Comedy',768
'buster','Drama',721
'buster','Romance',100
'ron','Romance',50
'ron','Comedy',10
how could i return this:
andy, Drama
bebe, Drama
buster, Action
ron, Romance
In Postgres, you can just use distinct on:
SELECT DISTINCT ON (c.customer_name) c.customer_name as cname,
ptr.product_type as pop_gen, sum(od.quantity) as li
FROM product_type_ref as ptr
INNER JOIN product as p
on p.product_type_ref_id = ptr.product_type_ref_id
INNER JOIN order_detail as od
on od.product_id = p.product_id
INNER JOIN order as o
on o.order_id = od.order_id
INNER JOIN customer as c
on c.customer_id = o.customer_id
GROUP BY cname, pop_gen
ORDER BY cname, li DESC;
Classic greatest-n-per-group. One possible solution is to use ROW_NUMBER():
WITH
CTE
AS
(
SELECT
c.customer_name as cname, ptr.product_type as pop_gen, sum(od.quantity) as li
,ROW_NUMBER() OVER(PARTITION BY c.customer_name ORDER BY sum(od.quantity) DESC) AS rn
FROM
product_type_ref as ptr
INNER JOIN product as p on p.product_type_ref_id = ptr.product_type_ref_id
INNER JOIN order_detail as od on od.product_id = p.product_id
INNER JOIN order as o on o.order_id = od.order_id
INNER JOIN customer as c on c.customer_id = o.customer_id
GROUP BY
cname, pop_gen
)
SELECT
cname, pop_gen, li
FROM CTE
WHERE rn = 1
ORDER BY cname;
Add ROW_NUMBER()
SELECT *
FROM (
SELECT c.customer_name as cname,
ptr.product_type as pop_gen,
sum(od.quantity) as li,
ROW_NUMBER() OVER (PARTITION BY c.customer_name
ORDER BY sum(od.quantity) DESC) as rn
......
) as T
WHERE T.rn = 1

Left join without using left join with 3 tables

I have to get all data from 3 tables with left join. I did that with and without using left join. But my teacher ask another solution that uses just inner join. I just couldn't find the solution please give some advice. Here are the tables:
And the results should look like this:
With left join solution:
SELECT O.*,C.CUST_CODE,C.CUST_NAME,P.PART_CODE,P.PART_NAME
FROM ORDERS O
LEFT OUTER JOIN PART P ON P.PART_ID = O.PART_ID
LEFT OUTER JOIN CUSTOMER C ON C.CUST_ID = O.CUST_ID
Without left join solution:
SELECT O.*,
(SELECT C.CUST_CODE FROM CUSTOMER C WHERE C.CUST_ID=O.CUST_ID) AS CUST_CODE,
(SELECT C.CUST_NAME FROM CUSTOMER C WHERE C.CUST_ID=O.CUST_ID) AS CUST_NAME,
(SELECT P.PART_CODE FROM PART P WHERE P.PART_ID = O.PART_ID ) AS PART_CODE,
(SELECT P.PART_NAME FROM PART P WHERE P.PART_ID = O.PART_ID ) AS PART_NAME
FROM ORDERS O
Here is one approach which uses only INNER JOIN and does not use LEFT JOIN anywhere. It begins with your original query using INNER JOIN instead of LEFT JOIN, and then adds to that result set the pieces which are missing from taking the inner product of all the tables.
SELECT t.* FROM
(
SELECT O.ORDER_ID, O.ORDER_DATE, C.CUST_CODE, C.CUST_NAME, P.PART_CODE, P.PART_NAME
FROM ORDERS O
INNER JOIN PART P
ON P.PART_ID = O.PART_ID
INNER JOIN CUSTOMER C
ON C.CUST_ID = O.CUST_ID
UNION
SELECT O.ORDER_ID, O.ORDER_DATE, NULL AS CUST_CODE, NULL AS CUST_NAME, P.PART_CODE,
P.PART_NAME
FROM ORDERS O
INNER JOIN PART P
ON P.PART_ID = O.PART_ID
WHERE O.CUST_ID NOT IN (SELECT C.CUST_ID FROM CUSTOMER C)
OR O.CUST_ID IS NULL
UNION
SELECT O.ORDER_ID, O.ORDER_DATE, C.CUST_CODE, C.CUST_NAME, NULL AS PART_CODE,
NULL AS PART_NAME
FROM ORDERS O
INNER JOIN CUSTOMER C
ON C.CUST_ID = O.CUST_ID
WHERE O.PART_ID NOT IN (SELECT P.PART_ID FROM PART P)
OR O.PART_ID IS NULL
UNION
SELECT O.ORDER_ID, O.ORDER_DATE, NULL AS CUST_CODE, NULL AS CUST_NAME,
NULL AS PART_CODE, NULL AS PART_NAME
FROM ORDERS O
WHERE (O.CUST_ID NOT IN (SELECT C.CUST_ID FROM CUSTOMER C) AND
O.PART_ID NOT IN (SELECT P.PART_ID FROM PART P)) OR
(O.CUST_ID IS NULL AND O.PART_ID IS NULL)
) t
ORDER BY t.ORDER_ID ASC
Follow the link below for a working demo:
SQLFiddle
This makes no sense in "real life", but I understand it's meaningfulness in a SQL course. You have a good teacher.
Try like this
SELECT * FROM
(
SELECT O.*,C.CUST_CODE,C.CUST_NAME,P.PART_CODE,P.PART_NAME
FROM ORDERS O
INNER JOIN PART P ON P.PART_ID = O.PART_ID
INNER JOIN CUSTOMER C ON C.CUST_ID = O.CUST_ID
UNION
SELECT O.*,NULL,NULL,P.PART_CODE,P.PART_NAME
FROM ORDERS O
INNER JOIN PART P ON P.PART_ID = O.PART_ID
WHERE O.CUST_ID IS NULL
UNION
SELECT O.*,C.CUST_CODE,C.CUST_NAME,NULL, NULL
FROM ORDERS O
INNER JOIN CUSTOMER C ON C.CUST_ID = O.CUST_ID
WHERE O.PART_ID IS NULL
UNION
SELECT O.*,NULL,NULL,NULL, NULL
FROM ORDERS O
WHERE O.PART_ID IS NULL AND O.CUST_ID IS NULL
) T
ORDER BY ORDER_ID

Is it possible to get one row by grouping more than one column

I have a query as below. DB from http://www.w3schools.com/sql/default.asp
SELECT count(distinct C.CustomerID),C.Country
FROM Customers C
inner join Orders O
on C.CustomerID = O.CustomerID
inner join OrderDetails D
on O.OrderID = D.OrderID
inner join Products P
on D.ProductID = P.ProductID
group by C.Country,P.CategoryID
order by C.Country
Here is the result from above.
But I want to get one row per country(as below pic) by counting CustomerIDs where any CustomerIDs are in the same country and have a same CategoryID as well. So I have to group by 2 columns. Is there any way to do it? Could you please kindly suggest me?
Thank you.
That's quite simple. Just remove P.CategoryID in your GROUP BY clause.
SELECT COUNT(DISTINCT C.CustomerID), C.Country
FROM Customers C
INNER JOIN Orders O
ON C.CustomerID = O.CustomerID
INNER JOIN OrderDetails D
ON O.OrderID = D.OrderID
INNER JOIN Products P
ON D.ProductID = P.ProductID
GROUP BY C.Country
ORDER BY C.Country;
Update
Following your comment, this should be correct approach then:
SELECT T.Country, SUM(T.Cnt)
FROM (
SELECT COUNT(DISTINCT C.CustomerID) AS Cnt, C.Country
FROM Customers C
INNER JOIN Orders O
ON C.CustomerID = O.CustomerID
INNER JOIN OrderDetails D
ON O.OrderID = D.OrderID
INNER JOIN Products P
ON D.ProductID = P.ProductID
GROUP BY C.Country, P.CategoryID
) AS T
GROUP BY T.Country
ORDER BY T.Country;