SQL 为什么选择 0, ... 而不是 SELECT

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13219472/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-01 11:55:23  来源:igfitidea点击:

Why SELECT 0, ... instead of SELECT

sqlsqlitecore-data

提问by Christian Kienle

Lets say I have a SQLite database that contains a table:

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

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

Now I want to get every entry which is in the table.

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

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

This works fine and this it what I would use. However I have worked with a framework from Apple (Core Data) that generates SQL. This framework generates a slightly different SQL query:

这工作正常,这就是我会使用的。但是,我使用了 Apple(Core Data)的框架来生成 SQL。这个框架生成一个稍微不同的 SQL 查询:

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

Every SQL query generated by this framework begins with "select 0,". Why is that?

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

I tried to use the explain command to see whats going on but this was inconclusive - at least to me.

我尝试使用解释命令来查看发生了什么,但这是不确定的 - 至少对我而言。

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 

And the table for the second query looks like this:

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

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     

回答by Christian Kienle

Some frameworks do this in order to tell, without any doubt, whether a row from that table was returned.

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

Consider

考虑

  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 |
+---+------+

In this result set, it is not possible to see that a = 1exists in table B, but a = 2does not. To get that information, you need to select a non-nullable expression from table b, and the simplest way to do that is to select a simple constant value.

在这个结果集中,不可能看到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 |
+---+------+------+

There are a lot of situations where these constant values are not strictly required, for example when you have no joins, or when you could choose a non-nullable column from b instead, but they don't cause any harm either, so they can just be included unconditionally.

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

回答by Glenn

When I have code to dynamically generate a WHEREclause, I usually start the clause with a:

当我有动态生成WHERE子句的代码时,我通常以以下内容开头子句:

WHERE 1 = 1

Then the loop to add additional conditions always adds each condition in the same format:

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

AND x = y

without having to put conditional logic in place to check if this is the first condition or not: "if this is the first condition then start with the WHEREkeyword, else add the ANDkeyword.

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

So I can imagine a framework doing this for similar reasons. If you start the statement with a SELECT 0then the code to add subsequent columns can be in a loop without any conditional statements. Just add , colxeach time without any conditional checking along the lines of "if this is the first column, don't put a comma before the column name, otherwise do".

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

Example pseudo code:

示例伪代码:

String query = "SELECT 0";

for (Column col in columnList)
    query += ", col";

回答by CL.

Only Apple knows … but I see two possibilities:

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

  1. Inserting a dummy column ensures that the actualoutput columns are numbered beginning with 1, not 0. If some existing interface already assumed one-based numbering, doing it this way in the SQL backend might have been the easiest solution.

  2. If you make a query for multiple objects using multiple subqueries, a value like this could be used to determine from which subquery a record originates:

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

    (I don't know if Core Data actually does this.)

  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 是否真的这样做了。)



Your EXPLAINoutput shows that the only difference is (at address 4) that the second program sets the extra column to zero, so there is only a minimal performance difference.

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