【问题标题】:Why SELECT 0, ... instead of SELECT为什么选择 0, ... 而不是 SELECT
【发布时间】:2012-10-24 13:01:55
【问题描述】:

假设我有一个包含表的 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”开头。这是为什么呢?

我尝试使用 explain 命令查看发生了什么,但这没有结果——至少对我来说是这样。

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     

【问题讨论】:

    标签: sql sqlite core-data


    【解决方案1】:

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

    考虑

      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 中选择一个不可为空的列时,但它们也不会造成任何伤害,所以它们可以被无条件地包括在内。

    【讨论】:

    • 但是常量0not 来自表B;在每条结果记录中无条件返回。
    • 但是 Core Data 将0 放到了最外层的查询中。
    • @CL。确实,但是如果保留常量,如果查询执行同样正确和快速,为什么还要费心不将其包含在最外层查询中呢? (要完全清楚,我并不是说这就是包含常量的原因,只是这就是其他一些框架这样做的原因,而且很可能也是这里发生的原因。)
    【解决方案2】:

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

    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)第二个程序将额外列设置为零,因此只有很小的性能差异。

    【讨论】:

      【解决方案3】:

      当我有代码动态生成WHERE 子句时,我通常以:

      WHERE 1 = 1
      

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

      AND x = y
      

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

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

      示例伪代码:

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

      【讨论】:

      • 我明白你的意思,但再说一遍:在 Objective-C 中,你可以像这样简单地加入你的列名: [@[@"firstName", @"lastName"] componentsJoinedByString:@", "] ];所以这里不需要。
      • 这正是我希望避免的那种代码 :)
      • 我更愿意使用 componentsJoinedByString 而不是 for 循环。也许这只是口味问题... :)
      • @cmk:或者其他考虑的问题,例如表现。我不知道Objective-C,但您其他评论中的代码示例在我看来似乎是某种语法糖,即为方便程序员而不是为了效率而设计的东西。一个没有条件的简单的for 循环可能会编译成一段比componentsJoinedByString 更有效的代码,不管它是什么。不要误解我的意思,我一般不反对语法糖,但我们谈论的是一个框架,效率应该是关键(或部分关键)。
      • componentsJoinedByString 只是在内部进行了一个 for 循环,因此它应该与您的代码一样快,但话又说回来:使用 componentsJoinedByString 时您的灵活性稍差......我们已经实现了自己的 ORM 框架和大量使用 componentsJoinedByString 没有任何性能问题。瓶颈通常在其他地方——至少在我们的例子中是这样。但感谢您的意见 - 我真的很感激。
      猜你喜欢
      • 2023-04-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-05
      • 1970-01-01
      相关资源
      最近更新 更多