2015-02-19 2 views
0

Следующий сценарий очень медленный при его запуске.Как улучшить производительность скрипта sql

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

SELECT DISTINCT 
     (id) 
FROM (SELECT DISTINCT 
        ct.id AS id 
      FROM  [Customer].[dbo].[Contact] ct 
        LEFT JOIN [Customer].[dbo].[Customer_ids] hnci ON ct.id = hnci.contact_id 
      WHERE  hnci.customer_id IN (
        SELECT DISTINCT 
          ([Customer_ID]) 
        FROM [Transactions].[dbo].[Transaction_Header] 
        WHERE actual_transaction_date > '20120218') 
      UNION 
      SELECT DISTINCT 
        contact_id AS id 
      FROM  [Customer].[dbo].[Restaurant_Attendance] 
      WHERE  (created > '2012-02-18 00:00:00.000' 
         OR modified > '2012-02-18 00:00:00.000' 
        ) 
        AND ([Fifth_Floor_London] = 1 
          OR [Fourth_Floor_Leeds] = 1 
          OR [Second_Floor_Bristol] = 1 
         ) 
      UNION 
      SELECT DISTINCT 
        (ct.id) 
      FROM  [Customer].[dbo].[Contact] ct 
        INNER JOIN [Customer].[dbo].[Wifinity_Devices] wfd ON ct.wifinity_uniqueID = wfd.[CustomerUniqueID] 
                   AND startconnection > '2012-02-17' 
      UNION 
      SELECT DISTINCT 
        comdt.id AS id 
      FROM  [Customer].[dbo].[Complete_dataset] comdt 
        LEFT JOIN [Customer].[dbo].[Aggregate_Spend_Counts] agsc ON comdt.id = agsc.contact_id 
      WHERE  agsc.contact_id IS NULL 
        AND (opt_out_Mail <> 1 
          OR opt_out_email <> 1 
          OR opt_out_SMS <> 1 
          OR opt_out_Mail IS NULL 
          OR opt_out_email IS NULL 
          OR opt_out_SMS IS NULL 
         ) 
        AND (address_1 IS NOT NULL 
          OR email IS NOT NULL 
          OR mobile IS NOT NULL 
         ) 
      UNION 
      SELECT DISTINCT 
        (contact_id) AS id 
      FROM  [Customer].[dbo].[VIP_Card_Holders] 
      WHERE  VIP_Card_number IS NOT NULL 
     ) AS tbl 
+2

первую очередь избежать подзапрос, если это возможно, то используйте индекс! в любом случае вы выполняете выбор 5. попробуйте один раз, чтобы увидеть, как медленно. используйте план выполнения оценки, чтобы увидеть, где можно оптимизировать ваш запрос. – giammin

+0

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

+2

Вам не нужно DISTINCT, когда UNION, все дубликаты уже удалены. – jarlh

ответ

-1

попробовать это, TempTable должно помочь вам:

