MySQL: sum values from subqueries - mysql

Is it possible to sum two values from subqueries?
I need select three values: total_view, total_comments and rating.
Both subqueries is very complicated, so i don't wish duplicate it.
My query example:
SELECT p.id,
(
FIRST subquery
) AS total_view,
(
SECOND subquery
) AS total_comments,
(
total_view * total_comments
) AS rating
FROM products p
WHERE p.status = "1"
ORDER BY rating DESC

I would suggest using a subquery:
SELECT p.*, (total_view * total_comments) as rating
FROM (SELECT p.id,
(FIRST subquery) AS total_view,
(SECOND subquery) AS total_comments,
FROM products p
WHERE p.status = '1' -- if status is a number, then remove quotes
) p
ORDER BY rating DESC;
MySQL materializes the subquery. But because the ORDER BY is on a computed column, it needs to sort the data anyway, so the materialization is not extra overhead.

You can't use alias but you can use the same code eg:
SELECT p.id,
(
FIRST subquery
) AS total_view,
(
SECOND subquery
) AS total_comments,
(
(
FIRST subquery
) * (
SECOND subquery
)
) AS rating
FROM products p
WHERE p.status = "1"
ORDER BY rating DESC

Simply use a Derived Table to be able to reuse the aliases:
SELECT p.id,
total_view,
total_comments,
total_view * total_comments AS rating
FROM
(
SELECT p.id,
(
FIRST subquery
) AS total_view,
(
SECOND subquery
) AS total_comments
FROM products p
WHERE p.status = "1"
) as dt
ORDER BY rating DESC

Related

Solution to MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'

For some reason MYSQL doesn't support LIMIT inside a subquery:
SELECT m.*
FROM `my_table` m
WHERE m.`id` IN (
SELECT o.`id`
FROM (SELECT DISTINCT i.`id`, i.`label`, i.`client`, i.`place`
FROM `my_table` i
ORDER BY i.`label`, -i.`client` DESC, -i.`place` DESC) o
WHERE m.`label` = o.`label` LIMIT 1
);
I've tried using join from this link: INNER JOIN INSTEAD OF IN(LIMIT error) but not succeeded. Has anyone any clues for it? Thanks.
Since the subquery returns only 1 row with 1 column there is no need for IN.
You can use =:
SELECT m.*
FROM `my_table` m
WHERE m.`id` = (
SELECT o.`id`
FROM (
SELECT DISTINCT i.`id`, i.`label`, i.`client`, i.`place`
FROM `my_table` i
ORDER BY i.`label`, -i.`client` DESC, -i.`place` DESC) o
WHERE m.`label` = o.`label` LIMIT 1
);
But as it is written, your query uses LIMIT without ORDER BY (you do use ORDER BY in the inner subquery where it is useless).
Do you mean to do something like this:
SELECT m.*
FROM `my_table` m
WHERE m.`id` = (
SELECT o.`id`
FROM (
SELECT DISTINCT i.`id`, i.`label`, i.`client`, i.`place`
FROM `my_table` i
) o
WHERE m.`label` = o.`label`
ORDER BY o.`label`, -o.`client` DESC, -o.`place` DESC
LIMIT 1
);
Also ordering by the negative value of a column descending is equivalent to ordering just ascending, so the ORDER BY clause can be simplified to:
ORDER BY o.`label`, o.`client`, o.`place`

SQL doesn't see alias

SELECT name,
manufacturer,
prize
FROM products AS p
GROUP BY manufacturer
HAVING prize = (
SELECT Max(prize)
FROM products p1
WHERE p.`id-product` = p1.`id-product`
GROUP BY p1.manufacturer DESC
LIMIT 1
)
And the error is:
#1054 - Unknown column 'shop.p.id.product' in 'where clause'
I think you need correlated subquery instead :
SELECT p.name, p.manufacturer, p.prize
FROM products AS p
WHERE p.prize = (SELECT MAX(p1.prize) FROM products p1 WHERE p1.manufacturer = p.manufacturer);
By this way, you will get manufacturers with highest price.
EDIT : If one product has same prize then you will need PK (Primary/Identity) column that specify unique sequence :
SELECT p.name, p.manufacturer, p.prize
FROM products AS p
WHERE p.pk = (SELECT p1.pk
FROM products p1
WHERE p1.manufacturer = p.manufacturer
ORDER BY p1.prize DESC
LIMIT 1
);
If you are running with latest version of MySQL, then you can also use ranking function :
SELECT p.*
FROM (SELECT p.name, p.manufacturer, p.prize,
ROW_NUMBER() OVER (PARTITION BY p.manufacturer ORDER BY p.prize DESC) AS Seq
FROM products AS p
) p
WHERE Seq = 1;
Your query has nothing of that form. I would recommend writing the query by qualifying all column references:
SELECT p.name, p.manufacturer, p.prize
FROM products p
GROUP BY p.manufacturer
HAVING p.prize = (SELECT MAX(p1.prize)
FROM products p1
WHERE p.`id-product` = p1.`id-product`
GROUP BY p1.manufacturer DESC
LIMIT 1
);
MySQL allows the syntax with extra columns in the SELECT that are not in the GROUP BY, so that would not (normally) generate an error. It is not correct, though, by the rules of SQL.
I'm not sure what the subquery is supposed to be doing. If you want the manufacturers with the highest price, then you would not use the GROUP BY in the subquery.
If you want the highest priced product for each manufacturer:
SELECT p.name, p.manufacturer, p.prize
FROM products p
HAVING p.prize = (SELECT MAX(p1.prize)
FROM products p1
WHERE p.manufacturer = p1.manufacturer
LIMIT 1
);

MySQL - the cheapest order

I have 2 MySQL tables:
Persons:
person_id
person_name
Orders:
person_id
cost
order_name
I need to make a ONE sql-query to get person name and it's cheapest order.
Want to write some like this: SELECT person_name, order=(SELECT order_name FROM orders WHERE order.person_id = person.person_id ORDER BY order.cost ASC LIMIT 1) FROM person
But it don't work.
I would use a correlated query. There are many ways to do it: as a subquery in the SELECT clause, or as a subquery in the WHERE clause.
In this case, I implemented the second option:
SELECT
person_name,
order_name
FROM Persons
INNER JOIN Orders ON Persons.person_id = Orders.person_id
WHERE
(
cost = (
SELECT MIN(cost)
FROM Orders
WHERE
(Orders.person_id = Persons.person_id)
)
)
remove order= and add AS order_name
Something like this:
SELECT p.person_name
, ( SELECT o.order_name
FROM orders o
WHERE o.person_id = p.person_id
ORDER BY o.cost ASC
LIMIT 1
) AS order_name
FROM person p
ORDER BY p.person_name
SELECT person_name, order_id, order_name, MIN(cost) as cost
FROM persons
INNER JOIN orders
ON persons.person_id = orders.person_id
GROUP BY person.person_name, order_id, order_name;
You can do this just by joining your 2 tables and use MIN.

Mysql count result in "where" clause

