Le scansioni costanti producono ciascuna una singola riga in memoria senza colonne. Lo scalare di calcolo superiore genera una singola riga con 3 colonne
Expr1005 Expr1006 Expr1004
----------- ----------- -----------
NULL NULL 60
Lo scalare di calcolo inferiore genera una singola riga con 3 colonne
Expr1008 Expr1009 Expr1007
----------- ----------- -----------
NULL 1048576 10
L'operatore di concatenazione unisce queste 2 righe e genera le 3 colonne ma ora vengono rinominate
Expr1010 Expr1011 Expr1012
----------- ----------- -----------
NULL NULL 60
NULL 1048576 10
La Expr1012
colonna è un insieme di flag utilizzati internamente per definire determinate proprietà di ricerca per il motore di archiviazione .
Il successivo scalare di calcolo lungo le uscite 2 righe
Expr1010 Expr1011 Expr1012 Expr1013 Expr1014 Expr1015
----------- ----------- ----------- ----------- ----------- -----------
NULL NULL 60 True 4 16
NULL 1048576 10 False 0 0
Le ultime tre colonne sono definite come segue e vengono utilizzate solo per scopi di ordinamento prima di presentare all'operatore Intervallo di unione
[Expr1013] = Scalar Operator(((4)&[Expr1012]) = (4) AND NULL = [Expr1010]),
[Expr1014] = Scalar Operator((4)&[Expr1012]),
[Expr1015] = Scalar Operator((16)&[Expr1012])
Expr1014
e Expr1015
basta verificare se alcuni bit sono attivi nella bandiera.
Expr1013
sembra restituire una colonna booleana vera se entrambi i bit per 4
sono attivi e Expr1010
attivi NULL
.
Provando altri operatori di confronto nella query ottengo questi risultati
+----------+----------+----------+-------------+----+----+---+---+---+---+
| Operator | Expr1010 | Expr1011 | Flags (Dec) | Flags (Bin) |
| | | | | 32 | 16 | 8 | 4 | 2 | 1 |
+----------+----------+----------+-------------+----+----+---+---+---+---+
| > | 1048576 | NULL | 6 | 0 | 0 | 0 | 1 | 1 | 0 |
| >= | 1048576 | NULL | 22 | 0 | 1 | 0 | 1 | 1 | 0 |
| <= | NULL | 1048576 | 42 | 1 | 0 | 1 | 0 | 1 | 0 |
| < | NULL | 1048576 | 10 | 0 | 0 | 1 | 0 | 1 | 0 |
| = | 1048576 | 1048576 | 62 | 1 | 1 | 1 | 1 | 1 | 0 |
| IS NULL | NULL | NULL | 60 | 1 | 1 | 1 | 1 | 0 | 0 |
+----------+----------+----------+-------------+----+----+---+---+---+---+
Da cui deduco che Bit 4 significa "Ha inizio dell'intervallo" (anziché essere illimitato) e Bit 16 indica che l'inizio dell'intervallo è inclusivo.
Questo set di risultati a 6 colonne viene emesso SORT
dall'operatore ordinato per
Expr1013 DESC, Expr1014 ASC, Expr1010 ASC, Expr1015 DESC
. Supponendo True
è rappresentato da 1
e False
per 0
il gruppo di risultati precedentemente rappresentato è già in questo ordine.
Sulla base delle mie ipotesi precedenti, l'effetto netto di questo tipo è quello di presentare gli intervalli all'intervallo di unione nel seguente ordine
ORDER BY
HasStartOfRangeAndItIsNullFirst,
HasUnboundedStartOfRangeFirst,
StartOfRange,
StartOfRangeIsInclusiveFirst
L'operatore dell'intervallo di unione genera 2 righe
Expr1010 Expr1011 Expr1012
----------- ----------- -----------
NULL NULL 60
NULL 1048576 10
Per ogni riga emessa viene eseguita una ricerca dell'intervallo
Seek Keys[1]: Start:[dbo].[t].c2 > Scalar Operator([Expr1010]),
End: [dbo].[t].c2 < Scalar Operator([Expr1011])
Quindi sembrerebbe che vengano eseguite due ricerche. Uno apparentemente > NULL AND < NULL
e uno > NULL AND < 1048576
. Tuttavia, i flag che vengono passati sembrano modificarlo in IS NULL
e < 1048576
rispettivamente. Speriamo che @sqlkiwi possa chiarire questo e correggere eventuali inesattezze!
Se si modifica leggermente la query in
select *
from t
where
c2 > 1048576
or c2 = 0
;
Quindi il piano appare molto più semplice con una ricerca di indice con predicati di ricerca multipli.
Il piano mostra Seek Keys
Start: c2 >= 0, End: c2 <= 0,
Start: c2 > 1048576
La spiegazione del perché questo piano più semplice non può essere utilizzato per il caso nel PO è data da SQLKiwi nei commenti al precedente post sul blog collegato .
Una ricerca di indice con più predicati non può mescolare diversi tipi di predicato di confronto (ad es. Is
E Eq
nel caso del PO). Questa è solo una limitazione attuale del prodotto (ed è presumibilmente il motivo per cui il test di uguaglianza nell'ultima query c2 = 0
viene implementato usando >=
e <=
piuttosto che la semplice uguaglianza cerca di ottenere per la query c2 = 0 OR c2 = 1048576
.