Estimativa da cardinalidade SARG, por que não fazer a varredura completa?

11

Por que não há verificação completa (no SQL 2008 R2 e 2012)?

Dados de teste:

DROP TABLE dbo.TestTable
GO  
CREATE TABLE dbo.TestTable
(
   TestTableID INT IDENTITY PRIMARY KEY,
   VeryRandomText VarChar(50),
   VeryRandomText2 VarChar(50)
)
Go
Set NoCount ON
Declare @i int
Set @i = 0
While @i < 10000
Begin
   Insert Into dbo.TestTable(VeryRandomText, VeryRandomText2)
      Values(Cast(Rand()*10000000 as VarChar(50)), Cast(Rand()*10000000 as VarChar(50)));
   Set @i = @i + 1;
End
Go
CREATE Index IX_VeryRandomText On dbo.TestTable
(
    VeryRandomText
)
Go

Ao executar a consulta:

Select * From dbo.TestTable Where VeryRandomText = N'111' -- bad

Receba um aviso (como esperado, porque a comparação de dados nchar com a coluna varchar):

<PlanAffectingConvert ConvertIssue="Cardinality Estimate" Expression="CONVERT_IMPLICIT(nvarchar(50),[DemoDatabase].[dbo].[TestTable].[VeryRandomText],0)" />

Mas, então, vejo o plano de execução, e posso ver, que ele não está usando a varredura completa, como seria de esperar, mas a busca por índice.

insira a descrição da imagem aqui

Obviamente, isso é bom, porque nesse caso específico, a execução é muito mais rápida do que se houvesse uma verificação completa.

Mas não consigo entender como o SQL Server tomou a decisão de fazer esse plano.

Além disso, se o agrupamento do servidor fosse do Windows no nível do servidor e no banco de dados do SQL Server, ele causaria uma verificação completa na mesma consulta.

Jānis
fonte

Respostas:

8

Ao comparar valores de diferentes tipos de dados, o SQL Server segue as regras de Precedência de tipos de dados . Como o nvarchar tem precedência mais alta que o varchar, o SQL Server precisa converter os dados da coluna em nvarchar antes de comparar valores. Isso significa aplicar uma função na coluna e isso tornaria a consulta não sargável.

No entanto, o SQL Server faz o melhor para protegê-lo contra seus erros, por isso usa uma técnica descrita por Paul White na postagem do blog Pesquisas dinâmicas e conversões implícitas ocultas para buscar uma faixa de valores e, em seguida, fazer a comparação final, com o parâmetro conversão do valor da coluna em nvarchar, em um predicado residual para filtrar quaisquer falsos positivos.

Como você observou, no entanto, isso não funciona quando o agrupamento da coluna é um agrupamento SQL. A razão disso, acredito, pode ser encontrada no artigo Comparando agrupamentos SQL com agrupamentos Windows

Basicamente, um agrupamento do Windows usa o mesmo algoritmo para varchar e nvarchar, em que um agrupamento SQL usa um algoritmo diferente para dados do varchar e o mesmo algoritmo que um agrupamento do Windows para dados do nvarchar.

Portanto, passar de varchar para nvarchar em um agrupamento do Windows usará o mesmo algoritmo e o SQL Server poderá produzir um intervalo de valores, no seu caso, um literal nvarchar para obter linhas do índice da coluna de agrupamento SQL varchar. No entanto, quando o agrupamento da coluna varchar é um agrupamento SQL que não é possível devido ao diferente algoritmo usado.


Atualizar:

Uma demonstração das diferentes ordens de classificação para colunas varchar usando janelas e agrupamento sql.

SQL Fiddle

Configuração do esquema do MS SQL Server 2014 :

create table T(C varchar(10));

insert into T values('a-b'),('aa'),('ac');

Consulta 1 :

select C
from T
order by C collate SQL_Latin1_General_CP1_CI_AS;

Resultados :

|   C |
|-----|
| a-b |
|  aa |
|  ac |

Consulta 2 :

select C
from T
order by C collate Latin1_General_100_CI_AS;

Resultados :

|   C |
|-----|
|  aa |
| a-b |
|  ac |
Mikael Eriksson
fonte
0

Você deve se lembrar que os nós de folha de um índice não clusterizado consistem em páginas de índice que contêm chave de cluster ou RID para localizar a linha de dados.

Na sua cláusula where, você declara VeryRandomText = N'111'Como existe um índice Não clusterizado no VeryRandomText (criar índice criará um índice não clusterizado, a menos que você diga explicitamente para criar um cluster), a maneira mais barata de encontrar os dados é varrendo o índice para encontrar o rowid e busque os dados para a linha.

Se você criar um índice em cluster

CREATE clustered Index IX_VeryRandomText On dbo.TestTable (VeryRandomText)

ou uma chave primária no VeryRandomText, você obteria uma varredura desse índice.

Consulte livros on-line ou aqui: http://www.sqlforge.com/w/Clustered_index,_nonclustered_index,_or_heap

Spörri
fonte
Sim, eu estou ciente do que você está escrevendo. Como você pode ver, já existe um índice clusterizado no TestTableID. Mas o problema é que, se o SQL Server não puder ver estatísticas da distribuição de dados da coluna (como neste caso, devido à incompatibilidade de tipos de dados que deve exigir toda a conversão de tipos de dados do valor da linha), ele deverá escolher a varredura de índice em cluster nesse caso, não a busca por índice .
Jānis
E nem sempre é mais barato procurar / varrer índices não agrupados - quando os valores não são suficientemente distintos ou não cobrem o índice, pode ser mais barato executar a varredura de índices agrupados.
Jānis
@ Jānis não accoring ao seu script de criar o índice não irá criar um índice agrupado você tem a dizer de forma tão explícita - mesmo se você ler o plano de consulta, de busca de índice (sem cluster)
Spörri
"Quando você cria uma restrição PRIMARY KEY, um índice clusterizado exclusivo na coluna ou colunas é criado automaticamente se um índice clusterizado da tabela ainda não existir e você não especificar um índice exclusivo não clusterizado". msdn.microsoft.com/en-us/library/ms186342.aspx
Jānis