Порядок полів у складеному порядку індексу з високою селективністю та низькою селективністю


11

У мене є таблиця SQL Server із понад 3 мільярдами рядків. Один із моїх запитів займає надзвичайно багато часу, тому я розглядаю можливість його оптимізації. Запит виглядає так:

SELECT [Enroll_Date]
      ,Count(*) AS [Record #]
      ,Count(Distinct UserID) AS [User #]
  FROM UserTable
  GROUP BY [Enroll_Date]

[Enroll_Date] - це стовпець із низькою селективністю з менш ніж 50 можливими значеннями, тоді як колонка UserID - це стовпець високої селективності з більш ніж 200 мільйонами різних значень. На основі своїх досліджень я вважаю, що я повинен створити некластеризований композитний індекс на цих двох стовпцях, а теоретично стовпець високої селективності повинен бути першим стовпцем. Але я не впевнений, що в моєму випадку це буде спрацьовувати, оскільки я використовую стовпець із низькою селективністю в групі за пунктом.

У цій таблиці немає кластерного індексу.


Чи можете ви розмістити фактичний план виконання xml (використовуйте пастібін та посилайте його тут)? Яку версію сервера sql ви використовуєте?
Кін Шах

3
Індекс із високоселективним стовпцем спочатку буде марний для конкретного запиту.
ypercubeᵀᴹ

Найкраще практично використовувати стовпчик із більшою селективністю як перший стовпець ключа в індексі (як правило). У цьому сценарії, як ви здогадалися, він вам зовсім не допомагає. Можливо, вам знадобляться два індекси! Що відбувається, коли ви використовуєте enroll_date перший та user_id другий?
паульбарбін

Відповіді:


12

Як альтернатива рішенню @ AaronBertrand (якщо ви не можете або не хочете створити індексований вигляд), я рекомендую вам створити індекс на (Enroll_Date, UserID). Якщо такий тип питань є дуже поширеним у вашому столі, це, мабуть, навіть має бути ваш кластерний індекс.

Як правило, я б не рекомендував індекси високої вибірковості як загальної "найкращої практики", а скоріше подивіться, який індекс дасть вашому запиту найкращі показники.

Індекс на (Enroll_Date, UserID)дасть вашому запиту високооптимізований, не блокуючий план запитів із потоковими агрегатами.

План поточного сукупного запиту

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


Смішно, 4 секунди один від одного і така ж відповідь.
usr

11

Відповідь Ааронів - чудове рішення. Я відповім на питання, припускаючи, що ви не хочете використовувати такий підхід.

Запит, який ви опублікували, зазвичай виконуватиметься спочатку групуванням (Enroll_Date, UserID), а потім знову (Enroll_Date). Ця оптимізація є новою для SQL Server 2012. Вона набуває чинності у випадку єдиного COUNT DISTINCT.

Індекс у цих двох стовпцях у певному порядку (Enroll_Date, UserID)буде достатнім для отримання ефективного плану, який поєднує сканування індексу у дві послідовні агрегати потоку. Протилежний порядок не дозволив би зробити цей план.

Тому використовуйте замовлення (Enroll_Date, UserID). Тут у вас немає вибору.


5 секунд один і той же розчин. Добре зіграно, сер. :)
Даніель Хатмахер

@DanielHutmacher OMG, чи вдасться ми майже втретє відповідати нашим постам ?! +1 вам! Як я не міг відповісти ідентичну відповідь?
usr

Глюк у матриці. :)
Даніель Хатмахер

Велике спасибі. Я створюю індекс і опублікую покращення після його завершення. Версія сервера - це Microsoft SQL Server 2008 R2 на AWS, але, мабуть, це все-таки є єдиним вибором незалежно.
Роздум

@Thinkinger у випадку, якщо ви не приймаєте підходу Aarons, у вас є жорсткий вибір :)
usr

11

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

CREATE VIEW dbo.MyIndexedView
WITH SCHEMABINDING
AS 
  SELECT Enroll_Date, UserID, RawCount = COUNT_BIG(*)
  FROM dbo.UserTable
  GROUP BY Enroll_Date, UserID;
GO

CREATE UNIQUE CLUSTERED INDEX CIX_miv ON dbo.MyIndexedView(Enroll_Date, UserID);

Для створення цього знадобиться деякий час, і, звичайно, знадобиться обслуговування протягом усіх операцій DML, як і індекс базової таблиці.

Тепер запит проти цього представлення був би досить схожим - кожен рядок у представленні тепер представляє собою окреме комбінацію користувача / дати, тому ця цифра може бути обчислена одним COUNT (*), тоді як загальна кількість рядків у базовій таблиці становить вже частково зведені для вас, тепер вам просто потрібно буде додати їх за допомогою SUM на дату:

SELECT Enroll_Date, 
  [Record #] = SUM(RawCount),
  [User #] = COUNT(*)
FROM dbo.MyIndexedView WITH (NOEXPAND)
GROUP BY Enroll_Date; 

Додано підказку NOEXPAND, згадавши це та це .

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

І якщо ви часто використовуєте одні й ті ж загальні пропозиції WHERE щодо Enroll_Date для конкретних, чітко визначених діапазонів (скажімо, поточного кварталу чи року на сьогоднішній день), ви можете додати відповідні відфільтровані індекси, які ще більше зменшують цей ввід / вивід (але завжди є компроміс).

Ви також можете розглянути можливість кластеризованого індексу на базову таблицю. Це, здається, не є одним з тих дуже рідкісних випадків використання, які отримують користь від купи.


Я щойно підтвердив нашу ІТ, і, здається, я не можу створити такий вид зору. Але все-таки оцініть свою пораду, і вона допоможе іншим, хто може її використати.
Роздум

1
Чи вважає ваш ІТ важлива різниця між індексованим поданням та додатковими чи різними індексами на базовій таблиці? Не є бойовим, просто цікавим, бо багато людей мають помилкові уявлення про індексовані погляди. Мені подобається думати про них як про додатковий, струнший кластерний індекс на столі, але з меншою кількістю рядків.
Аарон Бертран

@Thinkinger також, індексовані погляди не є лише EE. Індексований вид відповідності є ЕЕ-тільки. Ви можете безпосередньо націлити їх за допомогою NOEXPAND.
usr
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.