Small. Fast. Reliable.
Choose any three.
在SQLite中许多小查询都是有效的

1.执行摘要

2.感知的问题

SQLite的适当使用”页面表示,SQLite网站上的动态页面通常每个执行200条SQL语句。这引起了读者的批评。例子:

对于传统的客户端/服务器数据库引擎(例如MySQL,PostgreSQL或SQL Server),这种批评是有充分根据的。在客户机/服务器数据库中,每个SQL语句都要求从应用程序到数据库服务器再返回到应用程序的消息往返。依次执行200多次往返消息可能会严重影响性能。有时称为“ N + 1查询问题”或“ N + 1选择问题”,它是一种反模式。

3. N + 1查询不是SQLite的问题

但是,SQLite不是客户端/服务器。SQLite数据库在与应用程序相同的进程地址空间中运行。查询不涉及消息往返,仅涉及函数调用。在SQLite中,单个SQL查询的等待时间要短得多。因此,在SQLite中使用大量查询不是问题。

4.每个网页需要200多个SQL语句

SQLite网站上的动态网页主要由Fossil版本控制系统生成。典型的动态页面是时间轴,例如https://www.sqlite.org/src/timeline。时间轴使用的所有SQL的日志如下所示。

日志中的第一组查询是从Fossil数据库的“ config”和“ global_config”表中提取显示选项。然后是一个复杂的查询,该查询提取要在时间轴上显示的所有元素的列表。此“时间轴”查询表明SQLite可以轻松处理涉及多个表,子查询和复杂WHERE子句约束的复杂关系数据库查询,并且可以有效利用索引来以最少的磁盘I / O解决查询。

在单个大的“时间轴”查询之后,每个时间轴元素都有其他查询。Fossil使用“ N + 1查询”模式,而不是尝试在尽可能少的查询中获取所有信息。但这没关系,因为没有不必要的IPC开销。在每个时间轴页面的底部,Fossil大约显示生成该页面所花费的时间。对于50个条目的时间线,延迟通常小于25毫秒。分析表明,这些毫秒数很少花在数据库引擎内部。

在化石中使用N + 1查询模式不会损害应用程序。但是N + 1查询模式确实有好处。例如,创建时间轴查询的代码部分可以与准备显示每个时间轴条目的部分完全分开。这提供了责任分离,有助于使代码保持简单和易于维护。其次,显示所需的信息以及提取该信息所需的查询根据要显示的对象类型而有所不同。签入需要一组查询。票证需要另一组查询。Wiki页面需要其他查询。依此类推。通过按需实现这些查询,并且在处理各种实体的代码部分中,可以进一步分离责任和简化整个代码库。

因此,SQLite能够执行一个或两个大型而复杂的查询,或者它可以执行许多较小而简单的查询。两者都是有效的。应用程序可以使用这两种技术中的一种或两种,这取决于最适合当前情况的技术。

以下是用于生成一个特定时间轴的所有SQL的日志(捕获于2016-09-16):

-sqlite3_open:/home/drh/sqlite/sqlite/.fslckout
PRAGMA foreign_keys =关闭;
在localdb.sqlite_schema中选择sql,其中name =='vfile';
-sqlite3_open:/home/drh/.fossil
PRAGMA foreign_keys =关闭;
从vvar中选择值WHERE name ='repository';
ATTACH DATABASE'/home/drh/www/repos/sqlite.fossil'AS'repository'KEY'';
从配置中选择值WHERE name ='allow-symlinks';
从global_config中选择值WHERE name ='allow-symlinks';
从配置中选择值WHERE name ='aux-schema';
从config WHERE name ='baseurl:http://'中选择1;
从配置中选择值WHERE name ='ip-prefix-terms';
从global_config中选择值WHERE name ='ip-prefix-terms';
从配置中选择值WHERE name ='localauth';
从vvar中选择值WHERE name ='default-user';
从用户WHERE cap像'%s%'中选择uid;
从用户WHERE uid = 1中选择登录名;
从用户WHERE登录='nobody'中选择上限;
从用户WHERE login ='anonymous'中选择上限;
从配置中选择值WHERE name ='public-pages';
从global_config那里选择值WHERE name ='public-pages';
从配置中选择值WHERE name ='header';
从配置中选择值WHERE name ='project-name';
从配置中选择值WHERE name ='th1-setup';
从global_config那里选择值WHERE name ='th1-setup';
从配置中选择值WHERE name ='redirect-to-https';
从global_config那里选择值WHERE name ='redirect-to-https';
从配置中选择值WHERE name ='index-page';
从配置中选择mtime name ='css';
从配置中选择mtime name ='logo-image';
从配置中选择mtime name ='background-image';
如果时间轴不存在,则创建温度表(
  以及INTEGER PRIMARY KEY,
  uuid TEXT,
  时间戳记TEXT,
  评论TEXT,
  用户TEXT,
  伊斯莱夫·布尔(Ileaf BOOLEAN),
  bgcolor TEXT,
  输入TEXT,
  标签列表TEXT,
  tagid INTEGER,
  简短的TEXT,
  按实数排序
)
;
插入或忽略时间线选择
  blob.rid AS blobRid,
  uuid作为uuid,
  datetime(event.mtime,toLocal())作为时间戳,
  合并(ecomment,comment)AS评论,
  合并(euser,user)AS用户,
  blob.rid IN叶AS叶,
  bgcolor AS bgColor,
  event.type AS eventType,
  (SELECT group_concat(substr(tagname,5),',')FROM标签,tagxref
    标记名GLOB'sym- *'和tag.tagid = tagxref.tagid
      AND tagxref.rid = blob.rid AND tagxref.tagtype> 0)AS标签,
  tagid AS tagid,
  简短的AS简短的
  event.mtime AS mtime
 从事件CROSS JOIN Blob
在哪里blob.rid = event.objid
 不存在(从tagxref那里选择1,tagid = 5 AND tagtype> 0 AND rid = blob.rid)
 ORDER BY event.mtime DESC LIMIT 50;
-从配置中选择值WHERE name ='timeline-utc';
从时间轴中选择count(*)etype!='div';
从时间轴中选择分钟(时间戳);
SELECT julianday('2016-09-15 14:54:51',fromLocal());
SELECT EXISTS(从事件CROSS JOIN blob在其中blob.rid = event.objid和mtime <= 2457647.121412037中选择1);
从时间轴中选择max(timestamp);
SELECT julianday('2016-09-24 17:42:43',fromLocal());
SELECT EXISTS(从事件CROSS JOIN blob在其中blob.rid = event.objid和mtime> = 2457656.238009259中选择1);
从配置中选择值WHERE name ='search-ci';
从vvar中选择值WHERE name ='checkout';
从配置中选择值WHERE name ='timeline-max-comment';
从global_config那里选择值WHERE name ='timeline-max-comment';
从配置中选择值WHERE name ='timeline-date-format';
从配置中选择值WHERE name ='timeline-truncate-at-blank';
从global_config那里选择值WHERE name ='timeline-truncate-at-blank';
SELECT * FROM timeline ORDER BY sortby DESC;
从配置中选择值WHERE name ='hash-digits';
从global_config中选择值WHERE name ='hash-digits';
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 68028;
从plink那里选择pid,cid = 68028且pid not in phantom ORDER BY isprim DESC;
从tagxref处选择rid = 68028 AND tagid = 9 AND tagtype> 0;
从配置中选择值WHERE name ='timeline-block-markup';
从配置中选择值WHERE name ='timeline-plaintext';
从配置中选择值WHERE name ='wiki-use-html';
从global_config中选择值WHERE name ='wiki-use-html';
从私有WHERE里选择1 rid = 68028;
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 68026;
从plink那里选择pid,cid = 68026并且pid not in phantom ORDER BY isprim DESC;
从私有WHERE里选择1 rid = 68026;
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 68024;
从plink中选择pid,其中cid = 68024且pid not in phantom ORDER BY isprim DESC;
从私有WHERE中选择1 rid = 68024;
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 68018;
从plink那里选择pid,cid = 68018且pid not in phantom ORDER BY isprim DESC;
从私有WHERE里选择1 rid = 68018;
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 68012;
从plink中选择pid,其中cid = 68012且pid not in phantom ORDER BY isprim DESC;
从私有WHERE中选择1 rid = 68012;
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 68011;
从配置中选择值WHERE name ='details';
从plink那里选择pid,cid = 68011,并且pid not in phantom ORDER BY isprim DESC;
从tagxref中选择rid = 68011 AND tagid = 9 AND tagtype> 0;
从私有WHERE中选择1 rid = 68011;
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 68008;
从plink那里选择pid cid = 68008,并且pid not in phantom ORDER BY isprim DESC;
从私有WHERE中选择1 rid = 68008;
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 68006;
从plink那里选择pid,cid = 68006且pid not in phantom ORDER BY isprim DESC;
从私有WHERE里选择1 rid = 68006;
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 68000;
从plink那里选择pid cid = 68000,并且pid not in phantom ORDER BY isprim DESC;
从私有WHERE rid = 68000中选择1;
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 67997;
从plink那里选择pid cid = 67997,并且pid not in phantom ORDER BY isprim DESC;
从私有WHERE里选择1 rid = 67997;
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 67992;
从plink那里选择pid cid = 67992,并且pid not in phantom ORDER BY isprim DESC;
从私有WHERE里选择1 rid = 67992;
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 67990;
从plink那里选择pid cid = 67990,并且pid not in phantom ORDER BY isprim DESC;
从私有WHERE里选择1 rid = 67990;
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 67989;
从plink那里选择pid,cid = 67989且pid not in phantom ORDER BY isprim DESC;
从私有WHERE里选择1 rid = 67989;
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 67984;
从plink那里选择pid cid = 67984,并且pid not in phantom ORDER BY isprim DESC;
从私有WHERE里选择1 rid = 67984;
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 67983;
从plink那里选择pid cid = 67983,并且pid not in phantom ORDER BY isprim DESC;
从私有WHERE里选择1 rid = 67983;
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 67979;
从plink那里选择pid cid = 67979,并且pid not in phantom ORDER BY isprim DESC;
从私有WHERE中选择1 rid = 67979;
从配置中选择值WHERE name ='ticket-closed-expr';
SELECT status ='Closed'OR status ='Fixed'from ticket WHERE tkt_uuid> ='1ec41379c9c1e400'和tkt_uuid <'1ec41379c9c1e401';
从私有WHERE里选择1 rid = 67980;
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 67977;
从plink中选择pid,其中cid = 67977且pid not in phantom ORDER BY isprim DESC;
SELECT status ='Closed'OR status ='Fixed'from ticket WHERE tkt_uuid> ='1ec41379c9c1e400'和tkt_uuid <'1ec41379c9c1e401';
从私有WHERE中选择1 rid = 67977;
SELECT status ='Closed'OR status ='Fixed'from ticket WHERE tkt_uuid> ='1ec41379c9c1e400'和tkt_uuid <'1ec41379c9c1e401';
从私有WHERE里选择1 rid = 67974;
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 67971;
从plink那里选择pid cid = 67971,并且pid not in phantom ORDER BY isprim DESC;
从私有WHERE里选择1 rid = 67971;
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 67972;
从plink那里选择pid cid = 67972,并且pid not in phantom ORDER BY isprim DESC;
从私有WHERE里选择1 rid = 67972;
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 67969;
从plink那里选择pid cid = 67969,并且pid not in phantom ORDER BY isprim DESC;
从私有WHERE里选择1 rid = 67969;
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 67966;
从plink那里选择pid cid = 67966,并且pid not in phantom ORDER BY isprim DESC;
从私有WHERE里选择1 rid = 67966;
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 67962;
从plink那里选择pid cid = 67962,并且pid not in phantom ORDER BY isprim DESC;
从私有WHERE里选择1 rid = 67962;
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 67960;
从plink那里选择pid cid = 67960,并且pid not in phantom ORDER BY isprim DESC;
从私有WHERE里选择1 rid = 67960;
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 67957;
从plink那里选择pid cid = 67957,并且pid not in phantom ORDER BY isprim DESC;
从私有WHERE里选择1 rid = 67957;
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 67955;
从plink那里选择pid cid = 67955,并且pid not in phantom ORDER BY isprim DESC;
从私有WHERE里选择1 rid = 67955;
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 67953;
从plink那里选择pid cid = 67953,并且pid not in phantom ORDER BY isprim DESC;
SELECT status ='Closed'or status ='Fixed'from ticket WHERE tkt_uuid> ='5990a1bdb4a073'和tkt_uuid <'5990a1bdb4a074';
从blob中SELECT 1从uuid> ='5990a1bdb4a073'和uuid <'5990a1bdb4a074';
从私有WHERE里选择1 rid = 67953;
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 67941;
从plink那里选择pid cid = 67941,并且pid not in phantom ORDER BY isprim DESC;
从私有WHERE里选择1 rid = 67941;
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 67940;
从plink那里选择pid cid = 67940,并且pid not in phantom ORDER BY isprim DESC;
从私有WHERE里选择1 rid = 67940;
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 67938;
从plink那里选择pid cid = 67938,并且pid not in phantom ORDER BY isprim DESC;
从私有WHERE里选择1 rid = 67938;
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 67935;
从plink那里选择pid,cid = 67935,并且pid not in phantom ORDER BY isprim DESC;
从私有WHERE里选择1 rid = 67935;
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 67934;
从plink中选择pid,其中cid = 67934且pid not in phantom ORDER BY isprim DESC;
从私有WHERE里选择1 rid = 67934;
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 67932;
从plink那里选择pid cid = 67932,并且pid not in phantom ORDER BY isprim DESC;
从私有WHERE里选择1 rid = 67932;
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 67930;
从plink那里选择pid cid = 67930,并且pid not in phantom ORDER BY isprim DESC;
从私有WHERE里选择1 rid = 67930;
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 67928;
从plink那里选择pid cid = 67928,并且pid not in phantom ORDER BY isprim DESC;
从tagxref中选择rid = 67928 AND tagid = 9 AND tagtype> 0;
从私有WHERE里选择1 rid = 67928;
SELECT status ='Closed'or status ='Fixed'FROM ticket WHERE tkt_uuid> ='0eab1ac7591f511d'and tkt_uuid <'0eab1ac7591f511e';
从私有WHERE里选择1 rid = 67919;
SELECT status ='Closed'OR status ='Fixed'from ticket WHERE tkt_uuid> ='01874d252ac44861'and tkt_uuid <'01874d252ac44862';
从blob处选择1 uuid> ='01874d252ac44861'和uuid <'01874d252ac44862';
从私有WHERE里选择1 rid = 67918;
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 67916;
从plink那里选择pid cid = 67916,并且pid not in phantom ORDER BY isprim DESC;
SELECT status ='Closed'或status ='Fixed'FROM Ticket WHERE tkt_uuid> ='0eab1ac759'和tkt_uuid <'0eab1ac75:';
从私有WHERE里选择1 rid = 67916;
SELECT status ='Closed'or status ='Fixed'from ticket WHERE tkt_uuid> ='a49bc0a8244feb08'AND tkt_uuid <'a49bc0a8244feb09';
从blob中选择1 uuid> ='a49bc0a8244feb08'和uuid <'a49bc0a8244feb09';
从私有WHERE里选择1 rid = 67914;
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 67913;
从plink那里选择pid cid = 67913,并且pid not in phantom ORDER BY isprim DESC;
SELECT status ='Closed'or status ='Fixed'from ticket WHERE tkt_uuid> ='0eab1ac7591f'AND tkt_uuid <'0eab1ac7591g';
从私有WHERE里选择1 rid = 67913;
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 67911;
从plink那里选择pid cid = 67911,并且pid not in phantom ORDER BY isprim DESC;
从私有WHERE里选择1 rid = 67911;
SELECT status ='Closed'or status ='Fixed'FROM ticket WHERE tkt_uuid> ='0eab1ac7591f511d'and tkt_uuid <'0eab1ac7591f511e';
从私有WHERE里选择1 rid = 67909;
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 67907;
从plink那里选择pid,cid = 67907且pid not in phantom ORDER BY isprim DESC;
从私有WHERE里选择1 rid = 67907;
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 67899;
从plink那里选择pid cid = 67899,并且pid not in phantom ORDER BY isprim DESC;
从私有WHERE中选择1 rid = 67899;
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 67897;
从plink那里选择pid cid = 67897,并且pid not in phantom ORDER BY isprim DESC;
从私有WHERE里选择1 rid = 67897;
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 67895;
从plink那里选择pid,cid = 67895,并且pid not in phantom ORDER BY isprim DESC;
从私有WHERE里选择1 rid = 67895;
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 67893;
从plink那里选择pid cid = 67893,并且pid not in phantom ORDER BY isprim DESC;
从私有WHERE里选择1 rid = 67893;
从tagxref中选择值,其中tagid = 8 AND tagtype> 0 AND rid = 67891;
从plink那里选择pid cid = 67891,并且pid not in phantom ORDER BY isprim DESC;
从私有WHERE里选择1 rid = 67891;
从plink中选择count(*)
 哪里pid = 67928和isprim
   AND coalesce((从tagxref选择值
                  在哪里tagid = 8 AND rid = plink.pid),'trunk')
      = coalesce(((从tagxref选择值
                  在哪里tagid = 8 AND rid = plink.cid),'trunk')
;
从plink中选择count(*)
 pid = 68011和isprim
   AND coalesce((从tagxref选择值
                  在哪里tagid = 8 AND rid = plink.pid),'trunk')
      = coalesce(((从tagxref选择值
                  在哪里tagid = 8 AND rid = plink.cid),'trunk')
;
从plink中选择count(*)
 pid = 68028和isprim
   AND coalesce((从tagxref选择值
                  在哪里tagid = 8 AND rid = plink.pid),'trunk')
      = coalesce(((从tagxref选择值
                  在哪里tagid = 8 AND rid = plink.cid),'trunk')
;
从配置中选择值WHERE name ='show-version-diffs';
从配置中选择值WHERE name ='adunit-omit-if-admin';
从global_config那里选择值WHERE name ='adunit-omit-if-admin';
从配置中选择值WHERE name ='adunit-omit-if-user';
从global_config那里选择值WHERE name ='adunit-omit-if-user';
从配置中选择值WHERE name ='adunit';
从global_config WHERE name ='adunit'中选择值;
从配置中选择值WHERE name ='auto-hyperlink-delay';
从global_config中选择值WHERE name ='auto-hyperlink-delay';
从配置中选择值WHERE name ='footer';
PRAGMA database_list;
PRAGMA database_list;
PRAGMA localdb.freelist_count;
PRAGMA localdb.page_count;