Распространение ограничения запроса на подзапрос
Здесь моя упрощенная ситуация
create table t1 (i integer, d text);
insert into t1 values (0,'aa0');
insert into t1 values (1,'aa1');
insert into t1 values (2,'aa2');
insert into t1 values (3,'aa3');
insert into t1 values (4,'aa4');
insert into t1 values (5,'aa5');
insert into t1 values (6,'aa6');
insert into t1 values (7,'aa7');
insert into t1 values (8,'aa8');
insert into t1 values (9,'aa9');
create table t2 (i integer, e text);
insert into t2 values (0,'aa0');
insert into t2 values (1,'ba1');
insert into t2 values (2,'aa2');
insert into t2 values (3,'ba3');
insert into t2 values (4,'aa4');
insert into t2 values (5,'ba5');
insert into t2 values (6,'aa6');
insert into t2 values (7,'ba7');
insert into t2 values (8,'aa8');
insert into t2 values (9,'ba9');
Затем у меня есть внешний SELECT, целью которого является распечатать таблицу t1 для списка выбора идентификатора (i)
select d from t1 where i in (3,4) limit 4;
d
----
aa3
aa4
Идентификационный набор сгенерирован, поэтому я могу получить
select d from t1 where i in (3,4,1,6,7) limit 4;
d
----
aa1
aa3
aa4
aa6
Иногда набор идентификаторов является результатом внутреннего подзапроса SELECT, подобного этому
select d from t1 where i in (select i from t2 where e>'b') limit 4;
d
----
aa1
aa3
aa5
aa7
В моем реальном случае t1 и t2 большие, и внутренний SELECT может генерировать большой список идентификаторов, который внешний выбор обнуляет со своим ограничением ограничения.
Мой вопрос заключается в том, определяет ли оптимизатор запросов это ограничение внешнего лимита и распространяет его во внутренний выбор?
Если ответ НЕТ, то мне нужно пройти лишнюю милю, и мой генератор запросов должен явно переместить ограничение limit во внутреннем SELECT следующим образом.
select d from t1 where i in (select i from t2 where e>'b' limit 4);
d
----
aa1
aa3
aa5
aa7
Перед тем, как спросить, я посмотрел EXPLAIN и EXPLAIN QUERY PLAN, но это вне моего понимания и не смог ответить оттуда.
1 ответ
SQLite не имеет оптимизации, которая могла бы переместить предложение LIMIT в подзапрос, и уплощение подзапроса не применяется к предложениям IN.
Это подтверждается с помощью EXPLAIN (адрес 22 находится во внешнем цикле):
sqlite> объяснение, выберите d из t1, где i in (выберите i из t2, где e>'b'), предел 4; addr код операции p1 p2 p3 p4 p5 комментарий --- ------------- ---- ---- ---- ------------- -- ------------- 0 Init 0 26 0 00 Начало в 26 1 Integer 4 1 0 00 r[1]=4; LIMIT counter 2 OpenRead 0 2 0 2 00 root=2 iDb=0; t1 3 Rewind 0 24 0 00 4 Noop 0 0 0 00 begin IN expr 5 Один раз 0 16 0 00 6 OpenEphemeral 3 1 0 k(1,B) 00 nColumn=1 7 OpenRead 1 3 0 2 00 root=3 iDb=0; t2 8 Перемотка назад 1 15 0 00 9 Столбец 1 1 2 00 r[2]=t2.e 10 Le 3 14 2 (BINARY) 52, если r[3]<=r[2] перейти к 14 11 Столбец 1 0 4 00 r[4]=t2.i 12 MakeRecord 4 1 5 C 00 r[5]=mkrec(r[4]) 13 IdxInsert 3 5 0 00 key=r[5] 14 Далее 1 9 0 01 15 Закрыть 1 0 0 00 16 Столбец 0 0 2 00 r[2]=t1.i 17 IsNull 2 23 0 00, если r[2]==NULL goto 23 18 Сродство 2 1 0 C 00 сродство (r[2]) 19 NotFound 3 23 2 1 Ключ 00 =r[2]; end IN expr 20 Столбец 0 1 6 00 r[6]=t1.d 21 ResultRow 6 1 0 00 output=r[6] 22 DecrJumpZero 1 24 0 00 if (--r[1])==0 перейти к 24 23 Далее 0 4 0 01 24 Закрыть 0 0 0 00 25 Остановить 0 0 0 00 26 Транзакция 0 0 2 0 01 использует StmtJournal=0 27 TableLock 0 2 0 t1 00 iDb=0 root=2 write=0 28 TableLock 0 3 0 t2 00 iDb=0 root=3 write=0 29 String8 0 3 0 b 00 r[3]='b' 30 Перейти к 0 1 0 00
Обратите внимание, что LIMIT без предложения ORDER BY, вероятно, не очень полезен, если вы на самом деле не хотите случайную выборку строк.