Производительность запроса - внутреннее соединение - PullRequest
0 голосов
/ 18 февраля 2009

У меня есть этот запрос, и я хочу улучшить производительность:

SELECT 
    OrarioA, 
    OrarioB,
    IDOrario,
    IDDettaglioOrarioA, 
    IDDettaglioOrarioB
FROM
(
    SELECT 
        Tb_01.Orario AS OrarioA,
        Tb_02.Orario AS OrarioB,
        Tb_01.IDDettaglioOrariLinee AS IDDettaglioOrarioA,
        Tb_02.IDDettaglioOrariLinee AS IDDettaglioOrarioB,
        Tb_01.IDOrario,
        ROW_NUMBER() OVER (PARTITION BY Tb_01.Orario, Tb_02.Orario ORDER BY Tb_01.IDOrario DESC) AS Row
    FROM
        (
            SELECT Orario, IDDettaglioOrariLinee, IDOrario 
            FROM DettaglioOrariLinee 
            WHERE IDRelLineeStazionamenti = @IDRelA
        ) AS Tb_01
            INNER JOIN
        (
            SELECT Orario, IDDettaglioOrariLinee, IDOrario 
            FROM DettaglioOrariLinee 
            WHERE IDRelLineeStazionamenti = @IDRelB
        ) AS Tb_02
            ON Tb_01.IDOrario = Tb_02.IDOrario
            INNER JOIN
        (
            SELECT IDOrario 
            FROM Periodi 
            WHERE 
            (
                @Data = 0 OR
                (
                        @Data >= CAST(CAST(DATEPART(DAY, PeriodoDal) AS VARCHAR)+'/'+ CAST(DATEPART(MONTH, PeriodoDal)AS VARCHAR) +'/'+ CAST(DATEPART(YEAR,@Data)AS VARCHAR) AS DATETIME)
                    AND 
                        @Data <= CAST(CAST(DATEPART(DAY, PeriodoAl) AS  VARCHAR)+'/'+ CAST(DATEPART(MONTH, PeriodoAl)AS VARCHAR) +'/'+ CAST(DATEPART(YEAR,@Data)AS VARCHAR) AS DATETIME)
                )
            )
        ) Tb_Periodi
            ON Tb_01.IDOrario = Tb_Periodi.IDOrario        --dbo.periodi ON Tb_01.IDOrario = dbo.periodi.IDOrario
            INNER JOIN                                     --dbo.relgiornisettimanaorarilinee ON Tb_01.IDOrario = dbo.relgiornisettimanaorarilinee.IDOrario
        (
            SELECT IDOrario 
            FROM relgiornisettimanaorarilinee
            WHERE @IDGiorno = 0 OR IDGiorno = @IDGiorno
        ) Tb_Giorni
            ON Tb_01.IDOrario = Tb_Giorni.IDOrario
    WHERE
        (
            @Orario = '' OR DATEDIFF(minute, CAST(@ORARIO AS DATETIME), CAST(Tb_01.Orario AS DATETIME)) >=0
        ) AND (
            DATEDIFF(minute, CAST(Tb_01.Orario AS DATETIME), CAST(Tb_02.Orario AS DATETIME)) >=0
        ) AND (
            @IDOrari = '' OR Tb_01.IDOrario NOT IN (SELECT CAST(s AS INT) AS IDOrario FROM dbo.Split(',', @IDOrari) AS Split_1)
        )
        /*
        AND
            (
                   @Data = 0
                OR
                    (
                            @Data >= CAST(CAST(DATEPART(DAY, PeriodoDal) AS VARCHAR)+'/'+ CAST(DATEPART(MONTH, PeriodoDal)AS VARCHAR) +'/'+ CAST(DATEPART(YEAR,@Data)AS VARCHAR) AS DATETIME)
                        AND 
                            @Data <= CAST(CAST(DATEPART(DAY, PeriodoAl) AS  VARCHAR)+'/'+ CAST(DATEPART(MONTH, PeriodoAl)AS VARCHAR) +'/'+ CAST(DATEPART(YEAR,@Data)AS VARCHAR) AS DATETIME)
                    )
            )
        AND
            (@IDGiorno = 0 OR IDGiorno = @IDGiorno)
        */
) As Tb_New
WHERE ROW = 1

OPTION (MAXRECURSION 0);

Я хочу отфильтровать Tb_Periodi и Tb_Giorni с помощью IDOrario.

Как мне улучшить этот запрос?


Да. Я использую SQL Server 2005. Это запрос вызывается много раз, по 50 раз за запрос страницы. Это в цикле. Можно ли использовать кеш. Я не знаю, увеличить производительность. Я перепробовал все!

