Проблема производительности SQL Server при присоединении - PullRequest
0 голосов
/ 05 апреля 2011

У меня есть запрос или группа запросов, которые я просто не могу воспроизвести: :)

SELECT 
      C.ID AS cust_id,
      C.State AS cust_state,
      C.PrevState AS cust_previous_state,
      C.ProjectID AS cust_imported_on_project_id,
      C.CampaignID AS cust_imported_on_campaign_id,
      C.Priority AS cust_priority,
      C.Name AS cust_firstname,
      C.Name2 AS cust_lastname,
      C.AllocatedUser AS cust_allocated_user,
      C.ED1 AS cust_social_security_number,
      C.ED2 AS cust_customer_number,
      C.ED3 AS cust_type,
      C.ED4 AS cust_initial_fact_1,
      C.ED5 AS cust_initial_fact_2,
      C.ED6 AS cust_initial_fact_3,
      C.ED7 AS cust_initial_fact_4,
      C.ED8 AS cust_initial_fact_5,
      C.ED9 AS cust_extra_1,
      C.ED10 AS cust_extra_2,
      CED2.ED11 AS cust_extra_3,
      CED2.ED12 AS cust_extra_4,
      CED2.ED13 AS cust_extra_5,
      A.Serial AS address_serial,
      A.PostAddress AS address_postal_address,
      A.PostCode AS address_postal_code,
      A.PostOffice AS address_city,
      A.PhoneNr AS address_phonenumber,
      A.FaxNr AS address_faxnumber,
      A.EMail AS address_email,
      A.Notes AS address_notes,
      A.ED1 AS address_secondary_phonenumber,
      A.ED2 AS address_origin_file,
      A.State AS address_state
FROM TCustomers C WITH (NOLOCK)
    LEFT JOIN TCustED2 CED2 WITH (NOLOCK) ON C.ID = CED2.CustomerID 
    LEFT JOIN TAddresses A WITH (NOLOCK) ON C.AddressNr = A.Serial AND C.ID = A.CustomerID
WHERE C.CampaignID IN(SELECT items FROM dbo.Split('196,195,210,206,205,207,204,200,209,213,197,198,214', ',')) AND C.InsDate > '2011-04-03 00:00:00'

Этот запрос сам по себе очень быстрый и выполняется за несколько миллионов строк за несколько секунд. Проблема в том, что я хочу использовать информацию из таблицы заказов для фильтрации своих результатов.

Я пытался присоединиться к таблице заказов

SELECT 
      C.ID AS cust_id,
      C.State AS cust_state,
      C.PrevState AS cust_previous_state,
      C.ProjectID AS cust_imported_on_project_id,
      C.CampaignID AS cust_imported_on_campaign_id,
      C.Priority AS cust_priority,
      C.Name AS cust_firstname,
      C.Name2 AS cust_lastname,
      C.AllocatedUser AS cust_allocated_user,
      C.ED1 AS cust_social_security_number,
      C.ED2 AS cust_customer_number,
      C.ED3 AS cust_type,
      C.ED4 AS cust_initial_fact_1,
      C.ED5 AS cust_initial_fact_2,
      C.ED6 AS cust_initial_fact_3,
      C.ED7 AS cust_initial_fact_4,
      C.ED8 AS cust_initial_fact_5,
      C.ED9 AS cust_extra_1,
      C.ED10 AS cust_extra_2,
      CED2.ED11 AS cust_extra_3,
      CED2.ED12 AS cust_extra_4,
      CED2.ED13 AS cust_extra_5,
      A.Serial AS address_serial,
      A.PostAddress AS address_postal_address,
      A.PostCode AS address_postal_code,
      A.PostOffice AS address_city,
      A.PhoneNr AS address_phonenumber,
      A.FaxNr AS address_faxnumber,
      A.EMail AS address_email,
      A.Notes AS address_notes,
      A.ED1 AS address_secondary_phonenumber,
      A.ED2 AS address_origin_file,
      A.State AS address_state
FROM TCustomers C WITH (NOLOCK)
    LEFT JOIN TCustED2 CED2 WITH (NOLOCK) ON C.ID = CED2.CustomerID 
    LEFT JOIN TAddresses A WITH (NOLOCK) ON C.AddressNr = A.Serial AND C.ID = A.CustomerID
    LEFT JOIN TOrders O WITH (NOLOCK) ON C.ID = O.CustomerID
