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