Remove duplication in SQL query - mysql

SQL Practice from W3Schools
SELECT C.country,
Round(Sum(P.price * OD.quantity), 2) AS Revenue
FROM [orders] AS O
INNER JOIN [orderdetails] AS OD
ON O.orderid = OD.orderid
INNER JOIN [products] AS P
ON OD.productid = P.productid
INNER JOIN [customers] AS C
ON O.customerid = C.customerid
WHERE C.country = (SELECT C.country
FROM [orders] AS O
INNER JOIN [orderdetails] AS OD
ON O.orderid = OD.orderid
INNER JOIN [products] AS P
ON OD.productid = P.productid
INNER JOIN [customers] AS C
ON O.customerid = C.customerid
GROUP BY C.customerid
ORDER BY Round(Sum(P.price * OD.quantity), 2) DESC
LIMIT 1)
My joins are duplicated from main to sub-query.
Is there anyway to reduce code replication in this query?

Related

MySql : how to skip joins in subquery

I wrote a program about finding products sold in both France and Germany. On the other hand, I'm not happy with my subqueries. I have a feeling that all the joins could be skipped and done much faster, yet I could not find an answer to this problem.
Here is the code:
select productname
from products p
join [Order Details] od on p.productid = od.ProductID
join orders o on od.OrderID = o.OrderID
join customers c on o.CustomerID = c.CustomerID
where p.ProductID in (select p.productid
from products p
join [Order Details] od on p.productid = od.ProductID
join orders o on od.OrderID = o.OrderID
join customers c on o.CustomerID = c.CustomerID
where c.Country LIKE 'France')
and p.ProductID in (select p.productid
from products p
join [Order Details] od on p.productid = od.ProductID
join orders o on od.OrderID = o.OrderID
join customers c on o.CustomerID = c.CustomerID
where c.Country LIKE 'Germany')
group by productname
So what I'm asking for is to maybe simplify this as much as possible but in basic ways, or show the way to skip that joins etc.
https://i.stack.imgur.com/UWlzn.png
select productname
from products p
join [Order Details] od on p.productid=od.ProductID
join orders o on od.OrderID=o.OrderID
join customers c on o.CustomerID=c.CustomerID
where c.Country in ('France', 'Germany')
group by productname
having COUNT(DISTINCT c.Country) = 2;

Retrieve customer who bought more than 13 different products who never purchased same product

I tried this. But I feel this gives people who ordered same product
SELECT DISTINCT Count(od.orderqty) OrderQty,
c.customerid,
od.productid
FROM sales.customer c
INNER JOIN sales.salesorderheader oh
ON c.customerid = oh.customerid
INNER JOIN sales.salesorderdetail od
ON oh.salesorderid = od.salesorderid
GROUP BY od.productid,
c.customerid
HAVING Count(od.productid) > 10
ORDER BY c.customerid
Not sure what flavor of SQL you're using but try this:
select t.CustomerID
from (
select c.CustomerID
, count(distinct od.ProductID) as DistinctCount
, count(od.ProductID) as Count
from Sales.Customer c
join Sales.SalesOrderHeader oh
on c.customerid = oh.customerid
join Sales.SalesOrderDetail od
on oh.SalesOrderID = od.SalesOrderID
group
by c.CustomerID
) as t
where t.DistinctCount = t.Count
and t.DistinctCount > 13
order
by t.CustomerID

Most efficient way to to find most expensive recent order

