SARG оценка кардинальности, почему не полное сканирование?

11

Почему нет полного сканирования (в SQL 2008 R2 и 2012)?

Тестовые данные:

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

Когда выполнить запрос:

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

Получите предупреждение (как и ожидалось, потому что сравнивая данные nchar со столбцом varchar):

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

Но затем я вижу план выполнения и вижу, что он не использует полное сканирование, как я ожидал, а вместо этого поиск по индексу.

введите описание изображения здесь

Конечно, это хорошо, потому что в этом конкретном случае выполнение выполняется намного быстрее, чем при полном сканировании.

Но я не могу понять, как SQL-сервер пришел к решению сделать этот план.

Кроме того, если в качестве параметров сортировки сервера используются параметры сортировки Windows на уровне сервера и базы данных параметров сортировки SQL Server, то это приведет к полной проверке того же запроса.

Яниса
источник

Ответы:

8

При сравнении значений разных типов данных SQL Server следует правилам приоритета типа данных . Поскольку nvarchar имеет более высокий приоритет, чем varchar, SQL Server должен преобразовать данные столбца в nvarchar перед сравнением значений. Это означает применение функции к столбцу, и это сделает запрос несортируемым.

SQL Server, тем не менее, делает все возможное, чтобы защитить вас от ваших ошибок, поэтому он использует технику, описанную Полом Уайтом в посте блога « Динамический поиск и скрытые неявные преобразования», для поиска диапазона значений и последующего окончательного сравнения с преобразование значения столбца в nvarchar, в остаточный предикат для фильтрации любых ложных срабатываний.

Как вы заметили, это, однако, не работает, когда сопоставление столбца является сопоставлением SQL. Я полагаю, что причину этого можно найти в статье « Сравнение SQL-сопоставлений и Windows-сопоставлений».

По сути, для сортировки Windows используется один и тот же алгоритм для varchar и nvarchar, где для сортировки SQL используется другой алгоритм для данных varchar и тот же алгоритм, что и для сортировки Windows для данных nvarchar.

Поэтому при переходе от varchar к nvarchar при сопоставлении Windows будет использоваться тот же алгоритм, и SQL Server может выдавать диапазон значений из вашего литерала nvarchar для получения строк из индекса столбца сопоставления SQL varchar. Однако, когда сопоставление столбца varchar представляет собой сопоставление SQL, это невозможно из-за другого используемого алгоритма.


Обновить:

Демонстрация различных порядков сортировки для столбцов varchar с использованием параметров сортировки Windows и SQL.

SQL Fiddle

Настройка схемы MS SQL Server 2014 :

create table T(C varchar(10));

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

Запрос 1 :

select C
from T
order by C collate SQL_Latin1_General_CP1_CI_AS;

Результаты :

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

Запрос 2 :

select C
from T
order by C collate Latin1_General_100_CI_AS;

Результаты :

|   C |
|-----|
|  aa |
| a-b |
|  ac |
Микаэль Эрикссон
источник
0

Следует помнить, что конечные узлы некластеризованного индекса состоят из страниц индекса, которые содержат ключ кластеризации или RID для определения местоположения строки данных.

В своем предложении where вы заявляете, VeryRandomText = N'111'что в VeryRandomText есть некластеризованный индекс (создание индекса создаст некластеризованный индекс, если вы явно не скажете ему создать кластеризованный), самый дешевый способ найти данные - это сканировать индекс, чтобы найти rowid и затем получите данные для строки.

Если бы вы создали кластерный индекс

CREATE clustered Index IX_VeryRandomText On dbo.TestTable (VeryRandomText)

или первичный ключ на VeryRandomText, вы получите сканирование этого индекса.

Смотрите книги в Интернете или здесь: http://www.sqlforge.com/w/Clustered_index,_nonclustered_index,_or_heap

Spörri
источник
Да, я в курсе того, что ты пишешь. Как видите, на TestTableID уже есть кластеризованный индекс. Но дело в том, что если SQL-сервер не может видеть статистику распределения данных столбцов (как в этом случае из-за несоответствия типов данных, которое должно требовать преобразования всех типов данных значений строк), он должен выбрать сканирование кластеризованного индекса в этом случае, а не поиск по индексу ,
Янис
И не всегда дешевле искать / сканировать некластеризованный индекс - когда значения недостаточно различимы или не покрывают индекс, вместо этого может быть дешевле выполнить сканирование кластерного индекса.
Янис
@ Янис, не согласный с вашим сценарием создания индекса, не будет создавать кластеризованный индекс, который вы должны сказать прямо - то же самое, если вы читаете план запроса, поиск по индексу (некластеризованный)
Spörri
«При создании ограничения PRIMARY KEY уникальный кластеризованный индекс для столбца или столбцов автоматически создается, если кластеризованный индекс в таблице еще не существует, и вы не указываете уникальный некластеризованный индекс». msdn.microsoft.com/en-us/library/ms186342.aspx
Янис