IF OBJECT_ID('Tempdb..#Temp1') IS NOT NULL 
     DROP TABLE #Temp1 

    --Low perfomance because of using "WHERE hnci.customer_id IN (....) " - loop join must be 
    --and this "where" condition will apply to two tables after left join, 
    --so result will be same as with two inner joints but with bad perfomance 

    --SELECT DISTINCT 
    --  ct.id AS id 
    --INTO #temp1 
    --FROM [Customer].[dbo].[Contact] ct 
    --  LEFT JOIN [Customer].[dbo].[Customer_ids] hnci ON ct.id = hnci.contact_id 
    --WHERE hnci.customer_id IN (
    --  SELECT DISTINCT 
    --    ([Customer_ID]) 
    --  FROM [Transactions].[dbo].[Transaction_Header] 
    --  WHERE actual_transaction_date > '20120218')  
    -------------------------------------------------------------------------------- 
    --this will give the same result but with better perfomance then previouse one 
    -------------------------------------------------------------------------------- 
    SELECT DISTINCT 
      ct.id AS id 
    INTO #temp1 
    FROM [Customer].[dbo].[Contact] ct 
      JOIN [Customer].[dbo].[Customer_ids] hnci ON ct.id = hnci.contact_id 
      JOIN (SELECT DISTINCT 
          ([Customer_ID]) 
        FROM  [Transactions].[dbo].[Transaction_Header] 
        WHERE actual_transaction_date > '20120218' 
       ) T ON hnci.customer_id = T.[Customer_ID] 
    -------------------------------------------------------------------------------- 
    --------------------------------------------------------------------------------    
    INSERT INTO #temp1 
      (id 
      ) 
      SELECT DISTINCT 
        contact_id AS id 
      FROM [Customer].[dbo].[Restaurant_Attendance] 
      WHERE (created > '2012-02-18 00:00:00.000' 
         OR modified > '2012-02-18 00:00:00.000' 
        ) 
        AND ([Fifth_Floor_London] = 1 
          OR [Fourth_Floor_Leeds] = 1 
          OR [Second_Floor_Bristol] = 1 
         ) 
    INSERT INTO #temp1 
      (id 
      ) 
      SELECT DISTINCT 
        (ct.id) 
      FROM [Customer].[dbo].[Contact] ct 
        INNER JOIN [Customer].[dbo].[Wifinity_Devices] wfd ON ct.wifinity_uniqueID = wfd.[CustomerUniqueID] 
                    AND startconnection > '2012-02-17' 
    INSERT INTO #temp1 
      (id 
      ) 
      SELECT DISTINCT 
        comdt.id AS id 
      FROM [Customer].[dbo].[Complete_dataset] comdt 
        LEFT JOIN [Customer].[dbo].[Aggregate_Spend_Counts] agsc ON comdt.id = agsc.contact_id 
      WHERE agsc.contact_id IS NULL 
        AND (opt_out_Mail <> 1 
          OR opt_out_email <> 1 
          OR opt_out_SMS <> 1 
          OR opt_out_Mail IS NULL 
          OR opt_out_email IS NULL 
          OR opt_out_SMS IS NULL 
         ) 
        AND (address_1 IS NOT NULL 
          OR email IS NOT NULL 
          OR mobile IS NOT NULL 
         ) 
    INSERT INTO #temp1 
      (id 
      ) 
      SELECT DISTINCT 
        (contact_id) AS id 
      FROM [Customer].[dbo].[VIP_Card_Holders] 
      WHERE VIP_Card_number IS NOT NULL 

    SELECT DISTINCT 
      id 
    FROM #temp1 AS T 
+0

Кажется, это действительно хорошо. – Tun

+0

Это приведет к дублированию. Ничто не мешает добавлению дополнительных вставок из существующего значения. – Paparazzi

+0

@blam Я не согласен с вами, окончательный вывод используется отдельно – Vasily

0

Как указано в комментарии оптимизации по одному. Посмотрите, какой из них занимает больше всего времени, и сосредоточьтесь на этом.

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

На вас первым я хотел бы попробовать это:

Левый присоединиться убит ГДЕ hnci.customer_id так, Вы может также присоединиться.

Подзапрос неэффективен, так как не может использовать индекс для IN.
Оптимизатор запросов не знает, что в (select ..) вернется, поэтому он не может оптимизировать использование индексов.

SELECT ct.id AS id 
    FROM [Customer].[dbo].[Contact] ct 
    JOIN [Customer].[dbo].[Customer_ids] hnci 
    ON ct.id = hnci.contact_id 
    JOIN [Transactions].[dbo].[Transaction_Header] th 
    on hnci.customer_id = th.[Customer_ID] 
    and th.actual_transaction_date > '20120218' 

На этом втором объединении оптимизатор запросов имеет возможность применения первого условия. Скажем, [Customer]. [Dbo]. [Customer_ids]. [Customer_id] и [Transactions]. [Dbo]. [Transaction_Header] имеют индексы. Оптимизатор запросов имеет возможность применить это до [Транзакции]. [Dbo]. [Transaction_Header]. [Actual_transaction_date]. Если [actual_transaction_date] не проиндексирован, то, безусловно, он будет первым подключиться к другому идентификатору.

С помощью функции in (select ...) оптимизатор запросов не имеет опции, но сначала применим actual_transaction_date> '20120218'. ОК несколько раз оптимизатор запросов достаточно умен, чтобы использовать индекс внутри внутри вне, но почему это затрудняет оптимизатор запросов. Я нашел, что оптимизатор запросов принимает более правильные решения, если вы облегчаете принятие решений.

