Создайте следующие индексы:
CREATE INDEX ix_big_1_2_a ON tab_big (id1, id2, id_a)
CREATE UNIQUE INDEX ux_small_b_2_1 ON tab_small (id_b, id2, id1)
и попробуйте это:
SELECT DISTINCT
a.id_a
FROM tab_small b
JOIN tab_big a
ON (a.id1, a.id2) = (b.id1, b.id2)
WHERE b.id_b = 2
AND a.id_a NOT IN
(
SELECT id1
FROM tab_small b1 /* FORCE INDEX (PRIMARY) */
WHERE b1.id_b = 2
)
AND a.id_a NOT IN
(
SELECT id2
FROM tab_small b2 /* FORCE INDEX (ux_small_b_2_1) */
WHERE b2.id_b = 2
)
, который производит этот план запроса:
1, 'PRIMARY', 'b', 'ref', 'PRIMARY,ux_small_b_2_1', 'PRIMARY', '4', 'const', 1, 100.00, 'Using index; Using temporary'
1, 'PRIMARY', 'a', 'ref', 'ix_big_1_2', 'ix_big_1_2', '8', 'test.b.id1,test.b.id2', 2, 100.00, 'Using where'
3, 'DEPENDENT SUBQUERY', 'b2', 'ref', 'ux_small_b_2_1', 'ux_small_b_2_1', '8', 'const,func', 1, 100.00, 'Using index'
2, 'DEPENDENT SUBQUERY', 'b1', 'ref', 'PRIMARY', 'PRIMARY', '8', 'const,func', 1, 100.00, 'Using index'
Это не так эффективно, как могло бы быть, но я ожидаю, что это будет быстрее, чем ваш запрос.
Я закомментировал операторы FORCE INDEX
, но вам может понадобиться раскомментировать их, если оптимизатор не выберет эти индексы.
Все было бы намного проще, если бы MySQL
был способен сделать FULL OUTER JOIN
, используя MERGE
, но это не так.
Обновление:
Судя по вашей статистике, этот запрос будет более эффективным:
SELECT id_a
FROM (
SELECT DISTINCT id_a
FROM tab_big ad
) a
WHERE id_a NOT IN
(
SELECT id1
FROM tab_small b1 FORCE INDEX (PRIMARY)
WHERE b1.id_b = 2
)
AND id_a NOT IN
(
SELECT id2
FROM tab_small b2 FORCE INDEX (ux_small_b_2_1)
WHERE b2.id_b = 2
)
AND EXISTS
(
SELECT NULL
FROM tab_small be
JOIN tab_big ae
ON (ae.id1, ae.id2) = (be.id1, be.id2)
WHERE be.id_b = 2
AND ae.id_a = a.id_a
)
Работает следующим образом:
- Создает список из
DISTINCT id_a
(длина которого 100,000
строк)
- Отфильтровывает значения, присутствующие в подмножестве
- Для каждого значения
id_a
он ищет в подмножестве наличие (id_a, id1, id2)
. Это делается путем итерации подмножества. Поскольку вероятность найти это значение высока, скорее всего, поиск будет успешным в 10
строк или около того с начала подмножества, и EXISTS
вернет этот самый момент.
Скорее всего, потребуется приблизительно 1043 * записи или около того.
Убедитесь, что используется следующий план:
1, 'PRIMARY', '<derived2>', 'ALL', '', '', '', '', 8192, 100.00, 'Using where'
5, 'DEPENDENT SUBQUERY', 'be', 'ref', 'PRIMARY,ux_small_b_2_1', 'PRIMARY', '4', 'const', 1, 100.00, 'Using index'
5, 'DEPENDENT SUBQUERY', 'ae', 'eq_ref', 'PRIMARY,ix_big_1_2', 'PRIMARY', '12', 'a.id_a,test.be.id1,test.be.id2', 1, 100.00, 'Using index'
4, 'DEPENDENT SUBQUERY', 'b2', 'ref', 'ux_small_b_2_1', 'ux_small_b_2_1', '8', 'const,func', 1, 100.00, 'Using index'
3, 'DEPENDENT SUBQUERY', 'b1', 'ref', 'PRIMARY', 'PRIMARY', '8', 'const,func', 1, 100.00, 'Using index'
2, 'DERIVED', 'ad', 'range', '', 'PRIMARY', '4', '', 10, 100.00, 'Using index for group-by'
, самая важная часть - Using index for group-by
в последнем ряду.