Оптимизировать хранимую процедуру SQL - PullRequest
0 голосов
/ 06 мая 2009

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

AND city IN (SELECT DISTINCT city from tOH where clientId = @clientId)
AND state IN (SELECT DISTINCT state from tOH where clientId = @clientId)

* примечание, почти всегда будет возвращаться только одно состояние

Я пытаюсь поместить города в таблицу, а затем использовать таблицу для заполнения городов, но получаю сообщение об ошибке, что @cities не объявлено.

DECLARE @cities TABLE
(
city varchar(200)
);
INSERT INTO @cities (city) SELECT city FROM tOH WHERE clientId = @clientId GROUP BY city

Тогда мое предложение where изменится на

AND city IN (SELECT city from @cities) 

Может кто-нибудь найти хороший способ оптимизации этой хранимой процедуры?

---------------------------- ОБНОВЛЕНИЕ ------------------ ------------------

Соединения слишком медленные. Я думаю, что решение с временной таблицей или табличной переменной будет работать.

Ответы [ 12 ]

8 голосов
/ 06 мая 2009

Не только медленно, но и неверно.

Скажите, что ваш город "Эвансвилл, Висконсин", но в вашей таблице tOH есть записи только для "Эвансвилл, Индиана" и "Милуоки, Висконсин". В настоящее время вы проверяете части города и штата отдельно, поэтому ваш существующий запрос найдет совпадения как для «Эвансвилля», так и для «WI». Это позволит этому городу, хотя на самом деле не должно.

Сделайте это вместо:

INNER JOIN 
  ( 
    SELECT DISTINCT City AS tOHCity, State AS tOHState 
    FROM tOH 
    WHERE ClientID= @ClientID 
  ) cs ON cs.tOHCity = city AND cs.tOHState = state

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

Объедините это с правильной индексацией, и у вас все получится.

4 голосов
/ 07 мая 2009

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

4 голосов
/ 06 мая 2009

Из SQL Hacks:

Когда подзапрос не содержит функции агрегации, скорее всего, вы не нужен подзапрос - вам нужен JOIN.

Итак, вы должны преобразовать свой первый подзапрос (И CITY IN) в JOIN. Если вы не предоставите нам оставшуюся часть запроса, мы не сможем показать вам, как именно, но основой для этого будет добавление City в качестве таблицы, из которой вы выбираете основной запрос.

4 голосов
/ 06 мая 2009

Я бы попробовал JOIN в таблице tOH и отфильтровал весь запрос по clientid. Вы также можете использовать SELECT INTO, чтобы выбросить его во временную таблицу.

1 голос
/ 07 мая 2009

Я предполагаю, что причиной ОТЛИЧИЯ от tOH является то, что название города может существовать в нескольких штатах, а также существует несколько вхождений для штата, поскольку в каждом штате есть несколько городов.

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

select mytable.* 
from mytable m
inner join tOH t on  t.clientid = @clientId 
    and t.city = m.city and t.state = m.state
1 голос
/ 06 мая 2009

Вы, наверное, пробовали это, но моей первой реакцией было бы использовать заполнение временной таблицы с вашими городами. Это может быть то, что вы делаете, и я просто не знаком с синтаксисом, но я всегда использовал:

Create Table #Cities(City varchar(200))

Затем вы заполните таблицу temp и запросите ее, как в вашем примере (INSERT INTO ... и AND city IN (ВЫБЕРИТЕ city из #Cities))

1 голос
/ 06 мая 2009

Используйте EXISTS вместо IN

AND EXISTS(SELECT 1 FROM tOH WHERE tOH.city=main.city AND clientId=@clientId)

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

1 голос
/ 06 мая 2009

Возможно, вы захотите поместить индекс в столбец «Состояние», но вы должны сделать некоторые тесты по этому вопросу. При вставке новых строк вам придется взвесить выгоду индекса от стоимости.

Вы также можете сделать то же самое для столбца Город.

0 голосов
/ 07 мая 2009

Измените свой фильтр IN на существующий. Так что вместо:

AND city IN (SELECT DISTINCT city from tOH where clientId = @clientId)
AND state IN (SELECT DISTINCT state from tOH where clientId = @clientId)

Измените его на что-то вроде:

AND EXISTS ( 
      SELECT 0 FROM tOH t WHERE 
      ClientID = @clientId 
      and t.City = parent.ctiy 
      and t.state = parent.state 
)

Я провел тестирование производительности и всегда обнаруживал, что EXISTS работает быстрее, чем IN. Кроме того, поскольку вы делаете это дважды за одним столом, вы получаете двойной удар.

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

0 голосов
/ 07 мая 2009

Вы можете использовать свой оригинальный запрос (без объединений), внеся следующие исправления:

  1. Создание индекса TOH по номеру клиента, городу, штату
  2. Удалить ключевое слово DISTINCT - это позволяет оптимизатору эффективно использовать этот индекс
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...