Этот простой SQL-запрос может быть оптимизирован?

У меня есть следующий запрос:

SELECT COUNT(*) 
FROM Address adr INNER JOIN 
     Audit a on adr.UniqueId = a.UniqueId
  • на базе данных (1,3 миллиона адресов, больше чем 4 миллиона аудитов)
  • оба столбца UniqueId являются кластеризируемыми первичными ключами

Запрос берет довольно долго для завершения. Я чувствую себя немым, но есть ли какой-либо способ оптимизировать его? Я хочу считать все записи адреса, которые имеют базовое auditable.

Править: все Ваши исходные данные очень ценятся, вот еще некоторые детали:

  • Запрос не будет часто выполняться (это только для проверки), но благодарит за подсказку по индексному представлению, я добавлю это к своему знанию наверняка.
  • Все Адреса имеют связанное, 1 к 1 контролируют. Не все аудиты являются адресами.
  • Запрос занимает больше чем 1 минуту для окончания. Я нахожу это слишком долго для простого количества.
7
задан ibiza 10 October 2010 в 22:49
поделиться

8 ответов

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

SET STATISTICS IO ON
SET STATISTICS TIME ON

SELECT COUNT(*)  
FROM Address adr INNER JOIN  
     Auditable a on adr.UniqueId = a.UniqueId 
OPTION (LOOP JOIN)

SELECT COUNT(*)  
FROM Address adr INNER JOIN  
     Auditable a on adr.UniqueId = a.UniqueId 
OPTION (MERGE JOIN)

SELECT COUNT(*)  
FROM Address adr INNER JOIN  
     Auditable a on adr.UniqueId = a.UniqueId 
OPTION (HASH JOIN)

Редактировать:

Эти объяснения являются концептуальными. SQL Server может выполнять более сложные операции, чем показывают мои примеры. Это концептуальное понимание, сопоставленное с измерением времени и логического ввода-вывода с помощью команд SET STATISTICS и изучением планов выполнения запросов, составляет основу моей техники оптимизации запросов (выросшей за четыре года). Пусть он послужит вам так же хорошо, как и мне.

Подготовка

  • Получите 5 колод карт.
  • Возьмите 1 колоду и создайте родительский набор данных.
  • Возьмите остальные 4 колоды и создайте дочерний набор данных.
  • Упорядочить каждый набор данных по номиналу карты.
  • Пусть m будет количеством карточек в родительском наборе данных.
  • Пусть n будет количеством карточек в дочернем наборе данных.

NestedLoop

  • Возьмите карточку сверху родительского набора данных.
  • Поиск (с использованием двоичного поиска) в дочернем наборе данных для первого совпадения.
  • Искать в дочернем наборе данных от первого совпадения до тех пор, пока не будет найдено несовпадение. Вы нашли все совпадения.
  • Повторите это для каждой карты в родительском наборе данных.

Алгоритм вложенного цикла выполняет итерацию по родительскому набору данных, а затем выполняет поиск в дочернем наборе данных один раз для каждого родителя, что приводит к затратам: m * log (n)

Слияние

  • Снимите карточку с верхней части родительский набор данных.
  • Возьмите карточку в верхней части дочернего набора данных.
  • Если карты совпадают, вытягивайте карты из верхней части каждой колоды, пока в каждой из них не будет найдено несовпадение.Создайте каждую совпадающую пару между родительским и дочерним совпадениями.
  • Если карточки не совпадают, найдите меньшую между родительской и дочерней карточками и возьмите карточку сверху этого набора данных.

Алгоритм объединения слиянием выполняет итерацию родительского набора данных один раз и дочернего набора данных один раз, в результате чего стоимость: m + n. Он полагается на заказываемые данные. Если вы попросите объединить неупорядоченные данные, вам придется выполнить операцию упорядочивания! В результате стоимость составит (m * log (m)) + (n * log (n)) + m + n. В некоторых случаях даже это может быть лучше, чем вложенный цикл.

Хэш

  • Получите карточный стол.
  • Возьмите каждую карту из родительского набора данных и поместите ее на карточный стол, где вы сможете ее найти (не обязательно иметь какое-либо отношение к стоимости карты, просто должно быть удобно для вас).
  • Возьмите каждую карту из дочернего набора данных, найдите соответствующую родительскую карту на картонном столе и создайте соответствующую пару.

