Подтвердить что ты не робот

Почему SELECT 0,... вместо SELECT

Предположим, у меня есть база данных 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     
4b9b3361

Ответ 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 нет. Чтобы получить эту информацию, вам нужно выбрать выражение, исключающее null, из таблицы 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 вместо этого, но они также не нанесут никакого вреда, поэтому их можно просто включить безоговорочно.

Ответ 2

Когда у меня есть код для динамического создания предложения WHERE, я обычно запускаю предложение с помощью:

WHERE 1 = 1

Затем цикл для добавления дополнительных условий всегда добавляет каждое условие в том же формате:

AND x = y

без необходимости устанавливать условную логику, чтобы проверить, является ли это первым условием или нет: "если это первое условие, то начните с ключевого слова WHERE, добавьте ключевое слово AND.

Итак, я могу представить себе рамки, которые делают это по аналогичным причинам. Если вы начинаете инструкцию с SELECT 0, тогда код для добавления последующих столбцов может находиться в цикле без каких-либо условных операторов. Просто добавьте , colx каждый раз без какой-либо условной проверки по строкам "если это первый столбец, не помещайте запятую перед именем столбца, в противном случае".

Пример псевдокода:

String query = "SELECT 0";

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

Ответ 3

Только Apple знает... но я вижу две возможности:

  • Вставка фиктивного столбца гарантирует, что фактические выходные столбцы будут пронумерованы, начиная с 1, а не 0. Если какой-либо существующий интерфейс уже принял однонаправленную нумерацию, то это делается таким образом в бэкэнд SQL, возможно, было самым простым решением.

  • Если вы делаете запрос для нескольких объектов с использованием нескольких подзапросов, такое значение может быть использовано для определения того, из какого подзапроса возникает запись:

    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) заключается в том, что вторая программа устанавливает дополнительный столбец в ноль, поэтому существует только минимальная разница в производительности.