施玛
我在MySQL数据库中进行了以下设置:
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);
问题
我需要选择: *具有group_id该NULL值的 *所有 项目都具有价值, 并且 *每个组中的 *一项group_id以 最低的 价格确定。
我需要选择:
group_id
NULL
预期成绩
+----+--------+----------+-------+ | 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 | +----+--------+----------+-------+
可能的解决方案1: 两个查询UNION ALL
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 | | +----+--------------+------------+------+--------------------+--------------------+---------+-------+------+----------------------------------------------+
但是,不希望有两个查询,因为WHERE子句中的条件会更复杂,而且我需要对最终结果进行排序。
WHERE
可能的解决方案2: GROUP BY关于表达式(参考)
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; /* 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 | +----+-------------+-------+------+---------------+------+---------+------+------+---------------------------------+
解决方案2似乎更快速,更易于使用,但是我想知道在性能方面是否有更好的方法。
更新 :
根据@axiac引用的文档,此查询在SQL92和更早版本中是非法的,并且仅在MySQL中有效。
为了提高性能,还为添加了组合索引(group_id, price, id)。
(group_id, price, id)
解决方案
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;
作为偶然的副作用,此查询在我需要包含与 AND 相等的 AND的 所有 记录中包含最低价格的每个组中的 一项的情况下起作用。group_id``NULL
group_id``NULL
结果
解释
+----+-------------+-------+------+-------------------------------+--------------------+---------+----------------------------+------+--------------------------+ | 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 | +----+-------------+-------+------+-------------------------------+--------------------+---------+----------------------------+------+--------------------------+