Disons que j'ai une base de données SQLite qui contient une table:
sqlite> create table person (id integer, firstname varchar, lastname varchar);
Maintenant, je veux obtenir chaque entrée qui est dans la table.
sqlite> select t0.id, t0.firstname, t0.lastname from person t0;
Cela fonctionne bien et c'est ce que j'utiliserais. Cependant, j'ai travaillé avec un framework Apple (Core Data) qui génère du SQL. Ce framework génère une requête SQL légèrement différente:
sqlite> select 0, t0.id, t0.firstname, t0.lastname from person t0;
Chaque requête SQL générée par ce framework commence par "select 0". Pourquoi donc?
J'ai essayé d'utiliser la commande explicit pour voir ce qui se passait, mais ce n'était pas concluant - du moins pour moi.
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
Et la table pour la deuxième requête ressemble à ceci:
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
Certains frameworks le font afin de dire, sans aucun doute, si une ligne de cette table a été retournée.
Considérer
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 |
+---+------+
Dans cet ensemble de résultats, il n'est pas possible de voir que a = 1
existe dans la table B, contrairement à a = 2
. Pour obtenir ces informations, vous devez sélectionner une expression non nullable dans la table b, et le moyen le plus simple consiste à sélectionner une valeur constante simple.
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 |
+---+------+------+
Il existe de nombreuses situations dans lesquelles ces valeurs constantes ne sont pas strictement requises, par exemple, lorsque vous n'avez pas de jointures ou que vous pouvez choisir une colonne non nullable dans b, mais qu'elles ne causent pas de dommages non plus. juste être inclus sans condition.
Quand j'ai du code pour générer dynamiquement une clause WHERE
, je commence généralement la clause par:
WHERE 1 = 1
Ensuite, la boucle pour ajouter des conditions supplémentaires ajoute toujours chaque condition dans le même format:
AND x = y
sans avoir à mettre en place une logique conditionnelle pour vérifier s’il s’agit ou non de la première condition: "s’il s’agit de la première condition, commencez par le mot clé WHERE
, sinon ajoutez le mot clé AND
.
Je peux donc imaginer un cadre faisant cela pour des raisons similaires. Si vous démarrez l'instruction avec un SELECT 0
, le code permettant d'ajouter les colonnes suivantes peut se trouver dans une boucle sans instructions conditionnelles. Il suffit d’ajouter , colx
à chaque fois sans vérification conditionnelle le long de la ligne "s'il s'agit de la première colonne, ne mettez pas de virgule avant le nom de la colonne, sinon faites".
Exemple de pseudo-code:
String query = "SELECT 0";
for (Column col in columnList)
query += ", col";
Seul Apple sait… mais je vois deux possibilités:
L'insertion d'une colonne factice garantit que les colonnes actual output sont numérotées en commençant par 1, et non par 0. Si une interface existante supposait déjà une numérotation à une base, cette manière de procéder dans le backend SQL aurait pu être la solution la plus simple.
Si vous effectuez une requête pour plusieurs objets à l'aide de plusieurs sous-requêtes, une valeur comme celle-ci peut être utilisée pour déterminer la sous-requête d'un enregistrement:
SELECT 0, t0.firstname, ... FROM PERSON t0 WHERE t0.id = 123
UNION ALL
SELECT 1, t0.firstname, ... FROM PERSON t0 WHERE t0.id = 456
(Je ne sais pas si Core Data le fait réellement.)
Votre sortie EXPLAIN
indique que la seule différence est (à l'adresse 4) que le deuxième programme définit la colonne supplémentaire sur zéro, de sorte qu'il n'y a qu'une différence de performance minimale.