Sub queries, Aliases and Stored Values- MySQL - mysql

I am given a problem as following:
Create a SQL statement that shows all shipped orders with a total of more than $14,000. The results must contain the customer's first_name, last_name, order_id, order shipped_date, number of items and total amount. Make sure any discount is taken into account when calculating the total amount.
Tables to draw from are:
Order Items: quantity, list_price, discount, order_id
Order: order_id, shipped_date, order_status, customer_id
Customers: first_name, last_name, customer_id.
I'm pretty new to SQL and sub-queries so I tried to build it from inside out and it kept giving me rows returned...until the last join ("sub2"). My code is below (inelegant, but I do need to figure out the problem before worrying about that).
select c.first_name, c.last_name, sub2.order_id, sub2.shipped_date, sub2.quantity, sub2.total_amount from customers AS c
INNER JOIN(
select o.order_id, o.shipped_date, o.order_status, o.customer_id, sub1.total_price FROM orders as o
INNER JOIN (
SELECT oi.order_id, SUM((oi.list_price * oi.quantity) - ((oi.list_price * oi.quantity) * (oi.discount))) AS total_price
FROM order_items AS oi
group by oi.order_id
) AS sub1
ON o.order_id = sub1.order_id
WHERE
sub1.total_price > 14000
AND
o.order_status = 4
) AS sub2
ON c.customer_id = sub2.customer_id
;
The error I'm getting is "Unknown column 'sub2.quantity' in 'field list'"
I imagine Mysql does not like this double nesting, so how can I go about solving this?

There's no problem with nested queries, your problem is that the names on your nested statements don't correlate with the ones used outside as the alias.
eg: ... INNER JOIN(select o.order_id, o.shipped_date, o.order_status, o.customer_id, sub1.total_price FROM ...
doesn't has a quantity casted, so when it's rows are called outside as sub2 there is an Unknown column 'sub2.quantity' in field list, it will fail also with sub2.total_amount for the same reason.
I'm guessing that since you are grouping by the total amount of an order, it should also give the sum of the items quantities. So the SQL should be like so:
select c.first_name, c.last_name, sub2.order_id, sub2.shipped_date, sub2.quantities, sub2.total_amount from customers AS c
INNER JOIN(
select o.order_id, o.shipped_date, o.order_status, o.customer_id, sub1.total_price as total_amount, sub1.total_quantities FROM orders as o
INNER JOIN (
SELECT oi.order_id,SUM(oi.quantity) as total_quantities, SUM((oi.list_price * oi.quantity) - ((oi.list_price * oi.quantity) * (oi.discount))) AS total_price
FROM order_items AS oi
group by oi.order_id
) AS sub1
ON o.order_id = sub1.order_id
WHERE
sub1.total_price > 14000
AND
o.order_status = 4
) AS sub2
ON c.customer_id = sub2.customer_id group by sub2.order_id;

Related

MySQL not returning values from NOT IN function

I currently am trying to write a query that shows customers with at least 5 orders and customer with no orders. Orders are tracked in their own table and in order to find customers with 0 orders we have to find the customers NOT IN orders. Below is my query I'm trying to use and it returns the same customer 5 times for zero orders.
with t1 as
(select o.customerNumber, c.customerName, count(o.orderNumber) as FiveOrders
from orders o join customers c on (o.customerNumber = c.customerNumber)
group by o.customerNumber having count(o.orderNumber) = 5),
t2 as
(select distinct o.customerNumber, c.customerName, count(o.orderNumber) as NoOrders
from orders o join customers c on (o.customerNumber = c.customerNumber)
group by c.customerNumber not in(select customerNumber from orders))
select distinct t1.customerNumber as FiveOrderNumber, t1.customerName as FiveOrderName,
t2.customerNumber as NoOrderNumber, t2.customerName as NoOrderName
from t1 join t2
order by NoOrderName;
Any and all help is appreciated thanks!
If the errors were only in the second table to, I think it is after using
having with condition NOT IN without any logical comparison, I think you can get wanted results easily like:
select distinct customerNumber, customerName, "0" as NoOrders
from customers
where customerNumber not in (Select customerNumber from orders)
If the group by is important, you can use it like in your code.
Zero or five could be counted together with LEFT JOIN
select c.customerNumber, max(c.customerName) customerName, count(o.orderNumber) as FiveOrdersOrZero
from customers c
left join orders o on o.customerNumber = c.customerNumber
group by c.customerNumber
having count(o.orderNumber) in ( 0, 5 )
order by FiveOrdersOrZero

Getting invalid use of group function

Here's my query
Select email_address, count(order_id) AS order_count, (sum(item_price - discount_amount) * (quantity)) AS order_total,
(avg(item_price - discount_amount) * (quantity)) AS avg_order_total
from customers join orders
using(customer_id)
join order_items
using(order_id)
group by customer_id > 1, email_address
Output of my query
Output Expected
I'm trying to produce the wanted output but I'm not sure how to only display only 3 of them. Tried a where statement but it made it worst.
Another way to display output without the use of limit?
Here's my REVISED query
Select email_address, count(customer_id) AS order_count, sum((item_price - discount_amount) * (quantity)) AS order_total,
round(avg((item_price - discount_amount) * (quantity)),2) AS avg_order_total
from customers join orders
using(customer_id)
join order_items
using(order_id)
group by customer_id
order by count(customer_id) desc limit 3
I'm trying to produce the expected output without using limit but I can't figure how. Above is my query that I used limit on to display the result and it shows the output I wanted. I tried using a where statement, but it doesn't run my query when I did.
Could you try this query?
SELECT c.email_address,
count(o.order_id) AS order_count,
(sum(i.item_price - i.discount_amount) * (i.quantity)) AS order_total,
(avg(sum(i.item_price - i.discount_amount) * (i.quantity))) AS avg_order_total
from customers c
INNER JOIN orders o ON c.customer_id = o.customer_id
INNER JOIN order_items i ON o.order_id = i.order_id
GROUP BY email_address;
A few things to note:
You need to include any field in the SELECT clause that isn't being aggregated into your GROUP BY. So, I've added email_address.
It's far better to specify the actual columns being joined, instead of the USING keyword. This is because it's easier to maintain if any of the columns change.
I've made some assumptions about which table your fields belong to and prefixed them with the table alias (c, o, or i). If this is wrong they can be changed. But it's better to use table aliases as it improves the maintainability of the code, and it's easier to read & write.
There are several guesses in this suggestion:
SELECT
c.email_address
, COUNT(DISTINCT o.order_id) AS order_count
, (SUM(oi.item_price - oi.discount_amount) * (oi.quantity)) AS order_total
, (AVG(oi.item_price - oi.discount_amount) * (oi.quantity)) AS avg_order_total
FROM customers c
JOIN orders o ON c.customer_id = o.customer_id
JOIN order_items oi ON o.order_id = oi.order_id
WHERE c.customer_id > 1
GROUP BY
c.email_address
When joining several tables it is good practice to nominate which table a column comes from. To make this brief each table can be given an alias and a common method for that is to use first letters e.g. for this set of tables: c, o, oi
I may have guessed incorrectly when placing the aliases in some places and I cannot fix that as I don't know your tables.
+EDIT
By the way, I also included DISTINCT into the count of orders, if you don't do that you are counting the number of order items instead.
SELECT
c.email_address
, COUNT(DISTINCT o.order_id) AS order_count
, (SUM(oi.item_price - oi.discount_amount) * (oi.quantity)) AS order_total
, (SUM(oi.item_price - oi.discount_amount) * (oi.quantity))
/ COUNT(DISTINCT o.order_id) AS avg_order_total
FROM customers c
JOIN orders o ON c.customer_id = o.customer_id
JOIN order_items oi ON o.order_id = oi.order_id
WHERE c.customer_id > 1
GROUP BY
c.email_address

SELECT a column and SUM() of values from another table in SQL

I'm pretty new with SQL, and this is giving me trouble. The idea is that I have several tables. Here are the relevant tables and columns:
customers:
customer_id, customer_name
orders:
order_id, customer_id
orderline:
order_id, item_id, order_qty
items:
item_id, unit_price
I need to return customer_name as well as total revenue from that customer (calculated as item_price * order_qty * 2).
Here's what I have written:
SELECT customers.customer_name, sum(revenue)
FROM SELECT orderline.order_qty * items.unit_value * 2 AS revenue
FROM orderline
INNER JOIN orders
ON orderline.order_id = orders.order_id
INNER JOIN customers
ON revenue.customer_id = customers.customer_id;
This throws a syntax error and I'm not really sure how to proceed.
This is only one example of this type of problem that I need to work out, so more generalized answers would be helpful.
Thanks in advance!
EDIT:
With help from answers I ended up with this code, which just gets total revenue and puts it next to the first person in the DB's name. What did I get wrong here?
SELECT customers.customer_name, sum(revenue)
FROM(SELECT orderline.order_qty * items.unit_price * 2 AS revenue, orders.customer_id AS CustomerID
FROM( orderline
INNER JOIN orders
ON orderline.order_id = orders.order_id
INNER JOIN items
ON orderline.item_id = items.item_id)) CustomerOrders
INNER JOIN customers
ON CustomerOrders.CustomerID = customers.customer_id;
A couple issues with your query.
First, you need to scope your subquery and alias it:
(SELECT orderline.order_qty * items.unit_value * 2 AS revenue
FROM orderline
INNER JOIN orders
ON orderline.order_id = orders.order_id) CustomerOrders
Secondly, you need to select more than the revenue in the subquery since you are joining it to your customers table
(SELECT
orderline.order_qty * items.unit_value * 2 AS revenue,
orders.customer_id AS CustomerId
FROM
orderline
INNER JOIN orders ON orderline.order_id = orders.order_id) CustomerOrders
Then you need to use the subquery alias in the join to the customers table and wrap it all up in a group by customer_id and CustomerOrders.Revenue
I would tend to do it differently. I'd start with selecting from the customer table, because that is the base of what you are looking for. Then I'd do a cross apply on the orders that would all aggregating the order revenue in the subquery. It would look like this (tsql, you could do the same in mysql with a join with some aggregation):
SELECT
customers.customer_name,
ISNULL(customerOrders.Revenue, 0) AS Revenue
FROM
customers
OUTER APPLY (
SELECT
SUM (orderline.order_qty * items.unit_value * 2) AS Revenue
FROM
orders
INNER JOIN
orderline ON orders.order_id = orderline.order_id
INNER JOIN
items on orderline.item_id = items.item_id
WHERE
orders.customer_id = customers.customer_id
) CustomerOrders
In this case, the subquery aggregates all your orders for you and only returns one row per customer, so no extraneous returned data. Since it's an outer apply, it will also return null for customers with no orders. You could change it to a CROSS APPLY and it will filter out customers with no orders (like an INNER JOIN).
SELECT c.customer_name,
sum(COALESCE(ol.order_qty,0) * COALESCE(i.unit_value,0) * 2)
FROM customers c
INNER JOIN orders o
ON o.customer_id = c.customer_id;
INNER JOIN orderline ol
ON ol.order_id = o.order_id
INNER JOIN items i
ON i.item_id = ol.item_id
GROUP BY c.customer_id
select customer_name, sum(item_price * order_qty * 2) as total_revenue
from (
select * from customers
inner join orders using(customer_id)
inner join orderline using(order_id)
inner join items using(item_id)
)
group by customer_name
select
c.customer_name,
r.revenue
from
customers c
inner join
orders ord on
ord.customer_id = c.customer_id
inner join
(select i.item_id, o.order_id, sum(o.order_qty * items.unit_value * 2) as revenue
from orderline o
inner join items i on
i.item_id = o.item_id
group by o.order_id, i.item_id) as r on r.order_id = o.order_id

what is the solution of this query?

I am new to database field. I am currently learning mysql using Murach's Mysql. I came across following problem in the book but I am not able to figure out the correct query for solving it.
Write a SELECT statement that
joins the
Customers, Orders, Order_Items, and
Products tables. This statement should return these columns:
last_name,
first_name,
order_date, product_na
me, item_price, discount_amount,
and
quantity
.
Use aliases for the tables.
Sort the final result set by
last
_name,
order
_date, and
product_name
.
By far I have this query:
select last_name , first_name , order_date , product_name , tem_price,
discount_amount, quantity
from customers , orders , order_items product
order by last_name , order_date , product_name
You can use inner join for these 4 tables Customers , Orders, Order_items , Products but make sure there is a match between the columns in these 3 tables.
Example of the sql query based on the given information. (Assume CustomerID is a match between the columns)
Select A.last_name,A.first_name,B.order_date,B.product_name,C.item_price,C.discount_amount,D.quantity
from TB_Customers A inner join TB_Orders B on A.CustomerID = B.CustomerID
inner join TB_Order_Items C on B.CustomerID = C.CustomerID on
inner join TB_Products D on C.CustomerID = D.CustomerID Where D.CustomerID ='TEST111'
Using ANSI standard join syntax:
SELECT c.last_name, c.first_name, o.order_date, p.product_name,
p.item_price, p.discount_amount, p.quantity
FROM Customers AS c
INNER JOIN Orders AS o ON c.order_id = o.order_id
INNER JOIN Order_Items AS i ON o.order_id = i.order_id
INNER JOIN Products AS p ON i.product_id = p.product_id
ORDER BY c.last _name, o.order _date, p.product_name
Well, we are going to analize the query. Since you didn't specify the database structure, I'm going to do an aproaching.
"SELECT statement that joins the Customers, Orders, Order_Items, and Products tables."
This is refered to the tables, this is in the FROM clause. You have to join them with WHERE clause.
SELECT *
FROM Customers, Orders, Order_Items, Products
WHERE Customers.order_id = Orders.order_id
AND Order_Items.order_id = Orders.order_id
AND Products.product_id = Order_Items.product_id
"This statement should return these columns: last_name, first_name, order_date, product_na me, item_price, discount_amount, and quantity "
This part is refered to the SELECT clause.
SELECT last_name, first_name, order_date, product_name,
item_price, discount_amount, quantity
FROM Customers, Orders, Order_Items, Products
WHERE Customers.order_id = Orders.order_id
AND Order_Items.order_id = Orders.order_id
AND Products.product_id = Order_Items.product_id
"Use aliases for the tables"
This part involves the whole query and means that have to rename the tables (virtually and only form this query) and in every reference you have to use the new name.
SELECT c.last_name, c.first_name, o.order_date, p.product_name,
p.item_price, p.discount_amount, p.quantity
FROM Customers AS c, Orders AS o, Order_Items AS i, Products AS p
WHERE c.order_id = o.order_id
AND i.order_id = o.order_id
AND p.product_id = i.product_id
"Sort the final result set by last _name, order _date, and product_name."
This part refered to th ORDER BY clause. You have to keep in mind the aliases
SELECT c.last_name, c.first_name, o.order_date, p.product_name,
p.item_price, p.discount_amount, p.quantity
FROM Customers AS c, Orders AS o, Order_Items AS i, Products AS p
WHERE c.order_id = o.order_id
AND i.order_id = o.order_id
AND p.product_id = i.product_id
ORDER BY c.last _name, o.order _date, p.product_name

Can I execute a COUNT() before GROUP BY

I am working on an mySQL assignment for school and I am stuck on a question. I am still new to mySQL. COUNT(o.customer_id) is not working the way I want. I want it to count the number of orders but it is counting all items. i.e. Customer 1 has 2 orders but it is returning 3 because one order has two items. I have three tables one with customers, another with orders than another with each item on each order. Ive posed my query below. Any help would be great.
SELECT email_address, COUNT(o.order_id) AS num_of_orders,
SUM(((item_price - discount_amount) * quantity)) AS total
FROM customers c JOIN orders o
ON c.customer_id = o.customer_id
JOIN order_items ot
ON o.order_id = ot.order_id
GROUP BY o.customer_id
HAVING num_of_orders > 1
ORDER BY total DESC;
As simple as use Distinct reserved word:
SELECT email_address, COUNT(distinct o.order_id) AS num_of_orders
Looks like you want to count the DISTINCT number of orders. Add a DISTINCT into the COUNT. Although MySQL allows you to use the SELECT expression in the HAVING clause, it's not good practice to do so.
SELECT email_address, COUNT(DISTINCT o.order_id) AS num_of_orders,
SUM(((item_price - discount_amount) * quantity)) AS total
FROM customers c JOIN orders o
ON c.customer_id = o.customer_id
JOIN order_items ot
ON o.order_id = ot.order_id
GROUP BY o.customer_id
HAVING COUNT(DISTINCT o.order_id) > 1
ORDER BY total DESC;
Just take out the join to items. All it is doing is duplicating rows when there are multiple items.
SELECT email_address, COUNT(o.order_id) AS num_of_orders,
SUM(((item_price - discount_amount) * quantity)) AS total
FROM customers c JOIN orders o
ON c.customer_id = o.customer_id
GROUP BY o.customer_id
HAVING COUNT(o.order_id) > 1
ORDER BY total DESC;