使用SQLite进行昂贵的子查询调整

时间:2017-02-13 12:03:53

标签: performance sqlite

我正在使用sqlite开发一个小型媒体/文件管理实用程序,以满足其持久存储需求。我有一个文件表:

CREATE TABLE file
  ( file_id       INTEGER PRIMARY KEY AUTOINCREMENT
  , file_sha1     BINARY(20)
  , file_name     TEXT NOT NULL UNIQUE
  , file_size     INTEGER NOT NULL
  , file_mime     TEXT NOT NULL
  , file_add_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
  );

还有一张专辑

CREATE TABLE album
  ( album_id      INTEGER PRIMARY KEY AUTOINCREMENT
  , album_name    TEXT
  , album_poster  INTEGER
  , album_created TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
  , FOREIGN KEY (album_poster) REFERENCES file(file_id)
  );

可以分配哪些文件

CREATE TABLE album_file
  ( album_id  INTEGER NOT NULL
  , file_id   INTEGER NOT NULL
  , PRIMARY KEY (album_id, file_id)
  , FOREIGN KEY (album_id) REFERENCES album(album_id)
  , FOREIGN KEY (file_id) REFERENCES file(file_id)
  );

CREATE INDEX file_to_album ON album_file(file_id, album_id);

部分功能是列出专辑,曝光

  • 专辑ID,
  • 专辑的名称,
  • 该专辑的海报图片和
  • 相册中的文件数

目前使用此查询:

SELECT a.album_id, a.album_name, 
  COALESCE(
    a.album_poster,
    (SELECT file_id FROM file
     NATURAL JOIN album_file af
     WHERE af.album_id = a.album_id
     ORDER BY file.file_name LIMIT 1)),
  (SELECT COUNT(file_id) AS file_count
   FROM album_file WHERE album_id = a.album_id)
FROM album a
ORDER BY album_name ASC

该查询唯一“棘手”的部分是album_poster列可能为null,在这种情况下,COALESCE语句用于将相册中的第一个文件作为“默认海报”返回。

目前在album_file表中有~260000个文件,~2600个专辑和~250000个条目,此查询需要10秒以上,这会带来不太好的用户体验。这是查询计划:

0|0|0|SCAN TABLE album AS a
0|0|0|EXECUTE CORRELATED SCALAR SUBQUERY 1
1|0|1|SEARCH TABLE album_file AS af USING COVERING INDEX album_to_file (album_id=?)
1|1|0|SEARCH TABLE file USING INTEGER PRIMARY KEY (rowid=?)
1|0|0|USE TEMP B-TREE FOR ORDER BY
0|0|0|EXECUTE CORRELATED SCALAR SUBQUERY 2
2|0|0|SEARCH TABLE album_file USING COVERING INDEX album_to_file (album_id=?)

仅使用COALESCE替换a.album_poster语句,牺牲自动海报功能,将查询时间缩短到几毫秒:

0|0|0|SCAN TABLE album AS a
0|0|0|EXECUTE CORRELATED SCALAR SUBQUERY 1
1|0|0|SEARCH TABLE album_file USING COVERING INDEX album_to_file (album_id=?)
0|0|0|USE TEMP B-TREE FOR ORDER BY

我不明白的是,将专辑列表限制为1或1000行没有任何区别。似乎SQLite正在对所有专辑中的“默认”海报进行昂贵的子查询,只是在最终将结果集减少到查询指定的LIMIT时丢弃大部分结果。

我能做些什么来使原始查询大大加快,特别是考虑到我通常只查询所有行的一小部分(使用LIMIT)进行显示?

0 个答案:

没有答案