2012-11-04 13 views
20

Diciamo che ho un database SQLite che contiene una tabella:Perché scegliere 0, ... invece di SELEZIONE

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

Ora voglio ottenere ogni ingresso, che è nella tabella.

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

Questo funziona bene e questo è quello che vorrei usare. Tuttavia ho lavorato con un framework Apple (Core Data) che genera SQL. Questo quadro genera una leggermente diversa query SQL:

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

interrogazione Ogni SQL generato da questo quadro inizia con "selezionare 0". Perché?

Ho provato a utilizzare il comando spiega per vedere che cosa sta succedendo, ma questo era inconcludente - almeno per 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 

e il tavolo per la seconda query assomiglia a questo:

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  

risposta

15

Alcuni quadri normativi fare questo per raccontare, senza alcun dubbio, se una riga da quel tavolo è stato restituito.

consideri

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 questo gruppo di risultati, non è possibile vedere che a = 1 esiste nella tabella B, ma a = 2 no. Per ottenere tali informazioni, è necessario selezionare un'espressione non nullable dalla tabella b, e il modo più semplice per farlo è selezionare un valore costante semplice.

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

Ci sono un sacco di situazioni in cui non sono strettamente necessari questi valori costanti, per esempio quando non si ha si unisce, o quando si potrebbe scegliere una colonna non annullabile da B, invece, ma non lo fanno causa qualsiasi danno, quindi possono essere inclusi incondizionatamente.

+0

Ma la costante '0 'è * non * dalla tabella B; è restituito incondizionatamente in ogni record di risultato. –

+0

@CL. Modificato per rendere più chiaro cosa intendo. – hvd

+0

Ma Core Data mette il '0' nella query più esterna. –

0

solo Apple sa ... ma vedo due possibilità:

  1. Inserimento di una colonna fittizia garantisce che le colonne di output attuale sono numerati a partire da 1, non 0. Se un po 'di interfaccia esistente già assunto one la numerazione basata, facendolo in questo modo nel back-end SQL potrebbe essere stata la soluzione più semplice.

  2. Se si effettua una query per più oggetti utilizzando più subquery, un valore come questo potrebbe essere utilizzato per determinare da quale subquery un record origine:

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

    (non so se in realtà Core Data fa questo.)


tuo EXPLAIN output mostra che l'unica differenza è (all'indirizzo 4) che il secondo programma imposta la colonna supplementare a zero, s o C'è solo una minima differenza di prestazioni.

8

Quando Ho codice per generare dinamicamente una clausola WHERE, di solito iniziare la clausola con un:

WHERE 1 = 1 

Poi il ciclo di aggiungere ulteriori condizioni sempre aggiunge ogni condizione nello stesso formato:

AND x = y 

senza dover mettere in atto la logica condizionale per verificare se questa è la prima condizione o meno: "se questa è la prima condizione, iniziare con la parola chiave WHERE, oppure aggiungere la parola chiave AND.

Quindi posso immaginare un framework che faccia questo per ragioni simili. Se si avvia l'istruzione con un SELECT 0, il codice per aggiungere le colonne successive può essere in un ciclo senza alcuna istruzione condizionale. Basta aggiungere , colx ogni volta senza alcun controllo condizionale lungo le linee di "se questa è la prima colonna, non mettere una virgola prima del nome della colonna, altrimenti fare".

Esempio codice pseudo:

String query = "SELECT 0"; 

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

Vedo il tuo punto ma di nuovo: In Objective-C puoi semplicemente unire i nomi delle tue colonne in questo modo: [@ [@ "firstName", @ "lastName"] componentsJoinedByString: @ ","]]; Quindi no se necessario qui. –

+0

Questo sarebbe esattamente il tipo di codice che vorrei evitare :) – Glenn

+0

Preferisco usare componentJoinedByString invece di un ciclo for. Forse è solo una questione di gusti ... :) –

Problemi correlati