SQL Query - долго работающий / занимающий ресурсы процессора - PullRequest
0 голосов
/ 17 октября 2018

Здравствуйте, у меня есть приведенный ниже SQL-запрос, выполнение которого занимает в среднем 40 минут, в одной из таблиц, на которые он ссылается, содержится более 7 миллионов записей.

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

Любые предложения будут отличными, заранее спасибо

WITH CTE AS 
(
    SELECT r.Id AS ResultId,
    r.JobId,
    r.CandidateId,
    r.Email,
    CAST(0 AS BIT) AS EmailSent,
    NULL AS EmailSentDate,
    'PICKUP' AS EmailStatus,
    GETDATE() AS CreateDate,
    C.Id AS UserId,
    C.Email AS UserEmail,
    NULL AS Subject
    FROM Result R
    INNER JOIN Job J ON R.JobId = J.Id
    INNER JOIN User C ON J.UserId = C.Id
    WHERE 
    ISNULL(J.Approved, CAST(0 AS BIT)) = CAST(1 AS BIT)
    AND ISNULL(J.Closed, CAST(0 AS BIT)) = CAST(0 AS BIT)
    AND ISNULL(R.Email,'') <> '' -- has an email address
    AND ISNULL(R.EmailSent, CAST(0 AS BIT)) = CAST(0 AS BIT) -- email has not been sent
    AND R.EmailSentDate IS NULL -- email has not been sent
    AND ISNULL(R.EmailStatus,'') = '' -- email has not been sent
    AND ISNULL(R.IsEmailSubscribe, 'True') <> 'False' -- not unsubscribed
    -- not already been emailed for this job
    AND NOT EXISTS (
        SELECT SMTP.Email
        FROM SMTP_Production SMTP
        WHERE SMTP.JobId = R.JobId AND SMTP.CandidateId = R.CandidateId
    )
    -- not unsubscribed
    AND NOT EXISTS (

        SELECT u.Id FROM Unsubscribe u
        WHERE  ISNULL(u.EmailAddress, '') = ISNULL(R.Email, '')

    )
    AND NOT EXISTS (
        SELECT SMTP.Id FROM SMTP_Production SMTP
        WHERE SMTP.EmailStatus = 'PICKUP' AND SMTP.CandidateId = R.CandidateId
    )   
    AND C.Id NOT IN (
        -- list of ids
    )
    AND J.Id NOT IN (
        -- list of ids
    )
    AND J.ClientId NOT IN 
    (
        -- list of ids
    )
)
INSERT INTO smtp_production (ResultId, JobId, CandidateId, Email, EmailSent, EmailSentDate, EmailStatus, CreateDate, ConsultantId, ConsultantEmail, Subject)
OUTPUT INSERTED.ResultId,GETDATE() INTO ResultstoUpdate
SELECT 
    CTE.ResultId,
    CTE.JobId,
    CTE.CandidateId,
    CTE.Email,
    CTE.EmailSent,
    CTE.EmailSentDate,
    CTE.EmailStatus,
    CTE.CreateDate,
    CTE.UserId,
    CTE.UserEmail,
    NULL
FROM CTE
  INNER JOIN 
    (
        SELECT *, row_number() over(partition by CTE.Email, CTE.CandidateId order by CTE.EmailSentDate desc) as rn
        FROM CTE

    ) DCTE ON CTE.ResultId = DCTE.ResultId AND DCTE.rn = 1

Пожалуйста,см. мой обновленный запрос ниже:

WITH CTE AS 
(
    SELECT R.Id AS ResultId,
    r.JobId,
    r.CandidateId,
    R.Email,
    CAST(0 AS BIT) AS EmailSent,
    NULL AS EmailSentDate,
    'PICKUP' AS EmailStatus,
    GETDATE() AS CreateDate,
    C.Id AS UserId,
    C.Email AS UserEmail,
    NULL AS Subject
    FROM RESULTS R
    INNER JOIN JOB J ON R.JobId = J.Id
    INNER JOIN Consultant C ON J.UserId = C.Id
    WHERE 
    J.DCApproved = 1
    AND (J.Closed = 0 OR J.Closed IS NULL)
    AND (R.Email <> '' OR R.Email IS NOT NULL)
    AND (R.EmailSent = 0 OR R.EmailSent IS NULL)
    AND R.EmailSentDate IS NULL -- email has not been sent
    AND (R.EmailStatus = '' OR R.EmailStatus IS NULL)
    AND (R.IsEmailSubscribe = 'True' OR R.IsEmailSubscribe IS NULL)
    -- not already been emailed for this job
    AND NOT EXISTS (
        SELECT SMTP.Email
        FROM SMTP_Production SMTP
        WHERE SMTP.JobId = R.JobId AND SMTP.CandidateId = R.CandidateId
    )
    -- not unsubscribed
    AND NOT EXISTS (

        SELECT u.Id FROM Unsubscribe u
        WHERE (u.EmailAddress = R.Email OR (u.EmailAddress IS NULL AND R.Email IS NULL))

    )
    AND NOT EXISTS (
        SELECT SMTP.Id FROM SMTP_Production SMTP
        WHERE SMTP.EmailStatus = 'PICKUP' AND SMTP.CandidateId = R.CandidateId
    )   
    AND C.Id NOT IN (
        -- LIST OF IDS
    )
    AND J.Id NOT IN (
        -- LIST OF IDS
    )
    AND J.ClientId NOT IN 
    (
        -- LIST OF IDS
    )
)