Я заметил, что какой-то запрос повторяется много раз в цикле, согласно вам, как я могу воспользоваться этим?

Ответы [ 8 ]

3 голосов
/ 18 февраля 2009

При оптимизации производительности, как правило, лучше (и проще) определить, где находятся узкие места. Вы пытались использовать Query Analyzer ?

1 голос
/ 24 марта 2009

Я подозреваю, что это в конечном итоге станет проблемой проектирования базы данных, а не проблемой оптимизации запросов. Тем не менее, шаги для анализа этого запроса были изложены выше. Для повторения:

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

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

Одна вещь, которая, как я вижу, может повысить производительность (в зависимости от специфики ваших данных), это попытаться установить порядок доступа данных к таблице DettaglioOrariLinee. Например:

    ;WITH DettaglioOrariLineePeriodi AS
(
        SELECT Orario, IDDettaglioOrariLinee, IDOrario 
        FROM DettaglioOrariLinee
        WHERE IDOrario IN ( SELECT IDOrario --This is still an inner join
                            FROM Periodi 
                            WHERE 
                            (
                                @Data = 0 OR
                                (
                                    @Data >= CAST(CAST(DATEPART(DAY, PeriodoDal) AS VARCHAR)+'/'+ CAST(DATEPART(MONTH, PeriodoDal)AS VARCHAR) +'/'+ CAST(DATEPART(YEAR,@Data)AS VARCHAR) AS DATETIME)
                                    AND 
                                    @Data <= CAST(CAST(DATEPART(DAY, PeriodoAl) AS VARCHAR)+'/'+ CAST(DATEPART(MONTH, PeriodoAl)AS VARCHAR) +'/'+ CAST(DATEPART(YEAR,@Data)AS VARCHAR) AS DATETIME)
                                )
                            )
                        )
)
SELECT 
    OrarioA, 
    OrarioB,
    IDOrario,
    IDDettaglioOrarioA, 
    IDDettaglioOrarioB
FROM
(
    SELECT 
        Tb_01.Orario AS OrarioA,
        Tb_02.Orario AS OrarioB,
        Tb_01.IDDettaglioOrariLinee AS IDDettaglioOrarioA,
        Tb_02.IDDettaglioOrariLinee AS IDDettaglioOrarioB,
        Tb_01.IDOrario,
        ROW_NUMBER() OVER (PARTITION BY Tb_01.Orario, Tb_02.Orario ORDER BY Tb_01.IDOrario DESC) AS Row
    FROM
    (
        SELECT Orario, IDDettaglioOrariLinee, IDOrario
        FROM DettaglioOrariLineePeriodi                 --NEW CTE
        WHERE IDRelLineeStazionamenti = @IDRelA
    ) AS Tb_01
    INNER JOIN
    (
        SELECT Orario, IDDettaglioOrariLinee, IDOrario 
        FROM DettaglioOrariLineePeriodi                 --NEW CTE
        WHERE IDRelLineeStazionamenti = @IDRelB
    ) AS Tb_02
    ON Tb_01.IDOrario = Tb_02.IDOrario
    INNER JOIN --dbo.relgiornisettimanaorarilinee ON Tb_01.IDOrario = dbo.relgiornisettimanaorarilinee.IDOrario
    (
        SELECT IDOrario 
        FROM relgiornisettimanaorarilinee
        WHERE @IDGiorno = 0 OR IDGiorno = @IDGiorno
    ) Tb_Giorni
    ON Tb_01.IDOrario = Tb_Giorni.IDOrario
    WHERE
    (
    @Orario = '' OR DATEDIFF(minute, CAST(@ORARIO AS DATETIME), CAST(Tb_01.Orario AS DATETIME)) >=0
    ) AND (
    DATEDIFF(minute, CAST(Tb_01.Orario AS DATETIME), CAST(Tb_02.Orario AS DATETIME)) >=0
    ) AND (
    @IDOrari = '' OR Tb_01.IDOrario NOT IN (SELECT CAST(s AS INT) AS IDOrario FROM dbo.Split(',', @IDOrari) AS Split_1)
    )
/*
AND
(
@Data = 0
OR
(
@Data >= CAST(CAST(DATEPART(DAY, PeriodoDal) AS VARCHAR)+'/'+ CAST(DATEPART(MONTH, PeriodoDal)AS VARCHAR) +'/'+ CAST(DATEPART(YEAR,@Data)AS VARCHAR) AS DATETIME)
AND 
@Data <= CAST(CAST(DATEPART(DAY, PeriodoAl) AS VARCHAR)+'/'+ CAST(DATEPART(MONTH, PeriodoAl)AS VARCHAR) +'/'+ CAST(DATEPART(YEAR,@Data)AS VARCHAR) AS DATETIME)
)
)
AND
(@IDGiorno = 0 OR IDGiorno = @IDGiorno)
*/
) As Tb_New
WHERE ROW = 1

