查询限制约束传播到子查询
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 可能不是很有用,除非您真的想要一个随机的行样本。
这里是我精简的情况
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 t1select 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 可能不是很有用,除非您真的想要一个随机的行样本。