我正在使用SQL中的函數,我第一次需要幫助優化。我正在使用SQL Server 2016.sql優化表值函數
我的函數返回一個表變量,比較一年中每個月不同項目的計劃和完成的僱員小時數。但問題是查詢在30-40s內加載大約30,000行。我查了一些關於優化查詢的建議,但是我在代碼中找不到任何錯誤。你能給我一些建議如何優化它?
整個功能的代碼:
CREATE FUNCTION dbo.fnProjectHours(
@Project = '%',
@Task = '%',
@Year INT = 0
)
RETURNS @temp TABLE
(
Year INT, Month INT, Project VARCHAR(20), Task VARCHAR(20),
User VARCHAR(50), PlannedHours Numeric(14,2),
DoneHours Numeric(14,2) id int identity ,
primary key(Year, Project, Task, Month, User, id)
)
AS
BEGIN
SELECT @Year= ISNULL(NULLIF(@Year,0),DATEPART(yy,GETDATE()));
INSERT INTO @t
(
Year, Month, Project, Task, User, PlannedHours, DoneHours
)
SELECT rbh.Year, rbh.Month, rbh.Project, rbh.Task, rbh.User,
rbhp.SumPlan AS PlannedHours, rbhw.SumDone AS DoneHours
FROM
(
SELECT
CASE
WHEN DATEPART(yy, ll.DateStart) IS NULL THEN rbhw.Year
ELSE DATEPART(yy, ll.DateStart)
END AS Year,
CASE
WHEN DATEPART(mm, ll.DateStart) IS NULL THEN DATEPART(mm, rbhw.Date)
ELSE DATEPART(mm, ll.DateStart)
END AS Month,
dbo.wusr_fn_cut(mn.Number, '/') AS Project, ml.Task,
ISNULL(ll.Login,rbhw.Login) AS User
FROM dbo.Nag AS mn WITH (nolock)
INNER JOIN dbo.Lin AS ml WITH (NOLOCK) ON mn.ID = ml.ID
INNER JOIN dbo.LinLogin AS ll WITH (NOLOCK) ON ll.ID = ml.ID
AND ll.LinId = ml.LinId
INNER JOIN dbo.sl_Operator AS o WITH (Nolock) ON ll.Login = o.Login
FULL OUTER JOIN dbo.Hours AS rbhw WITH (NOLOCK)
ON dbo.wusr_fn_cut(mn.Number, '/') = rbhw.Project AND ml.Task = rbhw.Task
AND ll.Login = rbhw.Login AND DATEPART(yy, ll.DateStart) = DATEPART(yy, rbhw.Date)
AND DATEPART(mm, ll.DateStart) = DATEPART(mm, rbhw.Date)
WHERE (mn.Number IS NOT NULL) AND (mn.Status = 0) AND dbo.wusr_fn_cut(mn.Number, '/') LIKE @Project
AND ml.Task LIKE @Task
UNION ALL
SELECT
CASE
WHEN DATEPART(yy, ll.DateStart) IS NULL THEN rbhw.Year
ELSE DATEPART(yy, ll.DateStart)
END AS Year,
CASE
WHEN DATEPART(mm, ll.DateStart) IS NULL THEN DATEPART(mm, rbhw.Date)
ELSE DATEPART(mm, ll.DateStart)
END AS Month,
rbhw.Project, rbhw.Task,
ISNULL(ll.Login,rbhw.Login) AS User
FROM dbo.Nag AS mn WITH (nolock)
INNER JOIN dbo.Hours AS rbhw WITH (NOLOCK)
ON dbo.wusr_fn_cut(mn.Number, '/') = rbhw.Project
INNER JOIN dbo.Lin AS ml WITH (NOLOCK) ON mn.ID = ml.ID
AND rbhw.Task = ml.Task
INNER JOIN dbo.Operator AS o WITH (Nolock) ON rbhw.Login = o.Login
FULL OUTER JOIN dbo.LinLogin AS ll WITH (nolock) ON mn.ID = ll.ID
AND ml.LinId = ll.LinId AND o.Login = ll.Login
AND DATEPART(yy, rbhw.Date)=DATEPART(yy, ll.DateStart)
AND DATEPART(mm, rbhw.Date) = DATEPART(mm, ll.DateStart)
WHERE (rbhw.Project IS NOT NULL) AND (mn.Status = 0) AND (DATEPART(mm, ll.DateStart) IS NULL) AND rbhw.Project LIKE @Project
AND rbhw.Task LIKE @Task
) AS rbh
LEFT JOIN
(
SELECT DATEPART(yy, ll.DateStart) AS Year, DATEPART(mm, ll.DateStart) AS Month,
dbo.wusr_fn_cut(mn.Number, '/') AS Project, ml.Task AS Task,
ll.Login AS LoginLL, SUM(ll.Hours) AS SumPlan
FROM dbo.Nag AS mn WITH (nolock)
INNER JOIN dbo.Lin AS ml WITH (NOLOCK) ON mn.ID = ml.ID
INNER JOIN dbo.LinLogin AS ll WITH (NOLOCK) ON ll.ID = ml.ID
AND ll.LinId = ml.LinId
WHERE mn.Status=0
GROUP BY DATEPART(yy, ll.DateStart),DATEPART(mm, ll.DateStart),dbo.wusr_fn_cut(mn.Number, '/'),ml.Task,ll.Login
) AS rbhp
ON rbh.Project=rbhp.Project AND rbh.Task=rbhp.Task AND
rbh.Year=rbhp.Year AND rbh.Month=rbhp.Month AND rbh.User=rbhp.LoginLL
LEFT JOIN
(
SELECT h.Year, DATEPART(mm, h.Date) AS Month,h.Project AS Project, h.Task AS Task,
h.Login AS LoginRbhw, SUM(h.Hours) AS DoneSum
FROM dbo.Nag AS mn WITH (nolock)
INNER JOIN dbo.Lin AS ml WITH (NOLOCK) ON mn.ID = ml.ID
INNER JOIN dbo.Hours AS h WITH (NOLOCK) ON dbo.wusr_fn_cut(mn.Number, '/') = h.Project
AND ml.Task = h.Task
WHERE mn.Status=0
GROUP BY h.Year,DATEPART(mm, h.Date),h.Project,h.Task,h.Login
) AS rbhw
ON rbh.Project=rbhw.Project AND rbh.Task=rbhw.Task AND
rbh.Year=rbhw.Year AND rbh.Month=rbhw.Month AND rbh.User=rbhw.LoginRbhw
WHERE rbh.Month IS NOT NULL AND [email protected]
GROUP BY rbh.Year, rbh.Month, rbh.Project, rbh.Task, rbh.User,rbhp.SumPlan, rbhw.DoneSum
ORDER BY rbh.Project, rbh.Task, rbh.User, rbh.Month
RETURN
END
的rbh
子查詢得到的大多數列的值,比如項目號碼,用戶數據等
第一左JOIN(rbhp
)得到的時間之和該用戶計劃在明確的任務和月份中投入項目(返回表中的列PlannedHours
)。
第二個LEFT JOIN(rbhw
)獲取用戶在確定任務和月份中實際用於項目的小時數(返回表中的列DoneHours
)。
將函數轉換爲內聯表值函數。這可能會提高性能。 –
這是一個巨大的查詢。你的源表如何?我會首先優化它們的索引 - 確保您用來加入表的字段被索引。 –
在SSMS中,打開「包含實際執行計劃」。手動運行內部查詢('SELECT'部分)(正確聲明和設置變量),查看查詢計劃。最有可能的是,您可以看到一些非常粗體的箭頭 - 這些指向您查詢零件,需要優化。通常你需要創建一些(覆蓋)索引,有時候會改變邏輯;在'JOIN'條件下使用自定義函數也可以成爲性能殺手。 – Arvo