22

假设我有一个包含表的 SQLite 数据库:

sqlite> create table person (id integer, firstname varchar, lastname varchar);

现在我想获取表中的每个条目。

sqlite> select t0.id, t0.firstname, t0.lastname from person t0;

这很好用,这就是我要使用的。但是,我使用了来自 Apple(Core Data)的生成 SQL 的框架。这个框架生成一个稍微不同的 SQL 查询:

sqlite> select 0, t0.id, t0.firstname, t0.lastname from person t0;

此框架生成的每个 SQL 查询都以“select 0”开头。这是为什么?

我试图使用解释命令来查看发生了什么,但这还没有定论——至少对我来说是这样。

sqlite> explain select t0.id, t0.firstname, t0.lastname from person t0;
addr        opcode      p1          p2          p3          p4          p5          comment   
----------  ----------  ----------  ----------  ----------  ----------  ----------  ----------
0           Trace       0           0           0                       00          NULL      
1           Goto        0           11          0                       00          NULL      
2           OpenRead    0           2           0           3           00          NULL      
3           Rewind      0           9           0                       00          NULL      
4           Column      0           0           1                       00          NULL      
5           Column      0           1           2                       00          NULL      
6           Column      0           2           3                       00          NULL      
7           ResultRow   1           3           0                       00          NULL      
8           Next        0           4           0                       01          NULL      
9           Close       0           0           0                       00          NULL      
10          Halt        0           0           0                       00          NULL      
11          Transactio  0           0           0                       00          NULL      
12          VerifyCook  0           1           0                       00          NULL      
13          TableLock   0           2           0           person      00          NULL      
14          Goto        0           2           0                       00          NULL 

第二个查询的表如下所示:

sqlite> explain select 0, t0.id, t0.firstname, t0.lastname from person t0;
addr        opcode      p1          p2          p3          p4          p5          comment   
----------  ----------  ----------  ----------  ----------  ----------  ----------  ----------
0           Trace       0           0           0                       00          NULL      
1           Goto        0           12          0                       00          NULL      
2           OpenRead    0           2           0           3           00          NULL      
3           Rewind      0           10          0                       00          NULL      
4           Integer     0           1           0                       00          NULL      
5           Column      0           0           2                       00          NULL      
6           Column      0           1           3                       00          NULL      
7           Column      0           2           4                       00          NULL      
8           ResultRow   1           4           0                       00          NULL      
9           Next        0           4           0                       01          NULL      
10          Close       0           0           0                       00          NULL      
11          Halt        0           0           0                       00          NULL      
12          Transactio  0           0           0                       00          NULL      
13          VerifyCook  0           1           0                       00          NULL      
14          TableLock   0           2           0           person      00          NULL      
15          Goto        0           2           0                       00          NULL     
4

3 回答 3

17

一些框架这样做是为了毫无疑问地判断是否返回了该表中的一行。

考虑

  A      B
+---+  +---+------+
| a |  | a | b    |
+---+  +---+------+
| 0 |  | 0 |    1 |
+---+  +---+------+
| 1 |  | 1 | NULL |
+---+  +---+------+
| 2 |
+---+

SELECT A.a, B.b
FROM A
LEFT JOIN B
ON B.a = A.a

  Results
+---+------+
| a | b    |
+---+------+
| 0 |    1 |
+---+------+
| 1 | NULL |
+---+------+
| 2 | NULL |
+---+------+

在这个结果集中,不可能看到a = 1表 B 中存在,但a = 2不存在。要获取该信息,您需要从表 b 中选择一个不可为空的表达式,而最简单的方法是选择一个简单的常量值。

SELECT A.a, B.x, B.b
FROM A
LEFT JOIN (SELECT 0 AS x, B.a, B.b FROM B) AS B
ON B.a = A.a

  Results
+---+------+------+
| a | x    | b    |
+---+------+------+
| 0 |    0 |    1 |
+---+------+------+
| 1 |    0 | NULL |
+---+------+------+
| 2 | NULL | NULL |
+---+------+------+

在很多情况下,这些常量值并不是严格要求的,例如当您没有连接时,或者当您可以从 b 中选择一个不可为空的列时,但它们也不会造成任何伤害,因此它们可以只是无条件地被包括在内。

于 2012-11-04T14:55:55.300 回答
9

当我有代码来动态生成一个WHERE子句时,我通常以一个子句开头:

WHERE 1 = 1

然后添加附加条件的循环总是以相同的格式添加每个条件:

AND x = y

无需放置条件逻辑来检查这是否是第一个条件:“如果这是第一个条件,则以WHERE关键字开头,否则添加AND关键字。

所以我可以想象一个框架出于类似的原因这样做。如果以 a 开始语句,SELECT 0则添加后续列的代码可以在没有任何条件语句的循环中。, colx每次只添加没有任何条件检查的“如果这是第一列,不要在列名之前放逗号,否则做”。

示例伪代码:

String query = "SELECT 0";

for (Column col in columnList)
    query += ", col";
于 2012-11-04T16:38:37.193 回答
0

只有苹果知道……但我看到了两种可能性:

  1. 插入一个虚拟列可确保实际输出列从 1 开始编号,而不是 0。如果某些现有接口已经采用基于 1 的编号,那么在 SQL 后端这样做可能是最简单的解决方案。

  2. 如果您使用多个子查询对多个对象进行查询,则可以使用这样的值来确定记录源自哪个子查询:

    SELECT 0, t0.firstname, ... FROM PERSON t0 WHERE t0.id = 123
    UNION ALL
    SELECT 1, t0.firstname, ... FROM PERSON t0 WHERE t0.id = 456 
    

    (我不知道 Core Data 是否真的这样做。)


您的EXPLAIN输出显示唯一的区别是(在地址 4)第二个程序将额外列设置为零,因此只有最小的性能差异。

于 2012-11-04T15:38:45.017 回答