簡體   English   中英

SQL查詢-長時間運行/占用CPU資源

[英]SQL Query - long running / taking up CPU resource

您好,我有下面的SQL查詢平均需要運行40分鍾,它引用的表之一中有超過700萬條記錄。

我已經通過數據庫調整顧問運行了此操作,並應用了所有建議,而且我已經在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

在您的WHEREJOIN子句中使用ISNULL可能是這里的主要原因。 對查詢中的列使用函數會導致查詢變為非SARGable(這意味着它無法使用表上的任何索引,因此可以掃描整個對象)。 注意; 對變量使用函數, 通常WHERE都可以。 例如, WHERE SomeColumn = DATEADD(DAY, @n, @SomeDate) 諸如WHERE SomeColumn = ISNULL(@Variable,0)之類的東西具有“ WHERE SomeColumn = ISNULL(@Variable,0)查詢”的味道,因此可能會對性能產生影響; 取決於您的設置。 這不是手頭的討論。

對於像ISNULL(J.Closed, CAST(0 AS BIT)) = CAST(0 AS BIT)這樣的子句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作為一個為好。

你也有一個EXISTSWHERE子句ISNULL(u.EmailAddress, '') = ISNULL(R.Email, '') 這將需要成為:

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

您需要在WHERE子句(CTE和子查詢)中更改所有 ISNULL用法,並且應該會看到不錯的性能提高。

通常,700萬條記錄是現代數據庫的笑話。 如果您遇到問題,則應該在數十億行而不是700萬行上討論問題。

指示查詢有問題。 高CPU通常是不匹配字段(將一個表中的字符串與另一個表中的數字進行比較)或...函數調用過多的標志。 長時間正常運行是缺少索引或..不可刺的跡象。 您確實要強制執行哪些操作。

非安全性意味着不能使用taht索引。 這是所有示例:

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

ISNULL(field,value)表示字段上的索引不可用-基本上是“ goodby索引,hello表掃描”。 這也意味着-好吧...

(J.Approoved = 1或J.Approoved IS NULL)

具有相同的含義,但可更改。 您的幾乎所有情況都是以不可更改的方式編寫的-歡迎來到db hell。 開始重寫。

您可能想在https://www.techopedia.com/definition/28838/sargeable上閱讀有關可擴展性的更多信息。

還要確保您在所有相關外鍵(和引用的主鍵)上都有索引-否則,再次歡迎表掃描。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM