sql nested query with group by - mysql

I was reading some tutorials about group by clause, i faced the following problem and don't know why it was solved like that, the table is as follows:
the requirement is to select the most expensive product in each category, and the following query was the answer:
SELECT
categoryID, productID, productName, MAX(unitprice)
FROM
products A
WHERE
unitprice = (
SELECT
MAX(unitprice)
FROM
products B
WHERE
B.categoryId = A.categoryID)
GROUP BY categoryID;
i don't know why the above query was the answer, why it wasn't just:
SELECT
categoryID, productID, productName, MAX(unitprice)
FROM
products
GROUP BY categoryID;
also, if the first query is the right one, why MAX function exists in the outer and inner query, isn't it enough to exist in the inner query?
thanks.

The second query will produce an error because it is not possible to have columns in the select clause whitout grouping by them in the Group by clause (unless they are subject to the aggregation).
Therefore you need to first find the highest unit price in each category and then find which product has that uniprice. You can actually accomplish this in many ways. This first query is one of them.

From your picture it looks as others have mentioned that you are using mysql, the MYSQL optimiser doesn't like subqueries very much and it would horrible to run over lots of data, best habit is to use joins where possible (if you look at query plans in postgres, oracle or mssql it will re-write sub-queries as joins 90% of the time)
The second query will run on default mysql as it will group by the missed columns you missed.
Below is an example:
SELECT
A.categoryID, A.productID, A.productName, B.max_unitprice
FROM products A
JOIN (
SELECT
max(unit price) as max_unitprice,
categoryId
FROM products
GROUP BY categoryId) B
ON B.categoryId = A.categoryID

SELECT p.*
FROM products p
WHERE NOT EXISTS ( SELECT 'p2'
FROM products p2
WHERE p2.categoryId = p.categoryId
AND p2.unitPrice > p.unitPrice
)

Related

sql SELECT query for 3 tables

I have 3 tables:
1. products(product_id,name)
2. orders(id,order_id,product_id)
3. factors(id,order_id,date)
I want to retrieve product names(products.name) where have similar order_id on a date in two last tables.
I use this query for this purpose:
select products.name
from products
WHERE products.product_id ~IN
(
SELECT distinct orders.product_id FROM orders WHERE
order_id IN (select order_id FROM factors WHERE
factors.datex ='2017-04-29') GROUP BY product_id
)
but no result. where is my mistake? how can I resolve that? thanks
Your query should be fine. I am rewriting it to make a few changes to the structure, but not the logic (this makes it easier for me to understand the query):
select p.name
from products p
where p.product_id in (select o.product_id
from orders o
where o.order_id in (select f.order_id
from factors f
where f.datex = '2017-04-29'
)
) ;
Notes on the changes:
When using multiple tables in a query, always qualify the column names.
Use table aliases. They make queries easier to write and to read.
SELECT DISTINCT and GROUP BY are unnecessary in IN subqueries. The logic of IN already handles (i.e. ignores) duplicates. And by explicitly including the operations, you run the risk of a less efficient query plan.
Why might your query not work?
factors.datex has a time component. If so, then this will work date(f.datex) = '2017-04-29'.
There are no factors on that date.
There are no orders that match factors on that date.
There are no products in the orders that match the factors on that date.
In factors table column name is date so it should be -
factors.date ='2017-04-29'
You have written -
factors.datex ='2017-04-29'

SQL average and Join

I'm trying to merge these two statements into one query to get the a list of product names(or ids) against the average of their TTFF data, and I'm stuck.
select AVG(TTFF) from TTFFdata group by product_id
select product.product_name, count(*) from product join TTFFdata on product.product_id = TTFFdata.product_id
I've looked into using a temporary table (CREATE TEMPORARY TABLE IF NOT EXISTS averages AS (select AVG(TTFF) from TTFFdata group by product_id)) but couldn't get that to work with a join.
Anyone able to help me please?
You need to understand the components. Your second query is missing a group by. This would seem to be what you want:
select p.product_name, count(t.product_id), avg(t.TTFF)
from product p left join
TTFFdata t
on p.product_id = t.product_id
group by p.product_name
It is better to do group by on product_id, product_name for two reasons. One is, you can select product id along with product name. Second reason is, If the product name is not unique then it may give wrong results(this may be a rare scenario like product name is same but it differs based on other columns like version or model). The below is the final query.
select Product.product_id,
product_name,
AVG(TTFF) as Avg_TTFF
from Product
inner join
TTFFdata
on Product.product_id = TTFFdata.product_id
group by Product.product_id,Product.product_name
TTFFdata:
product:
Output:

Join query result with itself in MySQL

Let's say I have a query:
select product_id, price, price_day
from products
where price>10
and I want to join the result of this query with itself (if for example I want to get in the same row product's price and the price in previous day)
I can do this:
select * from
(
select product_id, price, price_day
from products
where price>10
) as r1
join
(
select product_id, price, price_day
from products
where price>10
) as r2
on r1.product_id=r2.product_id and r1.price_day=r2.price_day-1
but as you can see I am copying the original query, naming it a different name just to join its result with itself.
Another option is to create a temp table but then I have to remember to remove it.
Is there a more elegant way to join the result of a query with itself?
self join query will help
select a.product_ID,a.price
,a.price_day
,b.price as prevdayprice
,b.price_day as prevday
from Table1 a
inner join table1 b
on a.product_ID=b.product_ID and a.price_day = b.price_day+1
where a.price >10
You could do a bunch of things, just a few options could be:
Just let mysql handle the optimization
this will likely work fine until you hit many rows
Make a view for your base query and use that
could increase performance but mostly increases readability (if done right)
Use a table (non temporary) and insert your initial rows in there. (unfortunately you cannot refer to a temporary table more than once in a query)
this will likely be more expensive performance wise until a certain number of rows is reached.
Depending on how important performance is for your situation and how many rows you need to work with the "best" choice would change.
Just to get duplicates in the same row?
select product_id as id1, price as price1, price_day as priceday1, product_id as id2, price as price2, price_day as priceday2,
from products
where price>10

Get all the products, and putting a specific order's product first

I have 3 tables: products, orders and orderLines(order_id, product_id).
I have an sql query to figure out which seems nearly impossible to do in only one query.
Is there a way to have in only one query:
All the products but showning a specific order's products first;
which means that: for an order A: show product1, product2.. present in orderA's orderLines first, than the following products (not ordered) are shown next.
PS:
I know it's possible to achieve this with a union of two queries, but it would be better to have it done in only one query.
You can put a subquery in the order by clause. In this case, an exists subquery is what you need:
select p.*
from products p
order by (exists (select 1
from orderlines ol
where p.productid = ol.productid and o.orderid = ORDERA
)
) desc;

Joining two tables and selecting from the result

I am trying out SQL and having trouble figuring out making queries when it comes to combining info from multi tables. Been using w3school but they don't seem to have similar reference to the question below. Was thinking of joining them as my codes below but still that doesn't answer the question. Appreciate any advice. Thanks.
Question:
Products(itemID, description, quantity, supplierID)
Supplier(supplierID, name, address)
A product can be supplied by more than one supplier. Write the SQL to
list the quantity of each product by each supplier.
SELECT Products.quanity, Supplier.name
FROM Products INNER JOIN Supplier
ON Products.supplierID = Supplier.supplierID;
Assuming that the product table does not have two rows for the same supplier and product, then your query is very close. I would write it as:
SELECT p.description as ProductDescription, s.name as SupllierName, p.Quantity
FROM Products p INNER JOIN
Supplier s
ON p.supplierID = s.supplierID
ORDER BY p.description, p.Quantity desc;
Note the following:
The use of tables aliases (the p and s) make the query more readable and are used for every column reference.
The final result is explicitly order by product, with the largest quantity first.
If there are multiple rows for a given product and supplier, then you will need aggregation.
Looks like you need to use group by here:
select itemId, supplierId, sum(quantity) from Products group by itemId, supplierId;