INSERT INTO smtp_production (ResultId, JobId, CandidateId, Email, EmailSent, EmailSentDate, EmailStatus, CreateDate, UserId, UserEmail, Subject)
OUTPUT INSERTED.ResultId,GETDATE() INTO ResultstoUpdate
SELECT 
    CTE.ResultId,
    CTE.JobId,
    CTE.CandidateId,
    CTE.Email,
    CTE.EmailSent,
    CTE.EmailSentDate,
    CTE.EmailStatus,
    CTE.CreateDate,
    CTE.UserId,
    CTE.UserEmail,
    NULL
FROM CTE
  INNER JOIN 
    (
        SELECT *, row_number() over(partition by CTE.Email, CTE.CandidateId order by CTE.EmailSentDate desc) as rn
        FROM CTE

    ) DCTE ON CTE.ResultId = DCTE.ResultId AND DCTE.rn = 1


GO

Ответы [ 2 ]

0 голосов
/ 17 октября 2018

Использование ISNULL в ваших предложениях WHERE и JOIN, вероятно, является основной причиной.Использование функций для столбцов в вашем запросе приводит к тому, что запрос становится не-SARGable (это означает, что он не может использовать ни один из индексов в ваших таблицах (таблицах), и поэтому он сканирует все). Примечание;использование функций против переменных, там WHERE - это обычно отлично.Например WHERE SomeColumn = DATEADD(DAY, @n, @SomeDate).Такие вещи, как WHERE SomeColumn = ISNULL(@Variable,0) имеют запах «всеобъемлющего запроса», так что это может сказаться на производительности;в зависимости от вашей настройки.Это не обсуждение, однако.

Для таких предложений, как ISNULL(J.Closed, CAST(0 AS BIT)) = CAST(0 AS BIT), поэтому это большая головная боль для оптимизатора запросов, и ваш запрос изобилует ими.Вам нужно будет заменить их такими предложениями, как:

WHERE (J.Closed = 0 OR J.Closed IS NULL)

Хотя это не имеет значения, нет необходимости CAST 0 там же.SQL Server может видеть, что вы сравниваете с bit, и поэтому интерпретирует 0 как единое целое.

У вас также есть EXISTS с предложением WHERE ISNULL(u.EmailAddress, '') = ISNULL(R.Email, ''),Это должно стать:

WHERE (u.EmailAddress = R.Email
  OR   (u.EmailAddress IS NULL AND R.Email IS NULL))

Вам нужно будет изменить все вашего ISNULL использования в ваших WHERE предложениях (CTE и подзапросах), и вы должныувидеть приличное увеличение производительности.

0 голосов
/ 17 октября 2018

Как правило, 7 миллионов записей - это шутка для современных баз данных.Если у вас проблемы, вы должны говорить о проблемах на миллиардах строк, а не на 7 миллионах.

Что указывает на проблемы с запросом.Высокая загрузка ЦП, как правило, является признаком несовпадающих полей (сравнение строки в одной таблице с числом в другой) или ... функций, вызываемых слишком часто.Длительная работа в обычном режиме является признаком либо отсутствия индексов, либо… несоответствия.Что вы действительно делаете для принуждения.

Неразрешаемость означает, что индексы НЕ МОГУТ использоваться.Примером всего этого является:

ISNULL (J.Approved, CAST (0 AS BIT)) = CAST (1 AS BIT)

ISNULL (поле, значение) означает, что индексна поле не используется - по сути, "индекс до свидания, привет таблица сканирования".Это также означает - хорошо ....

(J.Approoved = 1 или J.Approoved IS NULL)

имеет то же значение, но оно имеет смысл.Практически КАЖДЫЕ из ваших условий написаны ненормативно - добро пожаловать в db hell.Начните переписывать.

Возможно, вы захотите прочитать больше о sargeability в https://www.techopedia.com/definition/28838/sargeable

Также убедитесь, что у вас есть индексы для всех соответствующих внешних ключей (и первичных ключей, на которые имеются ссылки) - иначе, сноваПриветственное сканирование таблицы.

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