WHERE C.CampaignID IN(SELECT items FROM dbo.Split('196,195,210,206,205,207,204,200,209,213,197,198,214', ',')) AND 
(C.InsDate > '2011-04-03 00:00:00' OR O.CustomerID NOT NULL OR O.[Date] > '2011-04-03 00:00:00' OR O.Exported IS NULL);

В результате время выполнения составляет несколько минут. Если я просто запускаю интересную часть из TOrders, это займет несколько секунд.

SELECT CustomerID 
FROM TOrders 
WHERE O.CustomerID NOT NULL 
   OR O.[Date] > '2011-04-03 00:00:00' 
   OR O.Exported IS NULL;

Так что проблема в том, как объединить два. Я попытался запустить запрос TOrders и вставить полученные CustomerID непосредственно в основной запрос, и это было быстро и заняло около 5-10 секунд. Я попытался предварительно загрузить интересные данные из TOrders и поместить их во временную таблицу, но это не помогло ускорить процесс.

CREATE TABLE #ORDERCUSTOMERS (
  CustomerID int
);
CREATE UNIQUE CLUSTERED INDEX IX_1 on #ORDERCUSTOMERS (CustomerID);

INSERT #ORDERCUSTOMERS SELECT DISTINCT O.CustomerID FROM LPD1_8.dbo.TOrders O WHERE O.CampaignID IN(SELECT items FROM dbo.Split('196,195,210,206,205,207,204,200,209,213,197,198,214', ',')) AND (O.[Date] > '2011-04-03 00:00:00' OR O.Exported IS NULL);

SELECT 
          C.ID AS cust_id,
          C.State AS cust_state,
          C.PrevState AS cust_previous_state,
          C.ProjectID AS cust_imported_on_project_id,
          C.CampaignID AS cust_imported_on_campaign_id,
          C.Priority AS cust_priority,
          C.Name AS cust_firstname,
          C.Name2 AS cust_lastname,
          C.AllocatedUser AS cust_allocated_user,
          C.ED1 AS cust_social_security_number,
          C.ED2 AS cust_customer_number,
          C.ED3 AS cust_type,
          C.ED4 AS cust_initial_fact_1,
          C.ED5 AS cust_initial_fact_2,
          C.ED6 AS cust_initial_fact_3,
          C.ED7 AS cust_initial_fact_4,
          C.ED8 AS cust_initial_fact_5,
          C.ED9 AS cust_extra_1,
          C.ED10 AS cust_extra_2,
          CED2.ED11 AS cust_extra_3,
          CED2.ED12 AS cust_extra_4,
          CED2.ED13 AS cust_extra_5,
          A.Serial AS address_serial,
          A.PostAddress AS address_postal_address,
          A.PostCode AS address_postal_code,
          A.PostOffice AS address_city,
          A.PhoneNr AS address_phonenumber,
          A.FaxNr AS address_faxnumber,
          A.EMail AS address_email,
          A.Notes AS address_notes,
          A.ED1 AS address_secondary_phonenumber,
          A.ED2 AS address_origin_file,
          A.State AS address_state
      FROM LPD1_8.dbo.TCustomers C WITH (NOLOCK)
    LEFT JOIN LPD1_8.dbo.TCustED2 CED2 WITH (NOLOCK) ON C.ID = CED2.CustomerID 
    LEFT JOIN LPD1_8.dbo.TAddresses A WITH (NOLOCK) ON C.AddressNr = A.Serial AND C.ID = A.CustomerID
    LEFT JOIN #ORDERCUSTOMERS O ON C.ID = O.CustomerID
      WHERE C.CampaignID IN(SELECT items FROM dbo.Split('196,195,210,206,205,207,204,200,209,213,197,198,214', ',')) AND 
    (C.InsDate > '2011-04-03 00:00:00' OR O.CustomerID NOT NULL);

DROP TABLE #ORDERCUSTOMERS;

