Оцінка кардинальності 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 literal, щоб отримати рядки з індексу стовпчика varchar SQL collation. Однак, коли порівняння стовпчика varchar - це SQL Collation, що неможливо через різний використовуваний алгоритм.


Оновлення:

Демонстрація замовлень різного сортування для стовпчиків varchar за допомогою вікон та 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 для пошуку рядка даних.

У вашому пункті де ви VeryRandomText = N'111'заявляєте, оскільки в VeryRandomText є індекс, який не кластеризується (створення індексу створить некластеризований індекс, якщо ви прямо не скажете йому створити кластер), найдешевшим способом пошуку даних є сканування індексу для пошуку рядкового і потім отримайте дані для рядка.

Якщо ви створили б кластерний індекс

CREATE clustered Index IX_VeryRandomText On dbo.TestTable (VeryRandomText)

або первинний ключ у VeryRandomText, ви отримаєте сканування цього індексу.

Дивіться книги онлайн або тут: http://www.sqlforge.com/w/Clustered_index,_nonclustered_index,_or_heap


Так, я знаю, про що ти пишеш. Як бачите, в TestTableID вже є кластерний індекс. Але річ у тому, що якщо SQL-сервер не може бачити статистику розподілу даних стовпців (як у цьому випадку, через невідповідність типу даних, яка потребує перетворення всіх значень рядкових значень рядків), він повинен вибрати в цьому випадку кластерне сканування індексу, а не пошук індексу .
Jānis

І не завжди найдешевше шукати / сканувати некластеризований індекс - коли значення недостатньо виразні або не покривають індекс, може бути дешевше зробити замість цього кластерне сканування індексів.
Яніс

@ Jānis, який не відповідає вашому сценарію створення індексу, не створить кластерний індекс, який ви повинні сказати так прямо - те саме, якщо ви читаєте план запитів, пошук запитів (некластеризовано)
Spörri

"Коли ви створюєте обмеження PRIMARY KEY, унікальний кластерний індекс на стовпчику або стовпцях автоматично створюється, якщо кластерний індекс у таблиці ще не існує, і ви не вказуєте унікальний некластеризований індекс." msdn.microsoft.com/en-us/library/ms186342.aspx
Jānis
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.