Sql-Server-2012
執行計劃與儲存過程不匹配
下面是一個儲存過程,用於計算一個月內製造的小元件數量。如果沒有製作小元件,則不存在任何記錄。
執行計劃顯示了
INNER JOIN
M 和 A 表中的一個,在語句中我正在執行一個LEFT OUTER JOIN
.我想計算一個時間範圍內製作的小元件數量,將其加入月表(1 月至 12 月),並將結果顯示在 SSRS 報告中。加入是因為我無法
COUNT
獲取不存在的數據。目前我得到:MonthName Widget Count February 2 March 3 April 4 May 6 June 5 July 4 August 6 September 2 October 4 November 1 December 2
我希望列表中包含沒有製作小元件的月份。
這是程式碼:
DECLARE @OName varchar(50) DECLARE @Start_Date DATE DECLARE @End_Date DATE SET @OName = 'John' SET @Start_Date = '01/01/2012' SET @End_Date = '12/31/2012' SELECT M.[MonthName] ,COUNT(A.[Widget_ID]) AS 'Widget Count' FROM [Connector].dbo.[Months] AS M LEFT OUTER JOIN [SERVER].[DATABASE].[dbo].[Widget] AS A ON MONTH(A.[Widget_Date]) = M.[MonthID] WHERE (A.[Operator_Name] LIKE '%'+ @OName +'%') AND A.[PlantID] = '00000001' AND ( (A.Widget_Date >= @Start_Date AND @End_Date IS NULL) OR (A.Widget_Datet <= @End_Date AND @Start_Date IS NULL) OR (A.Widget_Date >= @Start_Date AND A.Widget_Date <= @End_Date) OR (@Start_Date IS NULL AND @End_Date IS NULL) GROUP BY M.[MonthName], M.MonthID ORDER BY M.[MonthID]
下面是這個查詢的執行計劃。
::UPDATE1:: 我剛剛將月份的名稱插入到此表中。我無法更改遠端表。
CREATE TABLE [dbo].[Months]( [MonthID] [smallint] IDENTITY(1,1) NOT NULL, [MonthName] [varchar](25) NOT NULL, CONSTRAINT [PK_Months] PRIMARY KEY CLUSTERED ([MonthID] ASC) ) GO INSERT INTO [dbo].[Months] ([MonthName]) VALUES (<MonthName, varchar(25),>) GO
“小元件”表有 250 多個欄位,這將花費我很長時間才能匿名。
你的程式碼說你正在做 a
LEFT OUTER JOIN
,但你真的嗎?您的第一個WHERE
子句(以及隨後的每個子句)從外部表中過濾要包含的行:WHERE (A.[Operator_Name] LIKE '%'+ @OName +'%')
無論您是否有意,這都會將您
LEFT OUTER JOIN
變成。INNER JOIN
也許您打算將這些過濾器移到ON
子句中。當然,我可能會從 中刪除 non-sargeable
MONTH()
函式JOIN
,因為這將強制對遠端表進行完整掃描,並改為以這種方式編寫 - 也消除了與本地月份表的連接:DECLARE @OName VARCHAR(50) = 'John', @Start_Date DATE = NULL,--'20120101', -- use safe date formats @End_Date DATE = '20121231'; -- use semi-colons -- deal with NULLs here so the query is simpler: SELECT @Start_Date = COALESCE(@Start_Date, '20010101'), @End_Date = COALESCE(@End_Date, CURRENT_TIMESTAMP); ;WITH n(n) AS ( -- get the # of months you need instead of relying on your Connector table: SELECT TOP (DATEDIFF(MONTH, @Start_Date, @End_Date)+1) Number FROM master..spt_values WHERE [type] = N'P' AND Number >= 0 ORDER BY Number ), m(m) AS ( -- convert those to months: SELECT DATEADD(MONTH, n, DATEADD(MONTH, DATEDIFF(MONTH, 0, @Start_Date), 0)) FROM n ) SELECT m.m, MonthName = DATENAME(MONTH, m.m), [Widget Count] = COUNT(w.Widget_ID) FROM m LEFT OUTER JOIN [Server].[Database].dbo.Widget AS w -- meaningful alias ON w.Widget_Date >= m.m AND w.Widget_Date < DATEADD(MONTH, 1, m.m) -- open-ended date range query AND w.Operator_Name LIKE '%' + @OName + '%' GROUP BY m.m ORDER BY m.m;
只需忽略輸出中的
m
列(如果不將其包含在輸出中就不能按它排序,按名稱排序也沒有意義)。另一個建議:不要
'single quotes'
用作別名分隔符。這種形式已被棄用,它也使列別名看起來像字元串文字。改為使用[square brackets]
。