Sql-Server-2012

執行計劃與儲存過程不匹配

  • June 3, 2013

下面是一個儲存過程,用於計算一個月內製造的小元件數量。如果沒有製作小元件,則不存在任何記錄。

執行計劃顯示了INNER JOINM 和 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-sargeableMONTH()函式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]

引用自:https://dba.stackexchange.com/questions/43682