查询限制约束传播到子查询

query limit constraint propagation into subquery

这里是我精简的情况

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 其目的是为 ID (i)

的 select 列表打印出 table t1
select d from t1 where i in (3,4) limit 4;
d
----
aa3
aa4

已生成 ID 集,因此我可以得到

select d from t1 where i in (3,4,1,6,7) limit 4;
d
----
aa1
aa3
aa4
aa6

有时 ID 集是像这样的内部 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 可以生成一个大的 ID 列表,外部 select 将受到其限制约束的影响。

我的问题是,查询优化器是否检测到这个外部限制约束并将其传播到内部 select?

如果答案是否定的,那么我应该加倍努力,我的查询生成器必须在内部 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,但这超出了我的知识范围,无法从那里回答。

SQLite 没有 optimization 可以将 LIMIT 子句移动到子查询中,并且子查询扁平化不适用于 IN 子句。

这已通过 EXPLAIN 确认(地址 22 在外循环中):

sqlite> explain select d from t1 where i in (select i from t2 where e>'b') limit 4;
addr  opcode         p1    p2    p3    p4             p5  comment
----  -------------  ----  ----  ----  -------------  --  -------------
0     Init           0     26    0                    00  Start at 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       Once           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       Rewind         1     15    0                    00
9         Column         1     1     2                    00  r[2]=t2.e
10        Le             3     14    2     (BINARY)       52  if r[3]<=r[2] goto 14
11        Column         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      Next           1     9     0                    01
15      Close          1     0     0                    00
16      Column         0     0     2                    00  r[2]=t1.i
17      IsNull         2     23    0                    00  if r[2]==NULL goto 23
18      Affinity       2     1     0     C              00  affinity(r[2])
19      NotFound       3     23    2     1              00  key=r[2]; end IN expr
20      Column         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 goto 24
23    Next           0     4     0                    01
24    Close          0     0     0                    00
25    Halt           0     0     0                    00
26    Transaction    0     0     2     0              01  usesStmtJournal=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    Goto           0     1     0                    00

请注意,没有 ORDER BY 子句的 LIMIT 可能不是很有用,除非您真的想要一个随机的行样本。