(根据 cmets 和后续测试更新答案。)
你的问题的实际答案
如果我们有 100 万行,如何确保查询 ... 已优化,以便仅在易于测试的第二个条件已经为真时才测试第一个条件(CPU 昂贵)?
取决于
- WHERE 子句中的实际条件,以及
- SQLite 查询优化器在估算这些条件的成本方面有多聪明。
一个简单的测试应该告诉您您的查询是否会充分“优化”以满足您的需求。好消息是 SQLite 将首先执行简单(便宜)的条件,至少在某些情况下是这样。
对于测试表“mytable”
CREATE TABLE mytable (
description TEXT(50) NOT NULL,
column2 INTEGER NOT NULL,
CONSTRAINT mytable_PK PRIMARY KEY (column2)
);
包含一百万行
description column2
----------- -------
row000000 0
row000001 1
row000002 2
...
row999999 999999
Python 测试代码
import sqlite3
import time
log_file_spec = r'C:\Users\Gord\Desktop\log_file.txt'
def myfunc(thing):
with open(log_file_spec, 'a') as log:
log.write('HODOR\n')
return(int(thing[-6:]))
with open(log_file_spec, 'w'):
pass # just empty the file
cnxn = sqlite3.connect(r'C:\__tmp\SQLite\test.sqlite')
cnxn.create_function("myfunction", 1, myfunc)
crsr = cnxn.cursor()
t0 = time.time()
sql = """\
SELECT COUNT(*) AS n FROM mytable
WHERE myfunction(description) < 500 AND column2 < 1000
"""
crsr.execute(sql)
num_rows = crsr.fetchone()[0]
print(f"{num_rows} rows found in {(time.time() - t0):.1f} seconds")
cnxn.close()
返回
500 rows found in 1.2 seconds
并计算我们看到的 log_file.txt 中的行数
C:\Users\Gord>find /C "HODOR" Desktop\log_file.txt
---------- DESKTOP\LOG_FILE.TXT: 1000
表示我们的函数只被调用了一千次,而不是一百万次。 SQLite 明确地首先应用了column2 < 1000,然后对第一个条件的行子集应用了myfunction(description) < 500 条件。
(原来的“即兴”答案。)
您的问题的实际答案取决于查询优化器的聪明程度。一个简单的测试应该会告诉您您的查询是否会充分“优化”以满足您的需求。
但是,如果您的测试发现您的原始方法太慢,您确实有几个选择:
选项 1:尝试“先”进行简单比较
更改顺序可能会影响查询计划,例如
... WHERE <easy_condition> AND <expensive_condition>
可能会比
更快
... WHERE <expensive_condition> AND <easy_condition>
选项 2:尝试使用子查询强制排序
同样,取决于查询优化器的聪明程度
SELECT easy.*
FROM
(SELECT * FROM mytable WHERE column2 < 1000) easy
WHERE myfunction(easy.description) < 500
可能首先应用廉价条件,然后在结果行子集上应用昂贵条件。 (但是,有评论表明 SQLite 过于复杂,不会被这种策略所欺骗。)