Как оптимизировать SQL, когда найти максимальную запись каждой группы, пока таблица велика? - PullRequest
1 голос
/ 15 мая 2019

У меня есть таблица, которая содержит около 1 миллиона записей.Я хочу найти максимальную запись каждой группы.Вот мой sql:

SELECT * 
FROM t 
WHERE id IN (SELECT max(id) AS id 
             FROM t 
             WHERE a = 'some' AND b = 0 
             GROUP BY c, d);

Таблица объявляется следующим образом.

CREATE TABLE `t` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
  `a` varchar(32) NOT NULL COMMENT 'a',
  `b` tinyint(3) unsigned NOT NULL COMMENT 'b',
  `c` bigint(20) unsigned NOT NULL COMMENT 'c',
  `d` varchar(32) NOT NULL COMMENT 'd',
  PRIMARY KEY (`id`),
  KEY `idx_c_d` (`c`,`d`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='test table';

У меня есть индекс объединения на c и d.Таким образом, второй оператор (SELECT max(id) AS id FROM t WHERE a = 'some' AND b = 0 GROUP BY c, d) выполняется за 200 мс.Но общая выписка обойдется почти в 6 секунд (результат содержит 5000 строк).Вот explain показывает (некоторые столбцы опущены).

+-------------+-------+-------+---------------+--------+---------+----------+--------------------------+
| select_type | table | type  | possible_keys |  key   |  rows   | filtered |          Extra           |
+-------------+-------+-------+---------------+--------+---------+----------+--------------------------+
| PRIMARY     | t     | ALL   | NULL          | NULL   | 9926024 |   100.00 | Using where              |
| SUBQUERY    | t     | index | idx_1         | idex_1 | 9926024 |     1.00 | Using where; Using index |
+-------------+-------+-------+---------------+--------+---------+----------+--------------------------+

Ответы [ 4 ]

0 голосов
/ 17 мая 2019

Все разные способы "skin-a-cat", но здесь немного по-другому ... Так как вы ищете IN, я бы переместил этот запрос в переднюю позицию.Кроме того, это МОЖЕТ помочь при использовании специфического для MySQL ключевого слова «STRAIGHT_JOIN», говорящего MySQL о том, что нужно делать в указанном порядке.Опять же, это МОЖЕТ помочь

SELECT 
      T.* 
   FROM 
      (SELECT max(id) AS id 
          FROM t 
          WHERE b = 0 
             AND a = 'some' 
          GROUP BY c, d) PQ
      JOIN T
         on PQ.ID = T.ID

У меня также будет индекс, в частности, в порядке

(b, a, c, d, id )

Очевидно, сохраните первичный ключ ID, и при использовании STRAIGHT_JOIN, будет

SELECT STRAIGHT_JOIN 
      T.* ( ... rest of query) 
0 голосов
/ 15 мая 2019

Избежание необходимости подзапроса

SELECT t1.*
FROM t t1
LEFT OUTER JOIN t t2
ON t1.c = t2.c
AND t1.d = t2.d
AND t1.id < t2.id
AND t2.id IS NULL
AND t2.a = 'some' 
AND t2.b = 0 
0 голосов
/ 15 мая 2019

Я рекомендую использовать коррелированный подзапрос:

SELECT t.* 
FROM t 
WHERE t.id (SELECT MAX(t2.id)
            FROM t t2
            WHERE t2.c = t.c AND t2.d = t.d AND
                  t2.a = 'some' AND t2.b = 0
           );

Предполагается, что id уникален в таблице.

Для производительности вам нужен индекс для (c, d, a, b, id).

0 голосов
/ 15 мая 2019

вы можете попробовать, используя связанный подзапрос и создав индекс в column c and d

SELECT t1.* FROM table_name t1 
WHERE id = (SELECT max(id) AS id FROM table_name t2 where
             t1.c=t2.c and t1.d=t2.d
            ) and t1.a = 'some' AND t1.b = 0 
...