I stumbled upon a strange behavior of MySQL (v.8) when trying to run a nested subquery in the FROM clause. The (relevant part of the) schema of the sample database I am using is as follows:
The following two queries run identically on SQL Server:
SELECT SUM(tot) as total
FROM (
SELECT
SUM(OD.quantityOrdered * OD.priceEach) as tot,
C.customerNumber
FROM customers C
INNER JOIN orders O ON C.customerNumber = O.customerNumber
INNER JOIN orderdetails OD ON O.orderNumber = OD.orderNumber
GROUP BY O.orderNumber, C.customerNumber
) AS CO
GROUP BY CO.customerNumber;
and
SELECT
(
SELECT SUM(tot) as total
FROM
(
SELECT
(
SELECT SUM(OD.quantityOrdered * OD.priceEach)
FROM orderdetails OD
WHERE OD.orderNumber = O.orderNumber
) AS tot
FROM orders O
WHERE O.customerNumber = C.customerNumber
) AS ORD
) AS total
FROM customers AS C;
However, on MySQL, the first one runs fine, while the second one results in an error:
Error Code: 1054. Unknown column 'C.customerNumber' in 'where clause'
I will appreciate any clues about why this is happening. Please note that I am mostly interested not in workarounds or other ways to implement this query, but in understanding the reasons why the nested query fails.
C table alias in not in scope for the suquery
try refactoring the query using a join
eg
select c.customerNumber, t.my_tot
FROM customers AS C
INNER JOIN (
SELECT O.customerNumber, SUM(OD.quantityOrdered * OD.priceEach) my_tot
FROM orderdetails OD
INNER JOIN orders O ON OD.orderNumber = O.orderNumber
GROUP BY O.customerNumber
) t on t.customerNumber = c.customerNumber
or
select t.my_tot
FROM customers AS C
INNER JOIN (
SELECT O.customerNumber, SUM(OD.quantityOrdered * OD.priceEach) my_tot
FROM orderdetails OD
INNER JOIN orders O ON OD.orderNumber = O.orderNumber
GROUP BY O.customerNumber
) t on t.customerNumber = c.customerNumber
you can try like below
SELECT
(
select SUM(tot) as total from
(
SELECT
(
SELECT SUM(OD.quantityOrdered * OD.priceEach)
FROM orderdetails OD
WHERE OD.orderNumber = O.orderNumber
) AS tot,customerNumber
FROM orders O
) as ord
WHERE ord.customerNumber = C.customerNumber
) AS total
FROM customers AS C;
Your customers table not in scope of subquery where you used in where condition WHERE O.customerNumber = C.customerNumber so i made alias of that
and then later level i used same condition where customers table has scope
You have a correlated subquery in the second case. However, the correlation clause is two levels deep.
Many databases will still recognize c, even when nested multiple levels. However, MySQL (and Oracle and I think MS Access) is a database that limits correlation clauses to one level deep.
Related
Is there a way to answer this question without using joins?
Write a query that finds, for each customer X, another customer Y who has ordered at least one product in common with X. Find all such pairs of Customers (X, Y) and against each pair, the number of overlapping products. The query should thus have three columns. Order the results by the number of overlapping products.
The question uses the https://www.w3schools.com/sql/trysql.asp?filename=trysql_select_all
database.
Using joins, I can answer the question like this:
O2.CustomerID AS Cust2,
COUNT(*) AS OverlappingProd
FROM (SELECT O.CustomerID, OD.ProductID
FROM Orders AS O
JOIN OrderDetails AS OD
ON OD.orderid = o.orderid) AS O1
JOIN(SELECT O.CustomerID, OD.ProductID
FROM Orders AS O
JOIN OrderDetails AS OD
ON OD.orderid = o.orderid) AS O2
ON O2.ProductID = O1.ProductID
AND O2.CustomerID > O1.CustomerID
GROUP BY
O1.CustomerID,
O2.CustomerID
ORDER BY COUNT(*) DESC;
Is there a way to answer it not using the JOIN function? Thank you for your time and consideration.
I cannot think of a way of doing this without any joins. You can express this in SQL using cross join and exists, so the following comes close:
select c1.customerid, c2.customerid,
(select count(*)
from products p
where exists (select 1
from orderdetails od
where od.productid = p.productid and
exists (select 1
from orders o
where o.orderid = od.orderid and
o.customerid = c1.customerid
)
) and
exists (select 1
from orderdetails od
where od.productid = p.productid and
exists (select 1
from orders o
where o.orderid = od.orderid and
o.customerid = c2.customerid
)
)
) as num_products
from customers c1 cross join
customers c2
where c1.customerid < c2.customerid;
Although this syntax will work in many databases, the nested correlation clauses are not supported in MySQL. So, even accepting the CROSS JOIN, this does not work in MySQL.
The reason why a JOIN seems necessary is to get two independent customer ids in the result set.
I need to select the customer and product code and the date on which the order was made, but I'm having some trouble with the join orders.
My SQL select:
select c.customerNumber, p.productCode, o.orderDate as data_compra
from customers as c inner join orders as o
inner join products as p
where p.productCode =
any (
select p2.productCode from products as p2
inner join orders as o
inner join orderdetails as odt
where o.orderNumber = odt.orderNumber and
p2.productCode = odt.productCode
)
and o.orderNumber =
any (
select o2.orderNumber from orders as o2
inner join orderdetails as odt
where o.orderNumber = odt.orderNumber and
p.productCode = odt.productCode
)
Two simple joins should do what you want:
select
c.customerNumber,
d.productCode,
o.orderDate
from customer c
join orders o on o.customerNumber = c.customerNumber
join orderdetails d on d.orderNumber = o.orderNumber
In the code you're asking to inner join two tables but not specifying the relationship. You need to do so SQL can relate and match the rows in each table.
You do this with the ON keyword.
I suggest you watch this video and read this article before continuing
I'm trying to get the sum of each customer's orders separately, but I'm actually getting the sum of all the orders. What am I doing wrong here?
SELECT c.customerNumber, sum( r.quantityOrdered * r.priceEach ) AS sum
FROM customers c, orders o, orderdetails r
WHERE c.customerNumber = o.customerNumber
AND o.orderNumber = r.orderNUmber
GROUP BY c.customerNumber
HAVING COUNT( o.orderNumber ) <=3
First, you should use JOIN as it is the standardised way of writing the syntax. Makes it easier to read.
SELECT c.customerNumber, sum( r.quantityOrdered * r.priceEach ) AS sum
FROM customers c
LEFT JOIN orders o ON c.customerNumber = o.customerNumber
LEFT JOIN orderdetails r ON o.orderNumber = r.orderNUmber
GROUP BY c.customerNumber
HAVING COUNT( o.orderNumber ) <=3
When you use LEFT JOIN, this will give the results of the table customers, even if they dont have any recods in the orders and orderdetails.
Give it a try!
Your query looks ok to me other than the HAVING clause. I don't know what you are really trying to achieve with HAVING COUNT( o.orderNumber ) <=3. Do you really want customers with order of 3 or less? Have you tried using :
SELECT c.customerNumber, sum(r.quantityOrdered * r.priceEach) AS sum
FROM customers c
INNER JOIN orders o ON c.customerNumber = o.customerNumber
INNER JOIN orderdetails r ON o.orderNumber = r.orderNUmber
GROUP BY c.customerNumber
As the title says, i am trying to find the customer's who have made orders but have not made payments yet.
I have Three tables;
Customers, Payments, Orders
The sql i have so far gives me (nested query) all the customers without payments, the outer query then tries to join all the customers with orders and checks if those customers are not in my inner table?
SELECT customerWOpayments.customerNumber FROM
ClassicModels.Customers c
INNER JOIN ClassicModels.Orders o ON c.customerName = o.customerNumber
NOT IN
(SELECT distinct c.customerNumber
FROM ClassicModels.Customers c
LEFT OUTER JOIN ClassicModels.Payments p ON c.customerNumber = p.customerNumber
WHERE p.customerNumber IS NULL) customerWOpayments;
I am getting a mysql syntax error at line 8 but cannot figure out why?
This should return customers who have orders but no matching payment assuming all of the keys you joined on in your original example were correct (for example c.customerName = o.customerNumber seems suspicious).
SELECT c.customerNumber
FROM ClassicModels.Customers c
INNER JOIN ClassicModels.Orders o
ON c.customerNumber = o.customerNumber
LEFT OUTER JOIN ClassicModels.Payments p
ON c.customerNumber = p.customerNumber
WHERE p.customerNumber IS NULL;
Basically you missed the WHERE clause. And your question lacks information. Please provide the Schema of your tables. Thanks!
try this one:
Are you sure with this condition ON c.customerName = o.customerNumber?
SELECT customerWOpayments.customerNumber
FROM ClassicModels.Customers c INNER JOIN ClassicModels.Orders o
ON c.customerName = o.customerNumber -- Please check this out
WHERE o.customerNumber NOT IN
(SELECT distinct c.customerNumber
FROM ClassicModels.Customers c LEFT JOIN ClassicModels.Payments p
ON c.customerNumber = p.customerNumber
WHERE p.customerNumber IS NULL);
OR Without Subquery
SELECT a.*
FROM Customers a INNER JOIN Orders b ON
a.CustomerName = b.CustomerNumber -- Please check this line
LEFT JOIN Payments c ON
b.CustomerNumber = c.CustomerNumber
WHERE c.CustomerNumber IS NULL
I believe it's a typo error on a.CustomerName = b.CustomerNumber, instead a.CustomerNumber = b.CustomerNumber
I can't tell exactly if it is because you didn't provide the schema of your tables with some dummy records.
Hope this helps.
Unlike the other solutions, this solution will not produce duplicate customer numbers when customers have more than one order.
SELECT C.customerNumber
FROM ClassicModels.Customers C
WHERE
EXISTS(
-- customer has orders
SELECT *
FROM ClassicModels.Orders AS O
WHERE O.customerNumber = C.customerNumber
)
AND NOT EXISTS(
-- customer does not have payments
SELECT *
FROM ClassicModels.Payments P
WHERE P.customerNumber = C.customerNumber
)
In my query below, i am trying to join one result set to another using the customerNumber identifier. I want to find each customer's amount ordered and the amount paid. The sql does not execute with no help from Navicat Client
SELECT DISTINCT tabl1.customerNumber,
tabl1.amountOrdered, tabl2.amountPaid
FROM
(
SELECT Distinct c.customerNumber, o.orderNumber,
SUM(od.quantityOrdered * od.priceEach) amountOrdered
FROM ClassicModels.Customers c
INNER JOIN ClassicModels.Orders o
ON c.customerNumber = o.customerNumber
INNER JOIN ClassicModels.OrderDetails od
ON o.orderNumber = od.orderNumber
GROUP BY od.orderNumber
ORDER BY c.customerNumber
) tabl1
INNER JOIN
(
SELECT DISTINCT c.customerNumber, p.amount amountPaid
FROM ClassicModels.Customers c
INNER JOIN ClassicModels.Payments p
ON c.customerNumber = p.customerNumber
) tabl2 ON tabl1.customerNumber = tabl2.customerNumber
If you want to select each customer's amount ordered and amount paid, this much simpler query should work:
SELECT o.customerNumber as customerNumber,
SUM(od.quantityOrdered * od.priceEach) as amountOrdered,
SUM(p.amount) as amountPaid
FROM ClassicModels.Orders o
INNER JOIN ClassicModels.OrderDetails od
ON o.OrderNumber = od.OrderNumber
LEFT JOIN ClassicModels.Payments p
ON p.customerNumber = o.customerNumber
GROUP BY o.customerNumber