2009-01-13 66 views
201

查詢運行速度快:SQL服務器:查詢速度快,但速度緩慢的過程

DECLARE @SessionGUID uniqueidentifier 
SET @SessionGUID = 'BCBA333C-B6A1-4155-9833-C495F22EA908' 

SELECT * 
FROM Report_Opener 
WHERE SessionGUID = @SessionGUID 
ORDER BY CurrencyTypeOrder, Rank 

子樹成本:0.502

但是把相同的SQL存儲過程運行速度慢,並用完全不同的執行計劃

CREATE PROCEDURE dbo.ViewOpener @SessionGUID uniqueidentifier AS 
SELECT * 
FROM Report_Opener 
WHERE SessionGUID = @SessionGUID 
ORDER BY CurrencyTypeOrder, Rank 

EXECUTE ViewOpener @SessionGUID 

子樹成本:19.2

我已經運行

sp_recompile ViewOpener 

它仍然運行在相同的(嚴重),而且我也改變了存儲 過程

CREATE PROCEDURE dbo.ViewOpener @SessionGUID uniqueidentifier AS 
SELECT *, 'recompile please' 
FROM Report_Opener 
WHERE SessionGUID = @SessionGUID 
ORDER BY CurrencyTypeOrder, Rank 

,然後再返回,試圖欺騙真它進入重新編譯。

我已經刪除並重新創建存儲過程,以便讓它生成一個新的計劃。

我試圖重新編譯迫,和防止參數嗅探,通過使用誘餌變量:

CREATE PROCEDURE dbo.ViewOpener @SessionGUID uniqueidentifier AS 

DECLARE @SessionGUIDbitch uniqueidentifier 
SET @SessionGUIDbitch = @SessionGUID 

SELECT * 
FROM Report_Opener 
WHERE SessionGUID = @SessionGUIDbitch 
ORDER BY CurrencyTypeOrder, Rank 

我還試圖限定存儲過程WITH RECOMPILE

CREATE PROCEDURE dbo.ViewOpener @SessionGUID uniqueidentifier 
WITH RECOMPILE 
AS 
SELECT * 
FROM Report_Opener 
WHERE SessionGUID = @SessionGUID 
ORDER BY CurrencyTypeOrder, Rank 

因此,它的計劃永遠不會被緩存,並且我試過在執行時強制重新編譯:

EXECUTE ViewOpener @SessionGUID WITH RECOMPILE 

哪沒有幫助。

我試過的程序轉換爲動態SQL:

CREATE PROCEDURE dbo.ViewOpener @SessionGUID uniqueidentifier 
WITH RECOMPILE AS 
DECLARE @SQLString NVARCHAR(500) 

SET @SQLString = N'SELECT * 
    FROM Report_OpenerTest 
    WHERE SessionGUID = @SessionGUID 
    ORDER BY CurrencyTypeOrder, Rank' 

EXECUTE sp_executesql @SQLString, 
N'@SessionGUID uniqueidentifier', 
@SessionGUID 

這並沒有幫助。

實體「Report_Opener」是未被索引的視圖。該視圖僅引用基礎表。沒有表格包含計算列,索引或其他。

對於它的地獄我試圖與

SET ANSI_NULLS ON 
SET QUOTED_IDENTIFER ON 

沒有解決它創建視圖。

它是如何

  • 查詢是快速
  • 移動所述查詢的圖,從該視圖從視圖從存儲過程中選擇是快速
  • 選擇是40倍慢?

我試着將視圖的定義直接移動到存儲過程中(違反了3個業務規則,並且破壞了一個重要的封裝),並且僅使其速度降低了大約6倍。

爲什麼存儲過程版本太慢?與其他類型的特別SQL相比,運行臨時SQL的SQL Server可能會更快嗎?

我真的不想

  • 嵌入代碼的SQL
  • 變化的代碼在所有

    Microsoft SQL Server 2000 - 8.00.2050 (Intel X86) 
    Mar 7 2008 21:29:56 
    Copyright (c) 1988-2003 Microsoft Corporation 
    Standard Edition on Windows NT 5.2 (Build 3790: Service Pack 2) 
    

但是可以考慮SQL Server在無法運行速度與運行查詢的SQL Sever一樣快,如果不是參數嗅探。


我的下一次嘗試將有StoredProcedureA通話StoredProcedureB通話StoredProcedureC呼叫StoredProcedureD查詢視圖。