I am trying to find most recent and expensive purchase by customer. I have four tables customers,orders,order details and product code. All accounts are unique by emailaddress.
Error The multi-part identifier "cx.EmailAddress" could not be bound
SELECT c.EmailAddress,
o.BillingFirstName AS WC_FirstName,
o2.LatestOrder
FROM Customers c
JOIN orders o ON o.customerid = c.customerid
JOIN( SELECT
cx.EmailAddress,
MAX(o.OrderID) AS LatestOrder
FROM Customers AS cx
JOIN Orders o ON o.customerid = cx.customerid
WHERE o.OrderStatus <> 'CANCELLED' AND o.OrderDate > '09/01/2017 00:00'
GROUP BY EmailAddress) AS o2 on o2.EmailAddress = cx.EmailAddress
GROUP BY c.EmailAddress,o.BillingFirstName
UPDATE Tried it in another way too but still gets error : The multi-part identifier "c.EmailAddress" could not be bound.
SELECT
c.EmailAddress,
c.CustomerID,
o.OrderDate,
p.Google_Gender,
p.Google_Age_Group ,
p.productprice AS Product_Price
FROM
Customers c
JOIN
orders o ON o.CustomerID = c.CustomerID
JOIN
(SELECT
c.EmailAddress,
MAX(o.OrderID) AS LatestOrder
FROM
Orders o, Customers c
WHERE
o.OrderStatus <> 'CANCELLED' AND
o.CustomerID = c.CustomerID
GROUP BY
c.EmailAddress) AS o2 ON o2.EmailAddress = c.EmailAddress
JOIN
(SELECT
od.*,
c.EmailAddress,
row_number() over (partition BY c.EmailAddress
ORDER BY od.ProductPrice DESC, o.OrderDate DESC) AS seqnum
FROM
OrderDetails od
JOIN
Orders o ON od.OrderID = o.OrderID
JOIN
Customers c ON o.CustomerID = c.CustomerID
JOIN
(SELECT
c.EmailAddress,
MAX(o.OrderID) AS LatestOrder
FROM
Orders o , Customers c
WHERE
o.OrderStatus <> 'CANCELLED' AND
o.CustomerID = c.CustomerID
GROUP BY
c.EmailAddress) AS o2 ON o2.EmailAddress = c.EmailAddress
WHERE
o.OrderID = o2.LatestOrder) od ON od.CustomerID = c.CustomerID
AND seqnum = 1
JOIN
Products_Joined p ON od.ProductCode = p.ProductCode
FULL JOIN
(SELECT p.ProductCode, p.ProductName FROM Products_Joined AS p) AS p2 ON p2.ProductCode = p.Google_Age_Group
WHERE
AND o.PaymentAmount <> 0
AND o.OrderID = o2.LatestOrder
GROUP BY
c.EmailAddress, o.OrderDate, c.CustomerID, p.productprice,
p.Google_Age_Group, p.Google_Gender, p.Google_Pattern,
p.Google_Size, od.ProductName, p.ProductName, p2.productname,
p.productcode
ORDER BY
o.OrderDate DESC, MAX(od.ProductPrice) DESC;
You have that error because cx is an internal alias inside the o2 query. So you should probably change that JOIN condition for this one:
AS o2 on o2.EmailAddress = c.EmailAddress
This way you join your o2 email address with the one from Customers directly.
Just for the sake of performance, it would be better if you join customers by CustomerID, like you do in your first join, instead of by email.
Finally, you are doing the MAX of OrderID, which will most likely return the most recent order. Having that in mind, you should probably change your query to something like this (I'm guessing Amount is the name of the field):
SELECT c.EmailAddress,
o.BillingFirstName AS WC_FirstName,
MostExpensiveOrderId = o.OrderId,
MostExpensiveOrderAmount = o.Amount
FROM Customers c
INNER JOIN (
SELECT
CustomerID,
MAX(Amount)
FROM Orders
WHERE OrderStatus <> 'CANCELLED' AND OrderDate > '09/01/2017 00:00'
GROUP BY CustomerID
) AS omax ON omax.CustomerID = C.CustomerID
INNER JOIN Orders o ON o.CustomerID = c.CustomerID AND o.Amount = omax.Amount
ORDER BY c.EmailAddress, o.BillingFirstName
Note that you don't need the Customers table in the inner query. Also note that this way you may get more than one row per customer if the highest value is shared with more than one order.

SSIS: Adding a int variable to SQL Query via Expression

I have added a variable in SSIS and I am trying to add tha variable in my expression query. But it says Expression cannot be evaluated Below is my expression query. Please help if you know where I am making mistakes.
SQL Query
SELECT c.CustomerName, o.OrderID from Customers c
INNER JOIN Orders o ON c.CustomerID=o.CustomerID
Inner Join OrderDetails od ON od.OrderId = o.OrderID
Inner Join Products p on p.ProductID = od.ProductID
where ISNULL(c.IsResult,0) = 0 and o.CompanyID = #CompanyID
Expression Format
"SELECT c.CustomerName, o.OrderID from Customers c
INNER JOIN Orders o ON c.CustomerID=o.CustomerID
Inner Join OrderDetails od ON od.OrderId = o.OrderID
Inner Join Products p on p.ProductID = od.ProductID
where ISNULL(c.IsResult,0) = 0 and o.CompanyID ="+ #[User::intCompanyID]
Let me know if I am doing it right.
I found that casting the numeric values to String or Unicode works. so maybe try
ISNULL(c.IsResult,0) = 0 and o.CompanyID ="+ (DT_WSTR,20) #[User::intCompanyID]
Have you tried the following
"SELECT c.CustomerName, o.OrderID from Customers c
INNER JOIN Orders o ON c.CustomerID=o.CustomerID
Inner Join OrderDetails od ON od.OrderId = o.OrderID
Inner Join Products p on p.ProductID = od.ProductID
where ISNULL(c.IsResult,0) = 0 and o.CompanyID = ?"
NOTE: The image is from a different example
Click on the "Parameters" button and you can then selected your parameter from there.
Hope this helps.

Group by id having max(date_field)

To build a report, I must select some information on the last transaction status of all my customers. Until now, this is what I got:
SELECT c.firstname, c.lastname, d.product_name, o.payment, s.name, h.date_add
FROM ps_orders o
INNER JOIN ps_order_detail d ON d.id_order = o.id_order
INNER JOIN ps_customer c ON c.id_customer = o.id_customer
INNER JOIN ps_order_history h ON o.id_order = h.id_order
INNER JOIN ps_order_state_lang s ON s.id_order_state = h.id_order_state
WHERE s.id_lang =6
GROUP BY c.id_customer
HAVING MAX(h.date_add)
For each customer, this query is selecting the first date (the field h.date_add) when I need of the last one. It seems the MySQL is ignoring the HAVING.
I tried to make a sub-select, but it doesn't work too.
Thanks any answer.
Here, you need to have a subquery which gets the latest date_add for every id_order on table ps_order_history. The result of the subquery is then joined back on the original table ps_order_history provided that it macth on two columns: date_add and id_order.
SELECT c.firstname,
c.lastname,
d.product_name,
o.payment,
s.name,
h.date_add
FROM ps_orders o
INNER JOIN ps_order_detail d ON d.id_order = o.id_order
INNER JOIN ps_customer c ON c.id_customer = o.id_customer
INNER JOIN ps_order_history h ON o.id_order = h.id_order
INNER JOIN
(
SELECT id_order, MAX(date_add) max_date
FROM ps_order_history
GROUP BY id_order
) x ON h.id_order = x.id_order AND
h.date_add = x.max_date
INNER JOIN ps_order_state_lang s ON s.id_order_state = h.id_order_state
WHERE s.id_lang =6
GROUP BY c.id_customer
To get the last date, you need to join it in:
SELECT c.firstname, c.lastname, d.product_name, o.payment, s.name, h.date_add
FROM ps_orders o
INNER JOIN ps_order_detail d ON d.id_order = o.id_order
INNER JOIN ps_customer c ON c.id_customer = o.id_customer
INNER JOIN ps_order_history h ON o.id_order = h.id_order
INNER JOIN ps_order_state_lang s ON s.id_order_state = h.id_order_state
inner join (select o.id_customer, max(oh.date_add) as maxdate from ps_order_history h join ps_order o on h.id_order = o.id_order group by o.id_customer) omax on omax.id_customer = o.id_customer and o.date_add = omax.maxdate
WHERE s.id_lang =6
GROUP BY c.id_customer
Your having clause calculates the maximum date and then succeeds when it is not equal to 0 (which would be most of the time).