Изменение компаратора в предложении WHERE приводит к катастрофическим результатам в отношении производительности запросов. - PullRequest
0 голосов
/ 01 декабря 2011

У меня есть запрос монстра, который я запускаю для базы данных SQL SERVER 2005, который работает очень странно.У меня есть два условия в предложении WHERE самого внешнего выбора, сравнивающего поле с постоянной датой.Если постоянные даты либо идентичны (с точностью до секунды), либо их части даты не совпадают, запрос выполняется менее чем за 2 секунды.Если части даты совпадают, но части времени отличаются, выполнение запроса занимает около 7 минут.В частности, наличие предложения WHERE

WHERE
  d.date >= '2011-11-07 00:00:00' AND
  d.date <= '2011-11-08 11:59:59'

работает хорошо и как ожидалось.Изменение условия WHERE на

WHERE
  d.date >= '2011-11-07 00:00:00' AND
  d.date <= '2011-11-07 11:59:59'

приводит к тому, что запрос занимает много минут.

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

Ниже приведен полный запрос для справки (рассматриваемое предложение WHERE находится в самомконец):

SELECT
  s.transaction_id AS 'transaction',
  s.created_on AS transaction_date,
  s.first_name + ' ' + s.Last_Name AS customer_name,
  a.name AS agent_name,
  a.phantom AS phantom,
  a.team AS agent_team,
  a.id AS agent_number,
  h.hours,
  h2.hours_today,
  d.*
FROM
  (SELECT
     agents.first_name + ' ' + agents.last_name AS name,
     agents.id AS id,
     agents.phantom AS phantom,
     transient.value AS team,
     transient.start_date AS team_start_date,
     transient.end_date AS team_end_date
   FROM
       Agents.dbo.Agent_Static AS agents
     JOIN
       Agents.dbo.Agent_Transient AS transient
     ON transient.agent = agents.id
   WHERE
     transient.field = 'team') AS a
  LEFT JOIN Agents.dbo.Agent_Daily AS d
    ON d.agent = a.id
  LEFT JOIN (SELECT
               agent_hours.agent AS agent,
               dates.date AS date,
               CAST(COUNT(*) AS FLOAT) / 4 AS hours
             FROM
                 Agents.dbo.Agent_Hours AS agent_hours
               JOIN
                 (SELECT
                    DISTINCT CONVERT(
                               VARCHAR(10),
                               hour_worked,
                               101)
                               AS date
                  FROM
                    Agents.dbo.Agent_Hours) AS dates
               ON dates.date = CONVERT(
                                 VARCHAR(10),
                                 agent_hours.hour_worked,
                                 101)
             WHERE
               (status = 'Phone' OR
                status = 'Meeting')
             GROUP BY
               agent_hours.agent,
               dates.date) AS h
    ON h.agent = a.id AND
       h.date = d.date
  LEFT JOIN (SELECT
               agent_hours.agent AS agent,
               dates.date AS date,
               CAST(COUNT(*) AS FLOAT) / 4 AS hours_today
             FROM
                 Agents.dbo.Agent_Hours AS agent_hours
               JOIN
                 (SELECT
                    DISTINCT CONVERT(
                               VARCHAR(10),
                               hour_worked,
                               101)
                               AS date
                  FROM
                    Agents.dbo.Agent_Hours) AS dates
               ON dates.date = CONVERT(
                                 VARCHAR(10),
                                 agent_hours.hour_worked,
                                 101)
             WHERE
               (status = 'Phone' OR
                status = 'Meeting') AND
               CONVERT(
                 VARCHAR(10),
                 CAST('11/09/2011 13:01' AS DATETIME),
                 101) = CONVERT(
                          VARCHAR(10),
                          agent_hours.hour_worked,
                          101) AND
               CONVERT(
                 VARCHAR(10),
                 CAST('11/09/2011 13:01' AS DATETIME),
                 114) > CONVERT(
                          VARCHAR(10),
                          agent_hours.hour_worked,
                          114)
             GROUP BY
               agent_hours.agent,
               dates.date) AS h2
    ON h2.agent = a.id AND
       h2.date = d.date
  LEFT JOIN sale_transactions AS s
    ON a.id = s.agent_hermes_id AND
       s.created_on >= a.team_start_date AND
       s.created_on <= a.team_end_date AND
       CONVERT(
         VARCHAR(10),
         d.date,
         101) = CONVERT(
                  VARCHAR(10),
                  s.created_on,
                  101)
  LEFT JOIN sold_phrases AS p
    ON s.Transaction_ID = p.transaction_id