I'm facing a little problem with mysql where clause.
This is the query:
SELECT u.id user
, p.id product_purchased
, p.name product_name
, pl.store_id store
, COUNT(*) occurrences
, total_spent
, total_product_purchased
, pl.registration
FROM purchases_log pl
JOIN user u
ON pl.user_id = u.id
JOIN product p
ON pl.product_id = p.id
JOIN
( SELECT user_id
, SUM(price) total_spent
, COUNT(product_id) total_product_purchased
FROM purchases_log pl
GROUP
BY user_id
) t1
ON u.id = t1.user_id
WHERE pl.store_id IN (1,2,3)
AND occurrences > 1
GROUP
BY user
, product_name
ORDER
BY u.id ASC
, pl.registration ASC;
This is the output error:
Error Code: 1054. Unknown column 'occurrences' in 'where clause' 0.067 sec
I have already tried assign AS to occurrences or using pl.
So, can someone explain me how to correctly define the result of a count function in where clause?
You need to use HAVING instead of COUNT as group by is applied after WHERE clause and hence, it won't know about any group/aggregate columns, e.g/:
SELECT u.id user,p.id product_purchased, p.name product_name, pl.store_id store, COUNT(*) AS occurrences, total_spent, total_product_purchased, pl.registration
FROM purchases_log pl
JOIN user u ON pl.user_id=u.id
JOIN product p ON pl.product_id=p.id
JOIN (SELECT user_id, SUM(price) AS total_spent,COUNT(product_id) AS total_product_purchased FROM purchases_log pl GROUP BY user_id) t1 ON u.id=t1.user_id
WHERE pl.store_id IN (1,2,3)
GROUP BY user, product_name
HAVING COUNT(*) > 1
ORDER BY u.id ASC, pl.registration ASC;
Update
If a user has more than one product associated then it's good to add all the non aggregate columns in GROUP BY to get all the combinations of user and product. The current query will not return all the combinations.
For further optimization, as #strawberry has suggest, you can run EXPLAIN and see which indices are used and whether there is any need to create any new index.

Count the number of times an index exists using a query containing multiple EXIST() statements

My query gets the results of these products based on if they exist in a separate table index. I am trying to get a count of all the instances where they exist so I can ORDER the results by relevance. Everything I try seems to return the variable #priority as 0. Any ideas?
Maybe it is better to use join statements?
Thank you for your help. Here is my MySQL query:
SELECT `products` . * , #priority
FROM `products`
LEFT JOIN productstypes_index ON productstypes_index.product_id = products.id
WHERE (
EXISTS (
SELECT *
FROM `productstypes_index`
WHERE `productstypes_index`.`product_id` = `products`.`id`
AND `productstypes_index`.`_type_id` = '1'
)
AND (
(
(
EXISTS (
SELECT #priority := COUNT( * )
FROM `producthashtags_index`
WHERE `producthashtags_index`.`product_id` = `products`.`id`
AND `producthashtags_index`.`producthashtag_id` = '43'
)
)
AND (
EXISTS (
SELECT #priority := COUNT( * )
FROM `producthashtags_index`
WHERE `producthashtags_index`.`product_id` = `products`.`id`
AND `producthashtags_index`.`producthashtag_id` = '11'
)
)
)
)
)
ORDER BY `updated_at` DESC;
You could do without those exists, and without variables. Also, a left join has no sense if you have an exists condition on the joined table. Then you might as well do the more efficient inner join and put the extra type condition in the join condition.
The priority can be calculated by a count over the hash tags, but only those with id in ('43', '11').
SELECT products.*
count(distinct producthashtags_index.producthashtag_id) priority
FROM products
INNER JOIN productstypes_index
ON productstypes_index.product_id = products.id
AND productstypes_index._type_id = '1'
INNER JOIN producthashtags_index
ON producthashtags_index.product_id = products.id
AND producthashtags_index.producthashtag_id in ('43', '11')
GROUP BY products.id
ORDER BY updated_at DESC;
MySQL ignores the SELECT list in EXISTS subquery, so it makes no difference what you type in there. This is documented here.
An approach using joins would look like below:
SELECT p.id,
COUNT(case when phi.product_id is not null then 1 end) AS instances
FROM products p
INNER JOIN productstypes_index pti ON pti.product_id = p.id AND pti.`_type_id` = 1
LEFT JOIN producthashtags_index phi ON phi.product_id = p.id AND phi.producthashtag_id IN (11,43)
GROUP BY p.id
ORDER BY instances DESC;
I have removed additional backticks where I believe they are not neccessary and also if your id columns in tables are integers, you do not need quotation marks.