如果失敗了,請讓存儲過程調用存儲過程,調用UDF,調用UDF,調用存儲過程,調用UDF來查詢視圖。


從QA快速歸納起來,以下運行,但放到一個存儲過程時慢:

原:

--Runs fine outside of a stored procedure 
SELECT * 
FROM Report_OpenerTest 
WHERE SessionGUID = @SessionGUID 
ORDER BY CurrencyTypeOrder, Rank 

sp_executesql

--Runs fine outside of a stored procedure 
DECLARE @SQLString NVARCHAR(500) 
SET @SQLString = N'SELECT * 
FROM Report_OpenerTest 
WHERE SessionGUID = @SessionGUID 
ORDER BY CurrencyTypeOrder, Rank' 

EXECUTE sp_executesql @SQLString, 
     N'@SessionGUID uniqueidentifier', 
     @SessionGUID 

EXEC(@sql)

--Runs fine outside of a stored procedure 
DECLARE @sql NVARCHAR(500) 
SET @sql = N'SELECT * 
FROM Report_OpenerTest 
WHERE SessionGUID = '''+CAST(@SessionGUID AS varchar(50))+''' 
ORDER BY CurrencyTypeOrder, Rank' 

EXEC(@sql) 

執行計劃

計劃:

 |--Sort(ORDER BY:([Expr1020] ASC, [Currencies].[Rank] ASC)) 
      |--Compute Scalar(DEFINE:([Expr1020]=If ([Currencies].[CurrencyType]='ctCanadianCash') then 1 else If ([Currencies].[CurrencyType]='ctMiscellaneous') then 2 else If ([Currencies].[CurrencyType]='ctTokens') then 3 else If ([Currencies].[CurrencyType] 
       |--Nested Loops(Left Outer Join, OUTER REFERENCES:([Openers].[OpenerGUID])) 
        |--Filter(WHERE:((([Currencies].[IsActive]<>0 AND [Currencies].[OnOpener]<>0) AND ((((((([Currencies].[CurrencyType]='ctUSCoin' OR [Currencies].[CurrencyType]='ctMiscellaneousUS') OR [Currencies].[CurrencyType]='ctUSCash') OR [Currencies]. 
        | |--Nested Loops(Left Outer Join, OUTER REFERENCES:([Currencies].[CurrencyGUID], [Openers].[OpenerGUID]) WITH PREFETCH) 
        |   |--Nested Loops(Left Outer Join) 
        |   | |--Bookmark Lookup(BOOKMARK:([Bmk1016]), OBJECT:([GrobManagementSystemLive].[dbo].[Windows])) 
        |   | | |--Nested Loops(Inner Join, OUTER REFERENCES:([Openers].[WindowGUID])) 
        |   | |   |--Bookmark Lookup(BOOKMARK:([Bmk1014]), OBJECT:([GrobManagementSystemLive].[dbo].[Openers])) 
        |   | |   | |--Index Seek(OBJECT:([GrobManagementSystemLive].[dbo].[Openers].[IX_Openers_SessionGUID]), SEEK:([Openers].[SessionGUID]=[@SessionGUID]) ORDERED FORWARD) 
        |   | |   |--Index Seek(OBJECT:([GrobManagementSystemLive].[dbo].[Windows].[IX_Windows]), SEEK:([Windows].[WindowGUID]=[Openers].[WindowGUID]) ORDERED FORWARD) 
        |   | |--Clustered Index Scan(OBJECT:([GrobManagementSystemLive].[dbo].[Currencies].[IX_Currencies_CurrencyType])) 
        |   |--Clustered Index Seek(OBJECT:([GrobManagementSystemLive].[dbo].[OpenerDetails].[IX_OpenerDetails_OpenerGUIDCurrencyGUID]), SEEK:([OpenerDetails].[OpenerGUID]=[Openers].[OpenerGUID] AND [OpenerDetails].[CurrencyGUID]=[Currenc 
        |--Hash Match(Cache, HASH:([Openers].[OpenerGUID]), RESIDUAL:([Openers].[OpenerGUID]=[Openers].[OpenerGUID])) 
          |--Stream Aggregate(DEFINE:([Expr1006]=SUM(If (((([Currencies].[CurrencyType]='ctMiscellaneous' OR [Currencies].[CurrencyType]='ctTokens') OR [Currencies].[CurrencyType]='ctChips') OR [Currencies].[CurrencyType]='ctCanadianCoin') OR [ 
           |--Nested Loops(Inner Join, OUTER REFERENCES:([OpenerDetails].[CurrencyGUID]) WITH PREFETCH) 
            |--Nested Loops(Inner Join) 
            | |--Index Seek(OBJECT:([GrobManagementSystemLive].[dbo].[Openers].[IX_Openers_OneOpenerPerSession]), SEEK:([Openers].[OpenerGUID]=[Openers].[OpenerGUID]) ORDERED FORWARD) 
            | |--Clustered Index Seek(OBJECT:([GrobManagementSystemLive].[dbo].[OpenerDetails].[IX_OpenerDetails_OpenerGUIDCurrencyGUID]), SEEK:([OpenerDetails].[OpenerGUID]=[Openers].[OpenerGUID]) ORDERED FORWARD) 
            |--Index Seek(OBJECT:([GrobManagementSystemLive].[dbo].[Currencies].[PK_Currencies_CurrencyGUID]), SEEK:([Currencies].[CurrencyGUID]=[OpenerDetails].[CurrencyGUID]) ORDERED FORWARD) 

計劃

 |--Sort(ORDER BY:([Expr1020] ASC, [Currencies].[Rank] ASC)) 
      |--Compute Scalar(DEFINE:([Expr1020]=If ([Currencies].[CurrencyType]='ctCanadianCash') then 1 else If ([Currencies].[CurrencyType]='ctMiscellaneous') then 2 else If ([Currencies].[CurrencyType]='ctTokens') then 3 else If ([Currencies].[Currency 
       |--Nested Loops(Left Outer Join, OUTER REFERENCES:([Openers].[OpenerGUID])) 
         |--Filter(WHERE:((([Currencies].[IsActive]<>0 AND [Currencies].[OnOpener]<>0) AND ((((((([Currencies].[CurrencyType]='ctUSCoin' OR [Currencies].[CurrencyType]='ctMiscellaneousUS') OR [Currencies].[CurrencyType]='ctUSCash') OR [Currenc 
         | |--Nested Loops(Left Outer Join, OUTER REFERENCES:([Currencies].[CurrencyGUID], [Openers].[OpenerGUID]) WITH PREFETCH) 
         |   |--Filter(WHERE:([Openers].[SessionGUID]=[@SessionGUID])) 
         |   | |--Concatenation 
         |   |   |--Nested Loops(Left Outer Join) 
         |   |   | |--Table Spool 
         |   |   | | |--Hash Match(Inner Join, HASH:([Windows].[WindowGUID])=([Openers].[WindowGUID]), RESIDUAL:([Windows].[WindowGUID]=[Openers].[WindowGUID])) 
         |   |   | |   |--Clustered Index Scan(OBJECT:([GrobManagementSystemLive].[dbo].[Windows].[IX_Windows_CageGUID])) 
         |   |   | |   |--Table Scan(OBJECT:([GrobManagementSystemLive].[dbo].[Openers])) 
         |   |   | |--Table Spool 
         |   |   |   |--Clustered Index Scan(OBJECT:([GrobManagementSystemLive].[dbo].[Currencies].[IX_Currencies_CurrencyType])) 
         |   |   |--Compute Scalar(DEFINE:([Openers].[OpenerGUID]=NULL, [Openers].[SessionGUID]=NULL, [Windows].[UseChipDenominations]=NULL)) 
         |   |    |--Nested Loops(Left Anti Semi Join) 
         |   |     |--Clustered Index Scan(OBJECT:([GrobManagementSystemLive].[dbo].[Currencies].[IX_Currencies_CurrencyType])) 
         |   |     |--Row Count Spool 
         |   |      |--Table Spool 
         |   |--Clustered Index Seek(OBJECT:([GrobManagementSystemLive].[dbo].[OpenerDetails].[IX_OpenerDetails_OpenerGUIDCurrencyGUID]), SEEK:([OpenerDetails].[OpenerGUID]=[Openers].[OpenerGUID] AND [OpenerDetails].[CurrencyGUID]=[Cu 
         |--Hash Match(Cache, HASH:([Openers].[OpenerGUID]), RESIDUAL:([Openers].[OpenerGUID]=[Openers].[OpenerGUID])) 
          |--Stream Aggregate(DEFINE:([Expr1006]=SUM([partialagg1034]), [Expr1007]=SUM([partialagg1035]), [Expr1008]=SUM([partialagg1036]), [Expr1009]=SUM([partialagg1037]), [Expr1010]=SUM([partialagg1038]), [Expr1011]=SUM([partialagg1039] 
           |--Nested Loops(Inner Join) 
            |--Stream Aggregate(DEFINE:([partialagg1034]=SUM(If (((([Currencies].[CurrencyType]='ctMiscellaneous' OR [Currencies].[CurrencyType]='ctTokens') OR [Currencies].[CurrencyType]='ctChips') OR [Currencies].[CurrencyType]=' 
            | |--Nested Loops(Inner Join, OUTER REFERENCES:([OpenerDetails].[CurrencyGUID]) WITH PREFETCH) 
            |   |--Clustered Index Seek(OBJECT:([GrobManagementSystemLive].[dbo].[OpenerDetails].[IX_OpenerDetails_OpenerGUIDCurrencyGUID]), SEEK:([OpenerDetails].[OpenerGUID]=[Openers].[OpenerGUID]) ORDERED FORWARD) 
            |   |--Index Seek(OBJECT:([GrobManagementSystemLive].[dbo].[Currencies].[PK_Currencies_CurrencyGUID]), SEEK:([Currencies].[CurrencyGUID]=[OpenerDetails].[CurrencyGUID]) ORDERED FORWARD) 
            |--Index Seek(OBJECT:([GrobManagementSystemLive].[dbo].[Openers].[IX_Openers_OneOpenerPerSession]), SEEK:([Openers].[OpenerGUID]=[Openers].[OpenerGUID]) ORDERED FORWARD) 

壞,一個是急於後臺處理600萬行;另一個不是。

注意:這不是關於調整查詢的問題。我有一個快速運行的查詢。我只想讓SQL Server從存儲過程中快速運行。

回答

120

我發現這個問題,這裏的存儲過程的快慢版本的腳本:

dbo.ViewOpener__RenamedForCruachan__Slow.PRC

SET QUOTED_IDENTIFIER OFF 
GO 
SET ANSI_NULLS OFF 
GO 

CREATE PROCEDURE dbo.ViewOpener_RenamedForCruachan_Slow 
    @SessionGUID uniqueidentifier 
AS 

SELECT * 
FROM Report_Opener_RenamedForCruachan 
WHERE SessionGUID = @SessionGUID 
ORDER BY CurrencyTypeOrder, Rank 
GO 

SET QUOTED_IDENTIFIER OFF 
GO 
SET ANSI_NULLS ON 
GO 

dbo.ViewOpener__RenamedForCruachan__Fast.PRC

SET QUOTED_IDENTIFIER OFF 
GO 
SET ANSI_NULLS ON 
GO 

CREATE PROCEDURE dbo.ViewOpener_RenamedForCruachan_Fast 
    @SessionGUID uniqueidentifier 
AS 

SELECT * 
FROM Report_Opener_RenamedForCruachan 
WHERE SessionGUID = @SessionGUID 
ORDER BY CurrencyTypeOrder, Rank 
GO 

SET QUOTED_IDENTIFIER OFF 
GO 
SET ANSI_NULLS ON 
GO 

如果你沒沒有發現差異,我不怪你。根本不在存儲過程中。

慢:SET ANSI_NULLS OFF

快速:SET ANSI_NULLS ON


這個答案也可以是把一個快0.5費用查詢到一個沒有600萬行的渴望閥芯的區別因爲視圖確實有一個連接子句:

(table.column IS NOT NULL) 

所以涉及到一些NULL


的解釋是通過返回到查詢Analizer和運行

SET ANSI_NULLS OFF 

進一步證實。

DECLARE @SessionGUID uniqueidentifier 
SET @SessionGUID = 'BCBA333C-B6A1-4155-9833-C495F22EA908' 

SELECT * 
FROM Report_Opener_RenamedForCruachan 
WHERE SessionGUID = @SessionGUID 
ORDER BY CurrencyTypeOrder, Rank 

而查詢很慢。


所以問題不是因爲查詢正在從一個存儲過程運行。問題是企業管理器的連接默認選項是ANSI_NULLS off,而不是ANSI_NULLS on,這是QA的默認設置。

Microsoft在KB296769(BUG:無法使用SQL企業管理器創建包含鏈接的服務器對象的存儲過程)中承認這一事實。解決方法是包含在存儲過程對話框中的選項ANSI_NULLS

Set ANSI_NULLS ON 
Go 
Create Proc spXXXX as 
.... 
+0

我仍然不明白如何轉換ANSI_NULLS ON使得性能有如此巨大的差異。 – 2013-07-16 16:42:18

+0

@ Ek0nomik因爲'ANSI_NULLS OFF'時視圖內部的JOIN子句具有不同的含義。突然行匹配,導致優化器完全不同地運行查詢。想象一下,不是排除所有行中的99.9%,而是突然返回。 – 2013-07-16 23:00:51

1

雖然我通常反對它(儘管在這種情況下似乎你有一個真正的原因),你是否嘗試過在查詢的SP版本上提供任何查詢提示?如果SQL Server在這兩個實例中準備了不同的執行計劃,您是否可以使用提示來告訴它要使用哪個索引,以便計劃與第一個匹配?

對於一些示例,you can go here

編輯:如果你可以在這裏發佈你的查詢計劃,也許我們可以識別計劃的一些區別。

SECOND:更新鏈接爲SQL-2000特定。您必須向下滾動某種方式,但還有另一個標題爲「表格提示」,這就是您要查找的內容。

THIRD:「壞」查詢似乎忽略了「Openers」表上的[IX_Openers_SessionGUID] - 任何添加INDEX提示以強制它使用該索引的機會會改變什麼?

+0

在參考的最有用的查詢提示並非適用於SQL 2000這是這裏討論的版本。 – AnthonyWJones 2009-01-13 21:59:43

+0

此外,需要什麼提示?當我特別運行它時,SQL Server能夠發現它沒有問題。 – 2009-01-13 22:05:38

+0

當然,這也是我的經驗。然而,在這種情況下,他說這是一個完全不同的執行計劃。也許有一個特殊的索引,但由於某些原因在proc中被忽略。他可以強制SQL Server使用帶有「INDEX」提示的索引。 – SqlRyan 2009-01-13 22:12:20

1

您是否嘗試過重建Report_Opener表上的統計信息和/或索引。如果統計數據仍顯示數據庫首次啓動時的數據,那麼所有SP的重新編譯都不值得。

初始查詢本身可以快速工作,因爲優化程序可以看到參數永遠不會爲空。在SP的情況下,優化器不能確定該參數永遠不會爲空。

+0

有沒有辦法在存儲過程聲明中指出i參數不能爲空?這不是由sp_executesql修復的東西嗎? – 2009-01-13 22:07:03

+0

在一個單詞中,不在2000年。2005年添加了一個查詢提示,您可以在其中爲參數提供示例值,優化器會優化,就好像它知道總是使用該參數一樣。話雖如此,我通常發現這種事情是一個統計問題。 – AnthonyWJones 2009-01-13 22:14:57

+0

如果是統計問題,當我運行ad-hoc,sp_executesql,exec()時,它們可以在QA中正常工作。爲什麼當存儲過程包含臨時sql,sp_executesql,exec()時,它們都運行得不好? – 2009-01-13 22:18:18

0

我有另一個想法。如果你創建這個基於表格的功能:使用下面的語句(即使把這個在你的SP)

CREATE FUNCTION tbfSelectFromView 
( 
    -- Add the parameters for the function here 
    @SessionGUID UNIQUEIDENTIFIER 
) 
RETURNS TABLE 
AS 
RETURN 
(
    SELECT * 
    FROM Report_Opener 
    WHERE SessionGUID = @SessionGUID 
    ORDER BY CurrencyTypeOrder, Rank 
) 
GO 

,然後從中選擇:

SELECT * 
FROM tbfSelectFromView(@SessionGUID) 

它看起來像發生了什麼(這大家已經評論過)是SQL Server只是假設某個地方有錯誤,也許這會迫使它糾正假設。我討厭添加額外的步驟,但我不確定還有什麼可能導致它。

1

這可能不太可能,但考慮到你觀察到的行爲是不尋常的,它需要檢查,沒有人提到它。

你是絕對確定所有對象都是dbo所有,你沒有自己擁有的流氓拷貝或者不同的用戶在場嗎?

只是偶爾當我看到奇怪的行爲,這是因爲實際上有一個對象的兩個副本,你得到哪一個取決於指定的內容和你登錄的人。例如,完全有可能擁有兩個具有相同名稱但由不同所有者擁有的視圖或過程的副本 - 在沒有以dbo身份登錄到數據庫的情況下可能會出現的情況,並忘記將dbo指定爲對象所有者時你創建該對象。

在注意在文本你沒有指定所有者運行一些東西,比如

 
sp_recompile ViewOpener 

如果舉例來說,其中dbo所擁有viewOpener目前的兩個副本和[其他一些用戶]那麼哪一個你如果你沒有指定,實際上重新編譯依賴於情況。與Report_Opener視圖同上 - 如果存在兩個副本(並且它們可能在規範或執行計劃中有所不同),那麼使用的內容取決於具體情況 - 因爲您沒有指定所有者,所以您的即席查詢可能會使用一個和編譯的程序可以使用其他的。

正如我所說,這可能不太可能,但它是可能的,應該檢查,因爲你的問題可能是你只是在錯誤的地方尋找錯誤。

310

我有同樣的問題,因爲原來的海報,但所引用的答案並沒有解決這個問題對我來說。查詢仍然從存儲過程中運行得非常慢。

我發現了另一個答案here "Parameter Sniffing",謝謝Omnibuzz。歸結爲在存儲過程查詢中使用「局部變量」,但閱讀原始文件以獲得更多理解,這是一個很好的寫法。例如

慢速方式:

CREATE PROCEDURE GetOrderForCustomers(@CustID varchar(20)) 
AS 
BEGIN 
    SELECT * 
    FROM orders 
    WHERE customerid = @CustID 
END 

快速的方法:

CREATE PROCEDURE GetOrderForCustomersWithoutPS(@CustID varchar(20)) 
AS 
BEGIN 
    DECLARE @LocCustID varchar(20) 
    SET @LocCustID = @CustID 

    SELECT * 
    FROM orders 
    WHERE customerid = @LocCustID 
END 

希望這有助於別人,這樣做減少了我的執行時間從5+分鐘至約6-7秒。

3

這次你發現你的問題。如果下一次你不那麼幸運並且無法弄清楚,你可以使用plan freezing並停止擔心錯誤的執行計劃。

15

對您的數據庫執行此操作。我有同樣的問題 - 它在一個數據庫中工作正常,但是當我使用SSIS導入(而不是通常的恢復)將此數據庫複製到另一個時,這個問題發生在我的大多數存儲過程中。所以google搜索了一些之後,我發現了blog of Pinal Dave (which btw, I encountered most of his post and did help me a lot so thanks Pinal Dave)

我執行我的數據庫之下查詢,並修正它我的問題:

EXEC sp_MSforeachtable @command1="print '?' DBCC DBREINDEX ('?', ' ', 80)" 
GO 
EXEC sp_updatestats 
GO 

希望這有助於。只是傳遞幫助我的其他人的幫助。

0

上重建的有關表的索引幫助我度過這個問題

0

- 這裏是解決方案:

create procedure GetOrderForCustomers(@CustID varchar(20)) 

as 

begin 

select * from orders 

where customerid = ISNULL(@CustID, '') 

end 

- 這就是它

3

我遇到此問題。我的查詢看起來是這樣的:

select a, b, c from sometable where date > '20140101' 

我的存儲過程等被定義:

create procedure my_procedure (@dtFrom date) 
as 
select a, b, c from sometable where date > @dtFrom 

我改變了數據類型爲datetime,瞧!從30分鐘到1分鐘!

create procedure my_procedure (@dtFrom datetime) 
as 
select a, b, c from sometable where date > @dtFrom 
1

這可能聽起來很傻,並從名稱SessionGUID似乎是顯而易見的,但列在Report_Opener唯一標識符?如果沒有,你可能想嘗試將它轉換爲正確的類型,並給它一個鏡頭或聲明你的變量爲正確的類型。

作爲sproc一部分創建的計劃可能不直觀,並在大型桌子上進行內部演員表演。

4

我正面臨同樣的問題&這篇文章對我很有幫助,但沒有任何發佈的答案能解決我的具體問題。我想發佈對我有用的解決方案,希望它能幫助別人。

https://stackoverflow.com/a/24016676/814299

在查詢的結尾添加OPTION(OPTIMIZE FOR(@now未知))

相關問題