WHERE
  d.date >= '2011-11-07 00:00:00' AND
  d.date <= '2011-11-07 11:59:59'

Ответы [ 2 ]

3 голосов
/ 01 декабря 2011

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

Я не вижу никакой разницы между этими двумя случаями, но, учитывая ваше объяснение, это то, что, вероятно, происходит: оценки количества элементов для диапазона дат могут вызвать критическую точку индекса , и вы получите совершенно разные планы выполнения , Такие проблемы лучше всего решать с помощью руководств плана, см. Оптимизация запросов в развернутых приложениях с использованием руководств плана . Вы должны быть в состоянии подтвердить, действительно ли проблема в плане, см. Отображение графических планов выполнения (SQL Server Management Studio) .

1 голос
/ 02 декабря 2011

Возможно, это микрооптимизация, но не хотите ли вы изменить способ получения части даты с datetime на DATEADD(dd, 0, DATEDIFF(dd, 0, datetime_format)).Обычно это более быстрый способ, чем функция convert.

SELECT
  s.transaction_id AS 'transaction',
  s.created_on AS transaction_date,
  s.first_name + ' ' + s.Last_Name AS customer_name,
  a.name AS agent_name,
  a.phantom AS phantom,
  a.team AS agent_team,
  a.id AS agent_number,
  h.hours,
  h2.hours_today,
  d.*
FROM (SELECT
     agents.first_name + ' ' + agents.last_name AS name,
     agents.id AS id,
     agents.phantom AS phantom,
     transient.value AS team,
     transient.start_date AS team_start_date,
     transient.end_date AS team_end_date
   FROM
       Agents.dbo.Agent_Static AS agents
     JOIN
       Agents.dbo.Agent_Transient AS transient
     ON transient.agent = agents.id
   WHERE
     transient.field = 'team'
) AS a
LEFT JOIN Agents.dbo.Agent_Daily AS d ON d.agent = a.id
LEFT JOIN (
    SELECT
        agent_hours.agent AS agent, 
        dates.date AS date,
        COUNT(*) / 4.0 AS hours
    FROM Agents.dbo.Agent_Hours AS agent_hours
    JOIN (
        SELECT DATEADD(dd, 0, DATEDIFF(dd, 0, hour_worked)) as date
        FROM Agents.dbo.Agent_Hours GROUP BY DATEADD(dd, 0, DATEDIFF(dd, 0, hour_worked))
    ) AS dates ON dates.date = DATEADD(dd, 0, DATEDIFF(dd, 0, agent_hours.hour_worked))
    WHERE (status = 'Phone' OR status = 'Meeting')
    GROUP BY agent_hours.agent, dates.date
) AS h ON h.agent = a.id AND h.date = d.date
LEFT JOIN (
    SELECT
        agent_hours.agent AS agent,
        dates.date AS date, 
        COUNT(*) / 4.0 AS hours_today
    FROM Agents.dbo.Agent_Hours AS agent_hours
    JOIN (
        SELECT DATEADD(dd, 0, DATEDIFF(dd, 0, hour_worked)) as date
    FROM Agents.dbo.Agent_Hours GROUP BY DATEADD(dd, 0, DATEDIFF(dd, 0, hour_worked))
    ) AS dates ON dates.date = DATEADD(dd, 0, DATEDIFF(dd, 0, agent_hours.hour_worked))
    WHERE 
        (status = 'Phone' OR status = 'Meeting') AND
        agent_hours.hour_worked >=
        DATEADD(dd, 0, DATEDIFF(dd, 0, CAST('11/09/2011 13:01' AS DATETIME)))
        AND
        agent_hours.hour_worked <
        CAST('11/09/2011 13:01' AS DATETIME)
    GROUP BY agent_hours.agent, dates.date
    ) AS h2 ON h2.agent = a.id AND h2.date = d.date

  LEFT JOIN sale_transactions AS s
    ON a.id = s.agent_hermes_id AND
       s.created_on >= a.team_start_date AND
       s.created_on <= a.team_end_date AND
        DATEADD(dd, 0, DATEDIFF(dd, 0, d.date))
        = 
        DATEADD(dd, 0, DATEDIFF(dd, 0, s.created_on))
  LEFT JOIN sold_phrases AS p
    ON s.Transaction_ID = p.transaction_id
WHERE
  d.date >= '2011-11-07 00:00:00' AND
  d.date <= '2011-11-07 11:59:59'

Более важные (как уже писал Ремус Русану) индексы.Выполните оба запроса и проверьте, какие индексы используются в более быстрых запросах, и заставьте SQL Server использовать их всегда.Вы можете сделать это, используя with(index(index_name)).

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