Perché la variabile della tabella costringe una scansione indice mentre la tabella Temp utilizza la ricerca e la ricerca dei segnalibri?

dba.stackexchange https://dba.stackexchange.com/questions/108352

Domanda

Sto cercando di capire perché usare una variabile da tabella impedisce all'ottimizzatore di utilizzare un indice Seek e quindi la ricerca di bookmark contro una scansione indice.

Popolazione del tavolo:

CREATE TABLE dbo.Test 
(
    RowKey INT NOT NULL PRIMARY KEY, 
    SecondColumn CHAR(1) NOT NULL DEFAULT 'x',
    ForeignKey INT NOT NULL 
) 

INSERT dbo.Test 
(
    RowKey, 
    ForeignKey
) 
SELECT TOP 1000000 
    ROW_NUMBER() OVER (ORDER BY (SELECT 0)),
    ABS(CHECKSUM(NEWID()) % 10)     
FROM sys.all_objects s1
CROSS JOIN sys.all_objects s2 

CREATE INDEX ix_Test_1 ON dbo.Test (ForeignKey) 
.

Popolare una variabile tabella con un singolo record e tenta di cercare la chiave primaria e la seconda colonna cercando sulla colonna del tasto estraneo:

DECLARE @Keys TABLE (RowKey INT NOT NULL) 

INSERT @Keys (RowKey) VALUES (10)

SELECT 
    t.RowKey,
    t.SecondColumn
FROM
    dbo.Test t 
INNER JOIN 
    @Keys k
ON
    t.ForeignKey = k.RowKey
.

Di seguito è riportato il piano di esecuzione:

 Inserire l'immagine Descrizione qui

Ora la stessa query usando una tabella temp invece:

CREATE TABLE #Keys (RowKey INT NOT NULL) 

INSERT #Keys (RowKey) VALUES (10) 

SELECT 
    t.RowKey,
    t.SecondColumn
FROM
    dbo.Test t 
INNER JOIN 
    #Keys k
ON
    t.ForeignKey = k.RowKey
.

Questo piano di query utilizza una ricerca di Seek and Bookmark:

 Inserire l'immagine Descrizione qui

Perché l'ottimizzatore è disposto a cercare la ricerca dei segnalibri con la tabella Temp, ma non la variabile della tabella?

La variabile della tabella viene utilizzata in questo esempio per rappresentare i dati che vengono attraverso un tipo di tabella definito dall'utente in una stored procedure.

Mi rendo conto che l'indice cerca potrebbe non essere appropriato se il valore chiave estero si è verificato centinaia di migliaia di volte. In tal caso, una scansione sarebbe probabilmente una scelta migliore. Per lo scenario che ho creato, non c'era alcuna riga con un valore di 10. Penso ancora che il comportamento sia interessante e vorrei sapere se c'è una ragione per questo.

SQL Fiddle

L'aggiunta di OPTION (RECOMPILE) non ha cambiato il comportamento. L'UDDT ha una chiave primaria.

@@VERSION è SQL Server 2008 R2 (SP2) - 10.50.4042.0 (X64) (Build 7601: Service Pack 1) (Hypervisor)

È stato utile?

Soluzione

Il motivo del comportamento è che SQL Server non può determinare quante righe corrisponderanno alla chiave strategica, poiché non esiste un indice con RowKey come la colonna leader (può dedurlo dalle statistiche sulla tabella #temp, ma quelle Non esistere per le variabili da tavola / UDTTS), quindi fa una stima di 100.000 righe, che è meglio gestita con una scansione rispetto a una ricerca di ricerca +. Dal momento in cui SQL Server si realizza che c'è solo una riga, è troppo tardi.

Potresti essere in grado di costruire il tuo UDTT in modo diverso; Nelle versioni più moderne di SQL Server è possibile creare indici secondari sulle variabili delle tabelle, ma questa sintassi non è disponibile nel 2008 R2.

BTW È possibile ottenere il comportamento della ricerca (almeno nei miei studi limitati) se si tenta di evitare la bitmap / sonda, accendendo un loop nidificato unisciti:

DECLARE @Keys TABLE (RowKey INT PRIMARY KEY); -- can't hurt

INSERT @Keys (RowKey) VALUES (10);

SELECT 
     t.RowKey
    ,t.SecondColumn
FROM
    dbo.Test t 
INNER JOIN 
    @Keys k
ON
    t.ForeignKey = k.RowKey
    OPTION (LOOP JOIN);
.

I ha imparato questo trucco da Paul White diversi anni fa. Naturalmente, dovresti stare attento a mettere qualsiasi tipo di suggerimento di join in codice di produzione - questo può fallire se le persone apportano modifiche agli oggetti sottostanti e che il tipo specifico di join non è più possibile o non più ottimale.

Per query più complesse e quando si sposta su SQL Server 2012 o sopra, è possibile che bandiera traccia 2453 potrebbe aiutare. Quella flag non ha aiutato con questo semplice join, però. E gli stessi Disclaimer si applicherebbero - questa è solo una cosa alternativa che non si dovrebbe generalmente fare a meno di un sacco di documentazione e rigorose procedure di test di regressione in posizione.

Inoltre, il service pack 1 è a lungo fuori dal supporto, è necessario ottenere su Service Pack 3 + MS15-058 .

Altri suggerimenti

Variabili da tavolo e tavole Temp sono gestite in modo diverso in un numero di modi.C'è un GRANDERispondi qui con molte specifiche su dove sono diversi.

Specificamente nel tuo caso, immagino che il fatto che le tabelle temp possano avere piani generali di statistiche aggiuntivi e piani paralleli mentre le variabili delle tabelle hanno statistiche più limitate (nessuna statistica a livello di colonna) e nessun piano parallelo è il tuo colpevole.

Si può benissimo stare meglio a scaricare la variabile della tabella in una tabella temp per la durata della stored procedure.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a dba.stackexchange
scroll top