“值”是单个数字,字符串,BLOB或NULL。有时使用限定名称“标量值”来强调仅涉及一个数量。
“行值”是两个或多个标量值的有序列表。换句话说,“行值”是向量或元组。
行值的“大小”是该行值包含的标量值的数量。行值的大小始终至少为2。只有一列的行值只是一个标量值。没有列的行值是语法错误。
SQLite允许以两种方式表示行值:
SQLite可以在两个上下文中使用行值:
下面的示例说明了行值的语法以及可以使用行值的环境。
通过从左到右查看组成标量值来比较两行值。NULL表示“未知”。如果可以通过用替代值代替组成NULL来使结果为真或假,则比较的总体结果为NULL。以下查询演示了一些行值比较:
选择 (1,2,3)=(1,2,3)-1 (1,2,3)=(1,NULL,3),-NULL (1,2,3)=(1,NULL,4),-0 (1,2,3)<(2,3,4),-1 (1,2,3)<(1,2,4),-1 (1,2,3)<(1,3,NULL),-1 (1,2,3)<(1,2,NULL),-NULL (1,3,5)<(1,2,NULL),-0 (1,2,NULL)IS(1,2,NULL); -1
“(1,2,3)=(1,NULL,3)”的结果为NULL,因为如果替换为NULL→2,则结果可能为true;如果替换为NULL→9,则结果为false。“(1,2,3)=(1,NULL,4)”的结果不是NULL,因为没有组成NULL的替换将使表达式为true,因为在第三列中3永远不会等于4。
上一个示例中的任何行值都可以由返回三列的子查询替换,并且将得到相同的答案。例如:
创建表t1(a,b,c); 插入t1(a,b,c)值(1,2,3); SELECT(1,2,3)=(选择*来自t1); -1
对于行值IN运算符,左侧(以下称“ LHS”)可以是带括号的值列表,也可以是具有多列的子查询。但是右侧(以下简称“ RHS”)必须是子查询表达式。
创建表t2(x,y,z); 插入t2(x,y,z)值(1,2,3),(2,3,4),(1,NULL,5); 选择 (1,2,3)IN(选择*来自t2),-1 (7,8,9)IN(SELECT * FROM t2),-0 (1,3,5)IN(选择*来自t2); - 空值
行值也可以在UPDATE语句的SET子句中使用。LHS必须是列名的列表。RHS可以是任何行值。例如:
更新选项卡3 SET(a,b,c)=(选择x,y,z 从选项卡4 在哪里tab4.w = tab3.d) tab3.e在55和66之间;
假设应用程序要在一个滚动窗口中按姓氏,名字的字母顺序显示联系人列表,一次只能显示7个联系人。将滚动窗口初始化为前7个条目很容易:
选择*从联系人 ORDER BY姓氏,名字 LIMIT 7;
当用户向下滚动时,应用程序需要找到第二组7个条目。一种方法是使用OFFSET子句:
选择*从联系人 ORDER BY姓氏,名字 LIMIT 7 OFFSET 7;
偏移量给出正确的答案。但是,偏移需要的时间与偏移值成正比。“ LIMIT x OFFSET y”真正发生的是,SQLite将查询计算为“ LIMIT x + y”,并丢弃了前y个值,而没有将它们返回给应用程序。因此,当窗口向下滚动到长列表的底部,并且y值越来越大时,连续的偏移量计算将花费越来越多的时间。
一种更有效的方法是记住当前显示的最后一个条目,然后在WHERE子句中使用行值比较:
选择*从联系人 哪里(姓,名)>(?1,?2) ORDER BY姓氏,名字 LIMIT 7;
如果前一个屏幕的底部行上的姓和名绑定到?1和?2,则上面的查询将计算接下来的7行。而且,假设有一个合适的索引,它会非常高效地执行-比OFFSET效率要高得多。
在数据库表中存储日期的通常方法是作为单个字段,如UNIX时间戳,朱利安日数或ISO-8601日期字符串。但是某些应用程序商店的日期分别作为年,月和日的三个字段。
创建表信息( 年INT,-4位数字的年份 INT月,-1到12 第INT天,-1到31 other_stuff BLOB-等等等等 );
以这种方式存储日期时,行值比较提供了一种比较日期的便捷方法:
选择*从信息 (2015,9,12)和(2016,9,12)之间的(年,月,日);
假设我们想知道任何产品的订单号,产品编号和数量,其中产品编号和数量与订单编号365中任何项目的产品编号和数量相匹配:
SELECT ordid,prodid,qty 来自项目 WHERE(prodid,qty)IN(SELECT prodid,qty) 来自项目 在哪里ordid = 365);
上面的查询可以重写为联接,而无需使用行值:
选择t1.ordid,t1.prodid,t1.qty 从项目AS t1,项目AS t2 在哪里t1.prodid = t2.prodid AND t1.qty = t2.qty AND t2.ordid = 365;
因为可以在不使用行值的情况下编写相同的查询,所以行值不提供新功能。但是,许多开发人员表示,行值格式更易于读取,写入和调试。
即使采用JOIN形式,也可以通过使用行值来使查询更清晰:
选择t1.ordid,t1.prodid,t1.qty 从项目AS t1,项目AS t2 其中(t1.prodid,t1.qty)=(t2.prodid,t2.qty) AND t2.ordid = 365;
此后一个查询生成与以前的标量公式完全相同的字节码,但使用的语法更清晰,更易于阅读。
行值表示法对于从单个查询的结果更新表的两个或更多列很有用。例如,Fossil版本控制系统的全文本搜索功能 。
在化石全文检索系统,参与的全文检索的文献(wiki页面,门票,检查插件,文档文件等)是由一个名为“ftsdocs”(表跟踪˚F ULL牛逼分机小号目录操作搜索文档ument小号)。将新文档添加到资源库后,不会立即将它们编入索引。索引将推迟到有搜索请求为止。ftsdocs表包含一个“ idxed”字段,如果文档已被索引,则为true;否则为false。
发生搜索请求并首次索引待处理文档时,必须通过将idxed列设置为true并用与搜索有关的信息填充其他几个列来更新ftsdocs表。其他信息是从联接中获得的。查询是这样的:
更新ftsdocs SET idxed = 1, 名称= NULL, (label,url,mtime)= (选择printf('%% s上的“签入[%%。16s]”,blob.uuid, datetime(event.mtime)), printf('/ timeline?y = ci&c = %%。20s',blob.uuid), event.mtime FROM事件,blob 在哪里event.objid = ftsdocs.rid AND blob.rid = ftsdocs.rid) ftsdocs.type ='c'而不是ftsdocs.idxed
(有关 更多详细信息,请参见 源代码。此处和 此处的其他示例 。)
ftsdocs表中的9列中的5列已更新。修改后的两个列“ idxed”和“ name”可以独立于查询进行更新。但是三列“ label”,“ url”和“ mtime”都需要针对“ event”和“ blob”表的联接查询。如果没有行值,等效的UPDATE将要求连接重复三遍,每列更新一次。
有时,使用行值只会使SQL更易于读写。考虑以下两个UPDATE语句:
UPDATE tab1 SET(a,b)=(b,a); UPDATE tab1 SET a = b,b = a;
这两个UPDATE语句执行的操作完全相同。(它们生成相同的字节码。)但是第一种形式,即行值形式,似乎更清楚了该语句的意图是交换A和B列中的值。
或者考虑以下相同的查询:
SELECT * FROM tab1其中a =?1和b =?2; SELECT * FROM tab1 WHERE(a,b)=(?1,?2);
SQL语句再次生成相同的字节码,从而以完全相同的方式执行完全相同的工作。但是,通过将查询参数组合到一个单独的行值中,而不是将它们分散在WHERE子句中,可以使人类更容易阅读第二种形式。
行值已添加到SQLite 版本3.15.0(2016-10-14)。尝试在早期版本的SQLite中使用行值将生成语法错误。