Соотношение по второму запросу имеет ту же проблему. Вы отбираете опционы от оптимизатора запросов. Дайте комнате оптимизатора запросов дышать.

+0

этот момент был включен в мой пост час назад – Vasily

+0

@Vasily Что, ваш ответ использует темп? Я даже не согласен с этим. Я не рекомендую #temp для этого. Я добавляю улучшения к одному запросу - вы повторяете одни и те же запросы. – Paparazzi

+0

мой ответ рекомендую использовать временную таблицу и заменить там, где условие соединить – Vasily

0

Где существует, как правило, быстрее, чем в.

Или условия, как правило, более медленные, используйте вместо этого больше инструкций о союзе. И научитесь правильно использовать левые соединения. Если у вас есть условие where (отличное от того, где id равно null) в таблице на правой стороне левого соединения, оно преобразуется во внутреннее соединение. Если это не то, что вы хотите, тогда ваш код в настоящее время дает неверный набор результатов.

См. http://wiki.lessthandot.com/index.php/WHERE_conditions_on_a_LEFT_JOIN для объяснения того, как исправить.

+0

^^ Комментарий, а не ответ. – Zane

+0

@ Zane вы ошибаетесь – HLGEM

+4

действительно потому, что, когда я сделал этот комментарий, это было полностью ваше сообщение: «Где существует, как правило, быстрее, чем в том же». – Zane

2

Ничего себе, с чего начать ...

--this distinct does nothing. Union is already distinct 
--SELECT DISTINCT 
--  (id) 
--FROM ( 
SELECT DISTINCT [Customer_ID] as ID 
      FROM  [Transactions].[dbo].[Transaction_Header] 
       where actual_transaction_date > '20120218') 
      UNION 
      SELECT 
        contact_id AS id 
      FROM  [Customer].[dbo].[Restaurant_Attendance] 
-- not sure that you are getting the date range you want. Should these be >= 
-- if you want everything that occurred on the 18th or after you want >= '2012-02-18 00:00:00.000' 
-- if you want everything that occurred on the 19th or after you want >= '2012-02-19 00:00:00.000' 
-- the way you have it now, you will get everything on the 18th unless it happened exactly at midnight 
      WHERE  (created > '2012-02-18 00:00:00.000' 
         OR modified > '2012-02-18 00:00:00.000' 
        ) 
        AND ([Fifth_Floor_London] = 1 
          OR [Fourth_Floor_Leeds] = 1 
          OR [Second_Floor_Bristol] = 1 
         ) 
-- all of this does nothing because we already have every id in the contact table from the first query 
--   UNION 
--   SELECT 
--     (ct.id) 
--   FROM  [Customer].[dbo].[Contact] ct 
--     INNER JOIN [Customer].[dbo].[Wifinity_Devices] wfd ON ct.wifinity_uniqueID = wfd.[CustomerUniqueID] 
--                AND startconnection > '2012-02-17' 
      UNION 
-- cleaned this up with isnull function and coalesce 
      SELECT 
        comdt.id AS id 
      FROM  [Customer].[dbo].[Complete_dataset] comdt 
        LEFT JOIN [Customer].[dbo].[Aggregate_Spend_Counts] agsc ON comdt.id = agsc.contact_id 
      WHERE  agsc.contact_id IS NULL 
        AND (isnull(opt_out_Mail,0) <> 1 
          OR isnull(opt_out_email,0) <> 1 
          OR isnull(opt_out_SMS,0) <> 1 
         ) 
        AND coalesce(address_1 , email, mobile) IS NOT NULL 
      UNION 
      SELECT 
        (contact_id) AS id 
      FROM  [Customer].[dbo].[VIP_Card_Holders] 
      WHERE  VIP_Card_number IS NOT NULL 
--  ) AS tbl 
+0

Вы также можете удалить все 'DISTINCT'. Результат будет таким же. –

+2

Ваше правление в 1-й части 'UNION' неверно. 'IN (...)' будет удалять строки из результата. –

+0

@ypercube, хороший улов. Я неправильно понял это в первый раз. Удалил комментарий и вернул код. Продолжайте редактировать его дальше, чтобы улучшить этот запрос. – Kevin

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