Так у вас, ребята, есть идеи? Это будет храниться в хранимой процедуре, поэтому я хочу избежать динамического SQL, если это возможно. В противном случае это, вероятно, будет одним из способов получить данные из TOrders и вставить возвращенные идентификаторы CustomerID в предложение IN основного запроса. Но, возможно, есть аналогичный способ «вставить» массив идентификаторов в предложение IN или что-то в этом роде?

Ответы [ 4 ]

0 голосов
/ 05 апреля 2011

Способы оптимизатора запросов не всегда предсказуемы. Предполагая, что у вас есть индексы для всех столбцов, к которым вы присоединяетесь / фильтруете, я бы попробовал следующее:

OR O.CustomerID NOT NULL

представляется избыточным - поскольку вы используете этот столбец в условии соединения, он всегда должен иметь значение ИСТИНА. Это должно означать, что запрос возвращает каждую запись независимо от даты.

Кроме того, вы можете обнаружить, что замена

 WHERE C.CampaignID IN(SELECT items FROM dbo.Split('196,195,210,206,205,207,204,200,209,213,197,198,214', ','))

с

 WHERE C.CampaignID IN(196,195,210,206,205,207,204,200,209,213,197,198,214)

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

0 голосов
/ 05 апреля 2011

Это будет храниться в хранимой процедуре, поэтому я хочу по возможности избегать динамического SQL.

ПЛОХИЕ новости.Вы хотите динамически работать с чем-то подобным, потому что вы, по крайней мере, хотите «с перекомпиляцией» на SP, или ваш первый запуск решает ваш план запроса.Более подробная информация зависит от плана запроса.Может быть, у вас индекс msisan или два?Пожалуйста, опубликуйте планы выполнения (предполагается и из исполнения).

0 голосов
/ 05 апреля 2011

вы используете таблицу заказа в предложении where. это означает, что планировщик запросов не может начать удалять строки из таблицы клиентов достаточно рано. Вы пробовали присоединиться к таблице заказов ранее?

SELECT 
      C.ID AS cust_id,
      C.State AS cust_state,
      C.PrevState AS cust_previous_state,
      C.ProjectID AS cust_imported_on_project_id,
      C.CampaignID AS cust_imported_on_campaign_id,
      C.Priority AS cust_priority,
      C.Name AS cust_firstname,
      C.Name2 AS cust_lastname,
      C.AllocatedUser AS cust_allocated_user,
      C.ED1 AS cust_social_security_number,
      C.ED2 AS cust_customer_number,
      C.ED3 AS cust_type,
      C.ED4 AS cust_initial_fact_1,
      C.ED5 AS cust_initial_fact_2,
      C.ED6 AS cust_initial_fact_3,
      C.ED7 AS cust_initial_fact_4,
      C.ED8 AS cust_initial_fact_5,
      C.ED9 AS cust_extra_1,
      C.ED10 AS cust_extra_2,
      CED2.ED11 AS cust_extra_3,
      CED2.ED12 AS cust_extra_4,
      CED2.ED13 AS cust_extra_5,
      A.Serial AS address_serial,
      A.PostAddress AS address_postal_address,
      A.PostCode AS address_postal_code,
      A.PostOffice AS address_city,
      A.PhoneNr AS address_phonenumber,
      A.FaxNr AS address_faxnumber,
      A.EMail AS address_email,
      A.Notes AS address_notes,
      A.ED1 AS address_secondary_phonenumber,
      A.ED2 AS address_origin_file,
      A.State AS address_state
FROM TCustomers C WITH (NOLOCK)
    LEFT JOIN TOrders O WITH (NOLOCK) ON C.ID = O.CustomerID
    LEFT JOIN TCustED2 CED2 WITH (NOLOCK) ON C.ID = CED2.CustomerID 
    LEFT JOIN TAddresses A WITH (NOLOCK) ON C.AddressNr = A.Serial AND C.ID = A.CustomerID
WHERE C.CampaignID IN(SELECT items FROM dbo.Split('196,195,210,206,205,207,204,200,209,213,197,198,214', ',')) AND 
(C.InsDate > '2011-04-03 00:00:00' OR O.CustomerID NOT NULL OR O.[Date] > '2011-04-03 00:00:00' OR O.Exported IS NULL);
0 голосов
/ 05 апреля 2011

Вы пытались проанализировать свой запрос с помощью встроенного средства SQL Server Query Optimizer?

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