OPTION (MAXRECURSION 0);

Другая вещь, которую вы могли бы попробовать, это заменить строку

@IDOrari = '' OR Tb_01.IDOrario NOT IN (SELECT CAST(s AS INT) AS IDOrario FROM dbo.Split(',', @IDOrari) AS Split_1)

С

@IDOrari = '' OR (@IDOrari NOT LIKE Tb_01.IDOrario + ',%' AND @IDOrari NOT LIKE '%,' + Tb_01.IDOrario + ',%' AND @IDOrari NOT LIKE '%,' + Tb_01.IDOrario)

3) Если вы не можете найти ничего, что помогает, или у вас есть куча табличных сканирований, следующим шагом будет посмотреть, можете ли вы изменить индексы для этих таблиц. Я бы порекомендовал загрузить профилировщик SQL (вам может понадобиться установить его с вашего диска sql) и создать новую трассировку. Как только трассировка началась, выполните операцию, которая занимает слишком много времени, а затем остановите трассировку. Сохраните трассировку в файл, а затем запустите советник по настройке ядра базы данных. Загрузите файл, выберите базу данных для настройки и нажмите «Начать трассировку».

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

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

4) Если ничего из вышеперечисленного не даст вам необходимого улучшения производительности, вы можете изучить денормализацию таблицы DettaglioOrariLinee, чтобы в каждой строке были данные для сторон A и B. Это, вероятно, не очень хорошая идея.

1 голос
/ 18 февраля 2009

ОК,

  1. Работает ли запрос на самом деле? Нет смысла пытаться улучшить производительность запроса, возвращающего неправильные наборы результатов.

  2. У вас есть список планов тестирования с известными наборами результатов, которые вы можете сравнить. Улучшение SQL-запросов - ОЧЕНЬ хороший пример разработки, управляемой тестами, поскольку при реструктуризации запроса может быть ОЧЕНЬ легко вводить ошибки (неверные результаты).

  3. Опишите, что вы ожидаете от запроса НА АНГЛИЙСКОМ ЯЗЫКЕ - дайте нам возможность понять цель запроса.

  4. Опишите ваш набор данных (размер, индексы, распределение данных)

  5. Каковы ваши ожидания? Должен ли этот запрос завершиться за 1 секунду, 1 минуту, 1 час? Сколько времени это занимает? Сколько раз его вызывают (много раз в секунду или раз в неделю?)

Не думаю, что это справедливо, если ваш вопрос устарел - это правильный вопрос, но ему просто нужно больше информации. Удачи.

1 голос
/ 18 февраля 2009

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

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

0 голосов
/ 18 марта 2009

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

Обязательно очищайте кеш-таблицу, когда это необходимо (например, с помощью триггеров или других средств)

В зависимости от вашей системы, использование кеша - хороший или плохой вариант, я не могу сказать.

Кроме того, если этот код еще не находится в хранимой процедуре, поместите его в одну, поскольку для SQL Server будет проще кэшировать планы и т. Д.

Я бы предложил исправить плохой код, а не скрывать проблему с кешем.

ура

/ л

0 голосов
/ 18 марта 2009

Хороший код: -o

Как уже говорили многие, вам трудно помочь, когда мы ничего не знаем о данных или о том, чего вы пытаетесь достичь.

Но я могу дать вам несколько советов:

  • Проверьте http://sql -server-performance.com для получения информации о том, кого анализировать и повышать производительность запросов. У них также есть форум, но если вы не дадите больше информации, они вряд ли смогут вам помочь.

  • ИЛИ в предложениях WHERE или ON приводят к снижению производительности

  • IN также ведет к снижению производительности
  • Функции (UDF или встроенные) в предложениях WHERE или ON также плохо влияют на производительность.

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

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

Множество небольших запросов может привести к ухудшению производительности по сравнению с большим хорошим, но если вы не понимаете ваш код или почему он дает плохую производительность, это будет хуже:)

Удачи

0 голосов
/ 18 февраля 2009

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

0 голосов
/ 18 февраля 2009

Не зная структуры таблиц и какие индексы или статистика существуют для них, было бы очень трудно устранить неполадки производительности запросов. Как и было предложено, лучше всего первым делом взглянуть на план выполнения и определить, где находится большинство ваших расходов. Распространенным исправлением является внедрение дополнительных индексов для сокращения сканирования таблиц.

Настройка запросов - это своего рода искусство, поэтому вы не найдете ни одного «правильного» консенсуса по этому вопросу.

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