对于BLOB,'length()IS NULL'是否等效且快于'IS NULL'?

时间:2014-11-07 12:51:46

标签: sql sqlite

我在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;

这两个查询都是等价的吗?

其他信息

  1. 除自动索引外,不涉及任何索引。
  2. 它没有缓存。可以从任意数量的SQLite进程以任何顺序无限次地重现结果。

3 个答案:

答案 0 :(得分:4)

在SQLite中,每个列值的长度和类型都存储at the beginning of the row。 这样可以优化length()typeof()函数,以避免加载实际值。

IS NULL运算符没有这样的优化(尽管可以实现它)。

答案 1 :(得分:3)

我制作了一个脚本来对这两个函数进行基准测试除非您的length(x) IS NULL值大多为NULL,否则IS NULL会更快。

结果:

  • 随机数据和null之间交替50%:
    • length(x) IS NULL:11.343180236999842
    • IS NULL:7.824154090999855
  • 完全blob,没有空值:
    • length(x) IS NULL:15.019244787999924
    • IS NULL:7.527420233999919
  • 完全无效,无blob:
    • 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及更高版本绕过行扫描,这是提高查询性能的原因,因为数据是直接从元数据表中读取的。

编辑:

在部分INSERTSELECT处理期间的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相同,即来自元数据表。