2010-05-12 4 views
7

У меня есть следующий запрос:Можно ли оптимизировать этот простой SQL-запрос?

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

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

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

  • Запрос не будет запускать часто (это только для проверки), но спасибо за индексируемой кончике зрения, Я добавлю это, насколько мне известно.
  • Все адреса имеют связанный с аудитом 1-к-1. Не все аудиты - это адреса.
  • Выполнение запроса занимает более 1 минуты. Я нахожу это слишком долго для простого подсчета.
+0

Что такое «долгое время», есть ли у вас план выполнения запроса? Можете ли вы предоставить схему для таблиц? Если его хорошо проиндексировать, как вы говорите, нет никаких оснований для этого в любое время. –

+2

Можете ли вы предоставить какие-либо сведения о плане запроса? (например, скриншот из SQL Server Management Studio или плана XML). Также, какая доля адресов имеет базовую аудиторию? –

+0

Вы пробовали выбрать один из индексированных столбцов? В прежние времена было быстрее сказать COUNT (adr.uniqueid), потому что он мог читать все, что вам нужно, из индекса, и никогда не приходил к самому столу. Я бы попробовал это. Также проверьте, обновляется ли статистика на столе. – MJB

ответ

11

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

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) 

Edit:

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

Настройка

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

NestedLoop

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

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

Объединить

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

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

Hash

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

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

+0

Удивительно! Merge join - это повышение производительности на 100%, в два раза быстрее. Большое вам спасибо, я не знал, что мы можем указать, какой тип соединения использовать с запросом, я обязательно прочитаю об этом подробнее. Еще раз спасибо, я считаю, что это приемлемый ответ :) Не возражаете ли вы объяснить немного больше, почему объединение слияния происходит гораздо быстрее? – ibiza

+0

+1 +1 Ну, я не знал об этом! Спасибо за предоставление этого решения. Я узнал от этого! @ibiza: Спасибо за этот комментарий, в котором говорится о повышении производительности, чтобы дать представление о идее. –

+0

он также должен быть в зависимости от контекста ... Мне интересно, как определить, для какого случая каждый тип присоединения наилучший выбор – ibiza

0

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

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

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

1

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

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

SELECT COUNT(DISTINCT Auditable.UniqueID) 
FROM Auditable 

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

+0

Это не работает для меня, так как существует гораздо больше аудиталов, чем адресов. Все адреса должны иметь аудиторию, но для многих других есть и звуковые сигналы. Я хочу только подсчет аудита для Адресов. – ibiza

6

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

Improving Performance with SQL Server 2005 Indexed Views

+0

+1. Обратите внимание, что вы должны использовать 'COUNT_BIG' вместо' COUNT', чтобы представление было индексируемым. – Quassnoi

+0

спасибо за отзыв :) однако этот запрос не будет выполняться часто, но я сохраню это знание точно – ibiza

+1

SQL Tuning не должен полагаться на физическое сохранение ответов в качестве первого шага. Материализация ответа должна быть последней инстанцией. –

0

Отсутствует индекс внешнего ключа, я бы сказал.

  • 1,4 миллиона и 4 миллиона не являются большими столами, они небольшие. Скажите, если вы пройдете через 500 миллионов записей, пожалуйста.

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

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

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

0

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

2

Настоящей проблемой является объединение вложенных циклов. Для каждого 1,4 миллиона строк в таблице Address вы делаете индекс Seek в таблицу Auditble. Это означает, что корневой блок 1.4M, блок ветвления и блок листа читают для чтения в 4.2M блоков. Весь индекс, вероятно, всего 5K блоков или около того ... он должен делать хеш-соединение, поэтому он читает оба индекса один раз и хеширует через них.

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

+0

Я предполагаю blevel = 2. –

+0

привет и спасибо за ваш комментарий. Я запускаю это на процессоре 4core с 4-гигабайтным барабаном ... не топ, но неплохо. – ibiza

+0

В ноутбуках есть 4gb-RAM в эти дни, возможно, даже нетбуки –

1

Предложение EXISTS дешевле запускать, чем INNER JOIN.

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

Это вам подходит?

N.B. Гиды очень дороги для механизма базы данных.

+0

спасибо за указатели, EXISTS дает повышение производительности ~ 10% – ibiza

Смежные вопросы