使用GROUP BY对特定行进行分组

编程入门 行业动态 更新时间:2024-10-26 12:22:29
本文介绍了使用GROUP BY对特定行进行分组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述

SCHEMA

我在MySQL数据库中设置了以下设置:

CREATE TABLE项目(序列号,名称VARCHAR(100), group_id INT,价格DECIMAL(10,2), KEY items_group_id_idx(group_id), PRIMARY KEY(id)); INSERT INTO项目VALUES (1,'Item A',NULL,10),(2,'Item B',NULL,20), (3,'项目C',NULL,30),(4,'项目D',1,40),(5,'项目E',2,50),(6,'项目F',2,60),(7,'项目G',2,70);

问题 我需要选择:

  • 所有项目 group_id ,其中 NULL 值,和 group_id 具有最低价格价格。
  • 预期成果

    + ---- + -------- + ---------- + ------- + | id |名称| group_id |价格| + ---- + -------- + ---------- + ------- + | 1 |项目A | NULL | 10.00 | | 2 |项目B | NULL | 20.00 | | 3 |项目C | NULL | 30.00 | | 4 |项目D | 1 | 40.00 | | 5 |项目E | 2 | 50.00 | + ---- + -------- + ---------- + ------- +

    可能的解决方案1:使用 UNION ALL

    SELECT id,name,group_id,price FROM items WHERE group_id IS NULL UNION ALL SELECT id ,name,MIN(price)FROM items WHERE group_id IS NOT NULL GROUP BY group_id; / *解释* / + ---- + -------------- + ------------ + ------ + -------------------- + -------------------- + --------- ------- + ------ + + ------------------------- --------------------- + | id | select_type |表| |键入| possible_keys |键| key_len | ref |行|额外| + ---- + -------------- + ------------ + ------ + ----- --------------- + -------------------- + --------- + --- ---- + ------ + -------------------------------------- -------- + | 1 | PRIMARY |物品| ref | items_group_id_idx | items_group_id_idx | 5 | const | 3 |使用where | | 2 | UNION |物品| ALL | items_group_id_idx | NULL | NULL | NULL | 7 |在哪里使用;使用临时;使用filesort | | NULL |联合结果| < union1,2> | ALL | NULL | NULL | NULL | NULL | NULL | | + ---- + -------------- + ------------ + ------ + ----- --------------- + -------------------- + --------- + --- ---- + ------ + -------------------------------------- -------- +

    然而,有两个查询是不可取的,因为会有更复杂的条件在 WHERE 子句中,我需要对最终结果进行排序。 可能的解决方案2: GROUP BY 关于表达式( SELECT id,name,group_id ,MIN(price)FROM items GROUP BY CASE WHEN group_id IS NOT NULL THEN group_id ELSE RAND()END; / *解释* / + ---- + ------------- + ------- + ----- - + --------------- + ------ + --------- + ------ + ------ + - -------------------------------- + | id | select_type |表| |键入| possible_keys |键| key_len | ref |行|额外| + ---- + ------------- + ------- + ------ + ----------- ---- + ------ + --------- + ------ + ------ + -------------- ------------------- + | 1 | SIMPLE |物品| ALL | NULL | NULL | NULL | NULL | 7 |使用临时;使用filesort | + ---- + ------------- + ------- + ------ + ----------- ---- + ------ + --------- + ------ + ------ + -------------- ------------------- +

    解决方案2似乎更快更简单,但我想知道是否有更好的性能方法。

    / p>

    根据 解决方案

    根据此答案 @ axiac ,在兼容性和性能方面更好的解决方案如下所示。

    它也在 SQL反模式手册,第15章:不明确的组合。为了提高性能,组合索引也被添加到( group_id,price,id)。

    解决方案

    SELECT a.id,a.name,a.group_id,a.price FROM items a LEFT JOIN项目b ON a.group_id = b.group_id AND(a.price> b.price OR(a.price = b.price和a.id> b.id)) WHERE b.price为NULL;

    请参阅

    偶然的作为副作用,这个查询在我需要包含 ALL包含 group_id 的记录等于 NULL AND $ b

    结果

    + ---- + -------- + ---------- + - ------ + | id |名称| group_id |价格| + ---- + -------- + ---------- + ------- + | 1 |项目A | NULL | 10.00 | | 2 |项目B | NULL | 20.00 | | 3 |项目C | NULL | 30.00 | | 4 |项目D | 1 | 40.00 | | 5 |项目E | 2 | 50.00 | + ---- + -------- + ---------- + ------- +

    EXPLAIN

    + ---- + ------------- + ------- + ------ + - ------------------------------ + ------------------- - + --------- + ---------------------------- + ------ + - ------------------------ + | id | select_type |表| |键入| possible_keys |键| key_len | ref |行|额外| + ---- + ------------- + ------- + ------ + ----------- -------------------- + -------------------- + -------- - + ---------------------------- + ------ + ------------ -------------- + | 1 | SIMPLE | a | ALL | NULL | NULL | NULL | NULL | 7 | | | 1 | SIMPLE | b | ref | PRIMARY,ID,items_group_id_idx | items_group_id_idx | 5 | agi_development.a.group_id | 1 |在哪里使用;使用index | + ---- + ------------- + ------- + ------ + ----------- -------------------- + -------------------- + -------- - + ---------------------------- + ------ + ------------ -------------- +

    SCHEMA

    I have the following set-up in MySQL database:

    CREATE TABLE items ( id SERIAL, name VARCHAR(100), group_id INT, price DECIMAL(10,2), KEY items_group_id_idx (group_id), PRIMARY KEY (id) ); INSERT INTO items VALUES (1, 'Item A', NULL, 10), (2, 'Item B', NULL, 20), (3, 'Item C', NULL, 30), (4, 'Item D', 1, 40), (5, 'Item E', 2, 50), (6, 'Item F', 2, 60), (7, 'Item G', 2, 70);

    PROBLEM

    I need to select:

    • All items with group_id that has NULL value, and
    • One item from each group identified by group_id having the lowest price.

    EXPECTED RESULTS

    +----+--------+----------+-------+ | id | name | group_id | price | +----+--------+----------+-------+ | 1 | Item A | NULL | 10.00 | | 2 | Item B | NULL | 20.00 | | 3 | Item C | NULL | 30.00 | | 4 | Item D | 1 | 40.00 | | 5 | Item E | 2 | 50.00 | +----+--------+----------+-------+

    POSSIBLE SOLUTION 1: Two queries with UNION ALL

    SELECT id, name, group_id, price FROM items WHERE group_id IS NULL UNION ALL SELECT id, name, MIN(price) FROM items WHERE group_id IS NOT NULL GROUP BY group_id; /* EXPLAIN */ +----+--------------+------------+------+--------------------+--------------------+---------+-------+------+----------------------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+--------------+------------+------+--------------------+--------------------+---------+-------+------+----------------------------------------------+ | 1 | PRIMARY | items | ref | items_group_id_idx | items_group_id_idx | 5 | const | 3 | Using where | | 2 | UNION | items | ALL | items_group_id_idx | NULL | NULL | NULL | 7 | Using where; Using temporary; Using filesort | | NULL | UNION RESULT | <union1,2> | ALL | NULL | NULL | NULL | NULL | NULL | | +----+--------------+------------+------+--------------------+--------------------+---------+-------+------+----------------------------------------------+

    However it is undesirable to have two queries since there will be more complex condition in WHERE clause and I would need to sort the final results.

    POSSIBLE SOLUTION 2: GROUP BY on expression (reference)

    SELECT id, name, group_id, MIN(price) FROM items GROUP BY CASE WHEN group_id IS NOT NULL THEN group_id ELSE RAND() END; /* EXPLAIN */ +----+-------------+-------+------+---------------+------+---------+------+------+---------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+---------------------------------+ | 1 | SIMPLE | items | ALL | NULL | NULL | NULL | NULL | 7 | Using temporary; Using filesort | +----+-------------+-------+------+---------------+------+---------+------+------+---------------------------------+

    Solution 2 seems to be faster and simple to use but I'm wondering whether there is a better approach in terms of performance.

    UPDATE:

    According to documentation referenced by @axiac, this query is illegal in SQL92 and earlier and may work in MySQL only.

    解决方案

    According to this answer by @axiac, better solution in terms of compatibility and performance is shown below.

    It is also explained in SQL Antipatterns book, Chapter 15: Ambiguous Groups.

    To improve performance, combined index is also added for (group_id, price, id).

    SOLUTION

    SELECT a.id, a.name, a.group_id, a.price FROM items a LEFT JOIN items b ON a.group_id = b.group_id AND (a.price > b.price OR (a.price = b.price and a.id > b.id)) WHERE b.price is NULL;

    See explanation on how it works for more details.

    By accident as a side-effect this query works in my case where I needed to include ALL records with group_id equals to NULL AND one item from each group with the lowest price.

    RESULT

    +----+--------+----------+-------+ | id | name | group_id | price | +----+--------+----------+-------+ | 1 | Item A | NULL | 10.00 | | 2 | Item B | NULL | 20.00 | | 3 | Item C | NULL | 30.00 | | 4 | Item D | 1 | 40.00 | | 5 | Item E | 2 | 50.00 | +----+--------+----------+-------+

    EXPLAIN

    +----+-------------+-------+------+-------------------------------+--------------------+---------+----------------------------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+-------------------------------+--------------------+---------+----------------------------+------+--------------------------+ | 1 | SIMPLE | a | ALL | NULL | NULL | NULL | NULL | 7 | | | 1 | SIMPLE | b | ref | PRIMARY,id,items_group_id_idx | items_group_id_idx | 5 | agi_development.a.group_id | 1 | Using where; Using index | +----+-------------+-------+------+-------------------------------+--------------------+---------+----------------------------+------+--------------------------+

    更多推荐

    使用GROUP BY对特定行进行分组

    本文发布于:2023-10-24 08:27:41,感谢您对本站的认可!
    版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
    本文标签:GROUP

    发布评论

    评论列表 (有 0 条评论)
    草根站长

    >www.elefans.com

    编程频道|电子爱好者 - 技术资讯及电子产品介绍!