我在SSD上有一个~90 MB的SQLite数据库,主要包括消息附件,包括BLOB列内容,用于存储二进制附件数据。
现在我发现了以下查询
SELECT message_id FROM attachments WHERE length(content) IS NULL;
比原始
快500倍(0.5毫秒对250毫秒)SELECT message_id FROM attachments WHERE content IS NULL;
这两个查询都是等价的吗?
其他信息
答案 0 :(得分:4)
在SQLite中,每个列值的长度和类型都存储at the beginning of the row。
这样可以优化length()
和typeof()
函数,以避免加载实际值。
IS NULL运算符没有这样的优化(尽管可以实现它)。
答案 1 :(得分:3)
我制作了一个脚本来对这两个函数进行基准测试除非您的length(x) IS NULL
值大多为NULL
,否则IS NULL
会更快。
length(x) IS NULL
:11.343180236999842 IS NULL
:7.824154090999855 length(x) IS NULL
:15.019244787999924 IS NULL
:7.527420233999919 length(x) IS NULL
:6.184766045999822 import sqlite3
import timeit
conn = sqlite3.connect("test.db")
c = conn.cursor()
c.execute("DROP TABLE IF EXISTS test")
c.execute("CREATE TABLE test (data BLOB)")
for i in range(10000):
# Modify this to change data
if i % 2 == 0:
c.execute("INSERT INTO test(data) VALUES (randomblob(1024))")
else:
c.execute("INSERT INTO test(data) VALUES (NULL)")
def timeit_isnull():
c.execute("SELECT data IS NULL AS dataisnull FROM test")
c.fetchall()
def timeit_lenisnull():
c.execute("SELECT length(data) IS NULL AS dataisnull FROM test")
c.fetchall()
print(timeit.timeit(timeit_isnull, number=1000))
print(timeit.timeit(timeit_lenisnull, number=1000))
:6.448342310000044 {{1}}
答案 2 :(得分:1)
实际上使用LENGTH(some_blob_content)
指示MySQL服务器5.5及更高版本绕过行扫描,这是提高查询性能的原因,因为数据是直接从元数据表中读取的。
编辑:
在部分INSERT
和SELECT
处理期间的SQLite中,数据库中每行的完整内容被编码为单个BLOB 。有关详细信息,请参阅here。
对于字符串值X,
length(X)
函数返回数字 在第一个NULL
之前的X中的字符(不是字节) 字符。由于SQLite字符串通常不包含NULL
字符,length(X)
函数通常返回总数 字符串X中的字符数。对于blob
值X,length(X)
返回blob中的字节数。如果X是NULL
然后length(X)
为NULL。如果X是数字,那么length(X)
返回X的字符串表示的长度。
documentation的上述引用表明,表示blob值,读取它们的长度的过程与MySQL相同,即来自元数据表。