Алгоритм хэш-соединения выполняет итерацию родительского набора данных один раз и дочернего набора данных один раз, в результате чего стоимость: m + n. Он полагается на наличие достаточно большой карточной таблицы, чтобы вместить все содержимое родительского набора данных.

11
ответ дан 6 December 2019 в 08:42
поделиться

Настоящая проблема - соединение с вложенным циклом. Для каждых 1,4 миллиона строк в таблице адресов вы выполняете поиск индекса в таблице Auditble. Это означает 1,4 млн чтений корневого блока, блока ветвления и конечного блока, всего 4,2 млн чтений блока. Весь индекс, вероятно, состоит всего из 5К блоков или около того ... он должен выполнять хеш-соединение, чтобы он считал оба индекса один раз и хешировал их.

Если вы думаете, что эти таблицы большие, я предполагаю, что это небольшая коробка без большого количества памяти. Вы должны убедиться, что у вас достаточно памяти, выделенной для размещения всего индекса в памяти, чтобы сделать хеш-соединение эффективным.

2
ответ дан 6 December 2019 в 08:42
поделиться

Я бы сказал, что отсутствует индекс по внешнему ключу.

  • 1.4 миллиона и 4 миллиона - это не большие таблицы, они маленькие. Скажите масштабно, когда вы просматриваете 500 миллионов записей, пожалуйста.

  • Для реального ответа нам нужен план выполнения / план запроса, чтобы мы могли видеть, что происходит.

  • И было бы неплохо узнать, что такое «Long» в вашем мире (учитывая, что вы думаете, что 4 миллиона строк - это много). На этот вопрос никогда не ответят за 1 секунду - так чего же вы ожидаете и что происходит?

  • Но держу пари, что у вас отсутствует индекс. Короче говоря, я бы начал указывать на оборудование (потому что я тоже видел в этом причину плохой производительности).

0
ответ дан 6 December 2019 в 08:42
поделиться

Выполнение предложения EXISTS обходится дешевле, чем INNER JOIN.

select COUNT(adr.UniqueId)
    from Addresses adr
    where EXISTS (
        select 1
            from Auditables aud
            where aud.UniqueId = adr.UniqueId
    )

Это соответствует вашим потребностям?

Примечание. Гиды очень дороги для движка базы данных.

1
ответ дан 6 December 2019 в 08:42
поделиться

Не уверен, будет ли это быстрее, но вы можете попробовать следующее

SELECT COUNT(adr.UniqueID) FROM Address adr INNER JOIN Auditable a on adr.UniqueId = a.UniqueId

Это должно дать вам такое же количество, потому что unqieieid никогда не будет нулевым.

0
ответ дан 6 December 2019 в 08:42
поделиться

Для больших таблиц, таких как эти, вы можете захотеть разделить данные для увеличения производительности запросов. Также, если вы еще не сделали этого, попробуйте запустить Tuning Advisor, чтобы узнать, есть ли дополнительные индексы, которые могут оказаться полезными. Кроме того, проводили ли вы в последнее время реорганизацию кластерных индексов - является ли это задачей, входящей в пакет технического обслуживания? Во многих случаях это также значительно повышает производительность.

0
ответ дан 6 December 2019 в 08:42
поделиться

Является ли Auditable.UniqueID ссылкой внешнего ключа на Address.UniqueID, что означает, что в Auditable нет значений, которые также не существуют в Address?

Если да, то это может работать и может быть быстрее:

SELECT COUNT(DISTINCT Auditable.UniqueID)
FROM Auditable

Примечание: Это также предполагает, что UniqueID уникален (/первичный ключ) в таблице Address, но не уникален в таблице Auditable

.
1
ответ дан 6 December 2019 в 08:42
поделиться

Если этот запрос выполняется часто и он должен быть очень быстрым, создайте его материализованное индексированное представление. Будут небольшие накладные расходы на INSERT/UPDATE/DELETEs, но этот запрос будет почти мгновенным. Агрегаты могут быть предварительно вычислены и сохранены в индексе, чтобы свести к минимуму дорогостоящие вычисления во время выполнения запроса.

Повышение производительности с помощью индексированных представлений SQL Server 2005

6
ответ дан 6 December 2019 в 08:42
поделиться
Другие вопросы по тегам:

Похожие вопросы: