Suppose there is table Product with columns prod_id, a, b
Suppose there is table Warehouse with columns w_id, pid, amount
The point is to join these two tables so that we would get in result columns prod_id, a, b, total (which is sum of the amount for pid = prod_id)
Currently I have this query:
SELECT
t1.prod_id, a, b, c AS total
FROM
(
SELECT prod_id, a, b
FROM product
WHERE ean = "3165142644363"
) t1
LEFT JOIN
(
SELECT warehouse.pid, SUM(amount) AS c
FROM warehouse
WHERE warehouse.pid IN (SELECT prod_id FROM product WHERE ean = "3165142644363")
GROUP BY warehouse.pid
) t2
ON t2.pid = t1.prod_id
Now you see there is two similar but not identical queries:
SELECT prod_id, a, b FROM product WHERE ean = "3165142644363"
SELECT prod_id FROM product WHERE ean = "3165142644363"
Executing both of these queries takes too much time. Is there a way to execute only the first one and then select product id from the result of that subquery? Or any other efficient way to rewrite the query. Creating the view is not an option as there can be many queries all with different ean parameter. The query returns more than one row of data for the same ean
I think you can remove the subquery appearing after WHERE ... IN:
SELECT
t1.prod_id, a, b, c AS total
FROM
(
SELECT prod_id, a, b
FROM product
WHERE ean = '3165142644363'
) t1
LEFT JOIN
(
SELECT warehouse.pid, SUM(amount) AS c
FROM warehouse
GROUP BY warehouse.pid
) t2
ON t2.pid = t1.prod_id
The reason you can remove it is that the t2 subquery would only contain pid values which appear in the t1 subquery. Otherwise, the sum c would just be zero anyway after the left join. A left join already implicitly handles the case where records from product may not join to anything in the t2 subquery.
That being said, removing the WHERE ... IN subquery might not improve performance that much, because it is not correlated anyway. Most likely the optimizer would only need to compute it once and cache it somewhere.
Eventually found a solution myself. Way easier than I thought:
SELECT pid, a, b, SUM(amount) AS total
FROM product
LEFT JOIN warehouse USING(pid)
WHERE ean = "3165142644363"
GROUP BY pid
In my example there are columns prod_id and pid, but in fact it is pid in both tables
Here's another formulation that might be better:
SELECT pid, a, b,
( SELECT SUM(amount)
FROM wherehouse
WHERE pid = product.prod_id
) AS total
FROM product
WHERE ean = "3165142644363";
With indexes:
product: INDEX(ean)
wherehouse: INDEX(pid, amount) -- in this order
Related
I have two similar SELECT queries that retrieve data from the same table "my_table".
-- 1st select
SELECT
my_table.id,
a,
b
FROM my_table
JOIN table2 ON u = v
JOIN table3 ON x = y
UNION ALL
-- 2st select
SELECT
my_table.id,
a,
b
FROM my_table
JOIN table2 ON r = s
JOIN table3 ON t = u
Duplicates are to be filtered out under the following conditions:
If the second select returns an id that is already present in the 1st select, it should be discarded.
Is there an easy solution without using a common table expression?
Note: The SQL does not have to be a UNION and can also be changed.
UNION filters out duplicate rows by default. UNION ALL does not remove duplicates.
But the duplicates are based on all columns being identical, not just the id column. If a given id value occurs in both queries, but any of the other two columns are different, then it counts as a distinct row.
If you want to reduce the result to a single row per id, the use a GROUP BY:
SELECT id, ...aggregate expressions...
FROM (
SELECT my_table.id, a, b ...
UNION
SELECT my_table.id, a, b ...
) AS t
GROUP BY id;
When you GROUP BY id, then any other expressions of the outer select-list must be in aggregate functions like MAX() or SUM(), etc.
The reason it is important to use an aggregate function is that when there are multiple rows with the same id value which you want to reduce to one row, what value should be displayed for a and b?
Example:
id
a
b
4
12
24
4
18
28
If you group by id, you would get one row for id=4, but what value for the other two columns?
id
a
b
4
?
?
Read https://dev.mysql.com/doc/refman/8.0/en/group-by-handling.html for more details on this. Or my answer to Reason for Column is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause
You must use an aggregate function, which includes GROUP_CONCAT() to append all the values from that column in a comma-separated list. Or you can use ANY_VALUE() which picks one of the values from that column arbitrarily.
I think this should do it:
-- 1st select
SELECT
my_table.id,
a,
b
FROM my_table
JOIN table2 ON u = v
JOIN table3 ON x = y
WHERE id NOT IN (
SELECT
my_table.id,
FROM my_table
JOIN table2 ON r = s
JOIN table3 ON t = u
)
UNION ALL
-- 2st select
SELECT
my_table.id,
a,
b
FROM my_table
JOIN table2 ON r = s
JOIN table3 ON t = u
I don't know if my title is understandable or not, may be someone can help edit my title?
All I want to do is, for example:
I have a table like this
Engineering appears 5 times with different article_category_abbr, and I want to select only one row with the biggest value of num.
Here, it will be Engineering-ENG-192, and Geriatrics&Gerontology will be Geriatrics&Gerontology-CLM-26
But I don't know how to do it on the whole table using mysql
Join your table to a subquery which finds the greatest num value for each sc group.
SELECT t1.*
FROM yourTable t1
INNER JOIN
(
SELECT sc, MAX(num) AS max_num
FROM yourTable
GROUP BY sc
) t2
ON t1.sc = t2.sc AND
t1.num = t2.max_num;
You can have a subquery that gets the largest value for each sc and the resulting rows will then be joined with the table itself based from two columns - sc and num.
SELECT a.*
FROM tableName a
INNER JOIN
(
SELECT sc, MAX(num) AS Num
FROM tableName
GROUP BY sc
) b ON a.sc = b.sc
AND a.num = b.num
Here's a Demo
USE MAX function and GROUP BY like this. Here is more information.
SELECT myID, classTitle, subField, MAX(score) FROM myTable GROUP BY myID, classTitle, subField
I am struggling with a MySQL query which I cant get to work as I want.
In table1 I have co_id, name, code, product, logindate.
in table2 I have pr_id, productname, productno, price.
I want to count and group the PRODUCT from table1, so I can see how many that have picked for example product 1,2,3 etc.
But when I list the result on the page I will need productname, and productno for each id number in the GROUP search. table1.product is joined with table2.pr_id
This is what I have so far, but I think I am missing something with INNER JOIN or similar, right?
SELECT
codes.pickedgift,
products.productno,
products.productname,
COUNT(codes.pickedgift) as num
FROM
codes,
products
GROUP BY codes.pickedgift
ORDER BY codes.pickedgift
you missing the join condition, when you join 2 tables you should link primary key in table1 to its foreign key in another table, so your query can be:
SELECT
codes.pickedgift,
products.productno,
products.productname,
COUNT(codes.pickedgift) as num
FROM
codes INNER JOIN products ON codes.product = products.pr_id
GROUP BY codes.pickedgift
ORDER BY codes.pickedgift
You should use a sub-select for this query.
-- assuming I have your table structure correct.
SELECT p.productno, p.productname, num
FROM (SELECT codes.pickedgift, COUNT(codes.pickedgift) as num
FROM codes
GROUP BY codes.pickedgift) g
JOIN products p ON p.id = g.pickedgift
ORDER BY g.pickedgift
The other thing you have to make sure of is if you're using a group-by, the fields in your select must either be the fields in the group by, or aggregates. MySQL let's you include columns that are not part of the group-by / aggregate, it becomes ambiguous as to which value productno and productname should be represented, which is why I opted for a sub-select instead.
I have a sales table containing invoice number, product code, product category, qty, etc.
Using this table how would you go about finding invoices that contain at least one product from say category A AND 1 product from category B ?
Okay, here's what I believe is what you'll want. Happy to see any simpler suggestions if anyone has them:
Select tableA.invoiceNumber
from
(select * from myTable
where productCategory = 'A') tableA
inner join
(select * from myTable
where productCategory = 'B') tableB
on tableA.invoiceNumber = tableB.invoiceNumber
Here's a SQLFiddle:
http://sqlfiddle.com/#!9/03ddd/3
It's basically joining your query onto itself where there's a condition for each table on category.
Try this :
select * from mytable
inner join(
select invoiceNumber
from mytable
where mytable.productcategory = 'A'
) as a using (invoiceNumber)
inner join(
select invoiceNumber
from mytable
where mytable.productcategory = 'B') as b
using (invoiceNumber)
group by invoicenumber
How to convert this result:
Group | Sum
Services | 11120.99
Vendas | 3738.00
Into:
Group | Sum
Services | 74.84
Vendas | 25.16
That is, the second displays the results as percentages of total.
This is what I tried:
SELECT categories.cat AS 'Group', SUM(atual) AS 'Sum'
FROM `table1` INNER JOIN
categories
ON table1.category_id=categories.id
GROUP BY categoria
you can left join a total sum that is not grouped or split up, and divide that by your sum query. this way you are just doing the total select once for faster runtime
SELECT cat, sum_atual, sum_atual/total_atual as percent_atual
FROM
( SELECT categories.cat AS cat, SUM(atual) AS sum_atual
FROM `table1`
JOIN categories ON table1.category_id=categories.id
GROUP BY categoria
) t
LEFT JOIN
( SELECT SUM(atual) as total_atual
FROM `table1`
) t1
SELECT categories.cat AS categoria,
SUM(atual) * 100 / (select sum(atual) from table1) AS percentages
FROM `table1`
INNER JOIN categories ON table1.category_id=categories.id
GROUP BY categoria
You can do this several ways. One is to just use a subquery in the select clause. As written below, this assumes that the category_id column in table1 always matches categories:
SELECT c.categoria AS "Group", SUM(t1.atual) AS "Sum",
SUM(t1.atual) / (SELECT SUM(t1.atual) FROM table1) as "Percent"
FROM `table1` t1 INNER JOIN
categories c
ON t1.category_id = c.id
GROUP BY c.categoria;
I changed the group by clause as well. It is a good idea for the group by and select to use the same columns. And I added table aliases to all the column references, another good practice.