LIKE використовує індекс, CHARINDEX ні?


22

Це питання пов'язане з моїм старим питанням . Наведений нижче запит знадобився від 10 до 15 секунд:

SELECT [customer].[Customer name],[customer].[Sl_No],[customer].[Id]
FROM [company].dbo.[customer]
WHERE (Charindex('123456789',CAST([company].dbo.[customer].[Phone no] AS VARCHAR(MAX)))>0) 

У деяких статтях я бачив, що використання CASTта CHARINDEXне буде користі від індексації. Також є деякі статті, які говорять про те, що використання LIKE '%abc%'індексації не піде на користь LIKE 'abc%':

http://bytes.com/topic/sql-server/answers/81467-using-charindex-vs-like-where /programming/803783/sql-server-index-any-improvement-for -like-запити http://www.sqlservercentral.com/Forums/Topic186262-8-1.aspx#bm186568

У моєму випадку я можу переписати запит як:

SELECT [customer].[Customer name],[customer].[Sl_No],[customer].[Id]
FROM [company].dbo.[customer]
WHERE [company].dbo.[customer].[Phone no]  LIKE '%123456789%'

Цей запит дає такий же вихід, як і попередній. Я створив некластеризований індекс для стовпця Phone no. Коли я виконую цей запит, він запускається всього за 1 секунду . Це величезна зміна порівняно з 14 секундами раніше.

Як отримує LIKE '%123456789%'користь від індексації?

Чому перераховані статті стверджують, що це не покращить ефективність роботи?

Я спробував переписати запит для використання CHARINDEX, але продуктивність все ще повільна. Чому CHARINDEXіндексація не виграє, як видається, LIKEзапит?

Запит із використанням CHARINDEX:

SELECT [customer].[Customer name],[customer].[Sl_No],[customer].[Id]
 FROM [Company].dbo.[customer]
 WHERE ( Charindex('9000413237',[Company].dbo.[customer].[Phone no])>0 ) 

План виконання:

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

Запит із використанням LIKE:

SELECT [customer].[Customer name],[customer].[Sl_No],[customer].[Id]
 FROM [Company].dbo.[customer]
 WHERE[Company].dbo.[customer].[Phone no] LIKE '%9000413237%'

План виконання:

План запиту LIKE

Відповіді:


28

Як LIKE '% 123456789%' отримує користь від індексації?

Лише трохи. Процесор запитів може сканувати весь некластеризований індекс, шукаючи відповідність замість усієї таблиці (кластерний індекс). Некластеризовані індекси, як правило, менше, ніж таблиця, на якій вони побудовані, тому сканування некластеризованого індексу може бути швидшим.

Мінус полягає в тому, що будь-які стовпці, необхідні для запиту, які не включені до некластеризованого визначення індексу, повинні шукатись у базовій таблиці в рядку.

Оптимізатор приймає рішення між скануванням таблиці (кластерним індексом) та скануванням некластеризованого індексу з пошуковими запитами на основі кошторису витрат. Розрахункові витрати залежать значною мірою від того, скільки рядків оптимізатор очікує , що ваш LIKEабо CHARINDEXпредикат для вибору.

Чому перераховані статті стверджують, що це не покращить ефективність роботи?

Для LIKEумови, яка не починається з підстановки, SQL Server може виконати часткове сканування індексу замість того, щоб просканувати все. Наприклад, LIKE 'A%можна правильно оцінити, перевіривши лише записи індексу >= 'A'та < 'B'(точні граничні значення залежать від зіставлення).

Цей вид запиту може використовувати здатність пошуку індексів b-дерева: ми можемо перейти до першої записи >= 'A'за допомогою b-дерева, а потім сканувати вперед в порядку індексного ключа, поки не досягнемо запису, який не проходить < 'B'тест. Оскільки нам потрібно застосувати LIKEтест лише до меншої кількості рядків, ефективність, як правило, краща.

Навпаки, LIKE '%Aне можна перетворити на часткове сканування, оскільки ми не знаємо, з чого почати чи закінчити; будь-який запис може закінчитися 'A', тому ми не можемо покращити сканування цілого індексу та тестування кожного рядка окремо.

Я спробував переписати запит для використання CHARINDEX, але продуктивність все ще повільна. Чому CHARINDEXіндексація не виграє, як видається, запит LIKE?

Оптимізатор запитів має однаковий вибір між скануванням таблиці (кластерний індекс) і скануванням некластеризованого індексу (з пошуковими запитами) в обох випадках.

Вибір робиться між двома на основі оцінки витрат . Так трапляється, що SQL Server може дати різні оцінки для двох методів. Для LIKEформи запиту, оцінка може бути в змозі використовувати спеціальну рядкову статистику для створення досить точної оцінки. CHARINDEX > 0Форма проводить оцінку , засновану на гіпотезі.

Різних оцінок достатньо, щоб оптимізатор вибирав CHARINDEXкластерне сканування індексів та некластерне сканування індексів з пошуку LIKE. Якщо ви змусите CHARINDEXзапит використовувати некластеризований індекс із підказкою, ви отримаєте той же план LIKE, що і для , а продуктивність буде приблизно однаковою:

SELECT
    [Customer name],
    [Sl_No],
    [Id]
FROM dbo.customer WITH (INDEX (f))
WHERE 
    CHARINDEX('9000413237', [Phone no]) >0;

Кількість рядків, оброблених під час виконання, буде однаковим для обох методів, просто LIKEформа дає в цьому випадку більш точну оцінку, тому оптимізатор запитів вибирає кращий план.

Якщо вам LIKE %thing%часто потрібні пошукові запити, можливо, ви захочете розглянути техніку, про яку я писав у пошуку в рядках Trigram Wildcard String на SQL Server .


16

SQL Server веде статистику по підрядках у стовпчиках рядків у вигляді спроб, які можна використовувати за LIKEзапитом, а не за допомогою CHARINDEX.

Докладніше про це див. У розділі " Підсумкова статистика рядків" .

Кілька важливих застережень полягають у тому, що будь-яке усунення підстановочних кодів повинно здійснюватися за допомогою власного технічного квадратного дужок, а не за ESCAPEключовим словом, а для рядків довжиною більше 80 символів використовуються лише перший та останній 40 символів.

WHERE ( Charindex('9000413237',[Company].dbo.[customer].[Phone no])>0 ) 

просто використовуватиме стандартну здогадку для предикату нерівності, що повернеться 30% рядків.

Оцінка LIKEзапиту (у вашому випадку), напевно, значно менше рядків відповідатиме предикату.

Зауважте, що провідна маска все ще не дозволяє шукати індекс. Цілий індекс все ще сканується, але він використовує інший, який вужчий, ніж кластерний індекс. Більш вузький індекс не охоплює всіх стовпців, які використовуються в запиті, тому другий план вимагає пошуку ключа для отримання відсутніх стовпців.

Цей план навряд чи буде обраний із оцінкою 30%. SQL Server вважатиме, що дешевше сканувати весь кластерний індекс і уникнути багатьох пошукових запитів. Дивіться цю статтю на переломному рівні для додаткових прикладів.


мені незрозуміло з вашим поясненням. Ви хочете сказати, що вживання подібного краще, ніж charindex?
ІТ-дослідник

3
@ITresearcher - Так, можливо, замість того, щоб просто використовувати загальну здогадку про те, скільки рядків буде відповідати умові ( 30%), вона може переглянути LIKEшаблон, що надається, та підсумкову статистику рядків і отримати більш точну оцінку. Озброївшись цим, він може вибрати інший і більш відповідний план.
Мартін Сміт

3
... або, в "гіршому випадку", той же план.
Аарон Бертран
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.