问题
In my application, each product group has many products, and each product has one manufacturer. These relations are stored by MySQL in InnoDB tables product_groups
with an id
field, and products
with id
, product_group
and manufacturer
fields.
Is there a way to find the most common manufacturer in each product group, without resorting to selecting subqueries?
This is how I'm doing it currently:
SELECT product_groups.id,
(
SELECT manufacturer FROM products
WHERE product_group = product_groups.id
GROUP BY manufacturer
ORDER BY count(*) DESC
LIMIT 1
) manufacturer_mode
FROM product_groups;
回答1:
Try this solution:
SELECT
a.product_group,
SUBSTRING_INDEX(GROUP_CONCAT(a.manufacturer ORDER BY a.occurrences DESC SEPARATOR ':::'), ':::', 1) AS manufacturer_mode
FROM
(
SELECT
aa.product_group,
aa.manufacturer,
COUNT(*) AS occurrences
FROM
products aa
GROUP BY
aa.product_group,
aa.manufacturer
) a
GROUP BY
a.product_group
Explanation:
This still uses a form of subquery, but one which executes only once as opposed to one that executes on a row-by-row basis such as in your original example.
It works by first selecting the product_group
id, the manufacturer, and the count of how many times the manufacturer appears for each particular group.
The FROM
sub-select will look something like this after execution (just making up data here):
product_group | manufacturer | occurrences
---------------------------------------------------
1 | XYZ | 4
1 | Test | 2
1 | Singleton | 1
2 | Eloran | 2
2 | XYZ | 1
Now that we have the sub-select result, we need to pick out the row that has the maximum in the occurences
field for each product group.
In the outer query, we group the subselect once again by the product_group
field, but this time, only the product_group
field. Now when we do our GROUP BY
here, we can use a really compelling function in MySQL called GROUP_CONCAT
which we can use to concatenate the manufacturers together and in any order we want.
...GROUP_CONCAT(a.manufacturer ORDER BY a.occurrences DESC SEPARATOR ':::'...
What we are doing here is concatenating the manufacturers together that are grouped together per product_group
id, the ORDER BY a.occurrences DESC
makes sure that the manufacturer with the most appearances appears first in the concatenated list. Finally we are separating each manufacturer with :::
. The result of this for product_group
1
will look like:
XYZ:::Test:::Singleton
XYZ
appears first since it has the highest value in the occurance
field. We only want to select XYZ
, so we encase the concatenation within SUBSTRING_INDEX
, which will allow us to only pick the first element of the list based on the :::
delimiter.
The end result will be:
product_group | manufacturer_mode
---------------------------------------
1 | XYZ
2 | Eloran
来源:https://stackoverflow.com/questions/11223003/mode-calculation-without-a-subquery-field-in-mysql