2013-08-05 8 views
8

Mi aspetto che questi due SELECT s abbiano lo stesso piano di esecuzione e le stesse prestazioni. Poiché è presente un carattere jolly iniziale su LIKE, mi aspetto una scansione dell'indice. Quando eseguo questo e guardo i piani, il primo SELECT si comporta come previsto (con una scansione). Ma il secondo piano SELECT mostra un indice di ricerca, e viene eseguito 20 volte più veloce.Come può LIKE '% ...' cercare su un indice?

Codice:

-- Uses index scan, as expected: 
SELECT 1 
    FROM AccountAction 
    WHERE AccountNumber LIKE '%441025586401' 

-- Uses index seek somehow, and runs much faster: 
declare @empty VARCHAR(30) = '' 
SELECT 1 
    FROM AccountAction 
    WHERE AccountNumber LIKE '%441025586401' + @empty 

Domanda:

Come SQL Server utilizza un indice di ricerca quando il pattern inizia con un carattere jolly?

Domanda bonus:

Perché concatenando un cambiamento stringa vuota/di migliorare il piano di esecuzione?

Dettagli:

  • C'è un indice non cluster su Accounts.AccountNumber
  • Ci sono altri indici, ma sia la cercano e la scansione sono questo indice.
  • La colonna Accounts.AccountNumber è un nullable varchar(30)
  • Il server è 2012

di tabelle e indici definizioni SQL Server:

CREATE TABLE [updatable].[AccountAction](
    [ID] [int] IDENTITY(1,1) NOT NULL, 
    [AccountNumber] [varchar](30) NULL, 
    [Utility] [varchar](9) NOT NULL, 
    [SomeData1] [varchar](10) NOT NULL, 
    [SomeData2] [varchar](200) NULL, 
    [SomeData3] [money] NULL, 
    --... 
    [Created] [datetime] NULL, 
CONSTRAINT [PK_Account] PRIMARY KEY NONCLUSTERED 
(
    [ID] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 


CREATE NONCLUSTERED INDEX [IX_updatable_AccountAction_AccountNumber_UtilityCode_ActionTypeCd] ON [updatable].[AccountAction] 
(
    [AccountNumber] ASC, 
    [Utility] ASC 
) 
INCLUDE ([SomeData1], [SomeData2], [SomeData3]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 


CREATE CLUSTERED INDEX [CIX_Account] ON [updatable].[AccountAction] 
(
    [Created] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 

NOTA: Ecco l'attuale piano di di esecuzione per la due domande. I nomi degli oggetti differiscono leggermente dal codice sopra perché stavo cercando di mantenere la domanda semplice.

The two execution plans.

+1

c'è differenza di esecuzione reale o solo nei piani di stima? –

+3

@GordonLinoff Il numero di versione di SQL Server 2012 è 11, 2008 R2: 10.5, 2008: 10, ecc. – swasheck

+0

Non so quanto possa essere importante, ma le query effettivamente eseguite erano 'LIKE '% 441025586401%'' , con un carattere jolly all'inizio e alla fine – Lamak

risposta

7

Questi test (database AdventureWorks2008R2) mostra cosa succede:

SET NOCOUNT ON; 
SET STATISTICS IO ON; 

PRINT 'Test #1'; 
SELECT p.BusinessEntityID, p.LastName 
FROM Person.Person p 
WHERE p.LastName LIKE '%be%'; 

PRINT 'Test #2'; 
DECLARE @Pattern NVARCHAR(50); 
SET @Pattern=N'%be%'; 
SELECT p.BusinessEntityID, p.LastName 
FROM Person.Person p 
WHERE p.LastName LIKE @Pattern; 

SET STATISTICS IO OFF; 
SET NOCOUNT OFF; 

Risultati:

Test #1 
Table 'Person'. Scan count 1, logical reads 106 
Test #2 
Table 'Person'. Scan count 1, logical reads 106 

I risultati SET STATISTICS IO dimostra che LIO sono la stessa . Ma i piani di esecuzione sono molto diverse: enter image description here

Nella prima prova, SQL Server utilizza un Index Scan esplicito, ma nella seconda prova di SQL Server utilizza un Index Seek che è un Index Seek - range scan. Nell'ultimo caso SQL Server utilizza un operatore Compute Scalar per generare questi valori

[Expr1005] = Scalar Operator(LikeRangeStart([@Pattern])), 
[Expr1006] = Scalar Operator(LikeRangeEnd([@Pattern])), 
[Expr1007] = Scalar Operator(LikeRangeInfo([@Pattern])) 

e l'uso Index Seek all'operatore un Seek Predicate (ottimizzate) per un range scan (LastName > LikeRangeStart AND LastName < LikeRangeEnd) più un altro non ottimizzata Predicate (LastName LIKE @pattern).

Come può LIKE '% ...' cercare un indice?

La mia risposta: non è un "reale" Index Seek. È un Index Seek - range scan che, in questo caso, ha le stesse prestazioni come Index Scan.

Si veda, anche, la differenza tra Index Seek e Index Scan (discussione simile): So…is it a Seek or a Scan?.

Edit 1: Il piano di esecuzione per OPTION(RECOMPILE) (vedi raccomandazione di Aaron per favore) mostra, anche, un Index Scan (invece di Index Seek): enter image description here