SQL Server: ค้นหาอย่างรวดเร็ว แต่ช้าจากขั้นตอน


257

แบบสอบถามทำงานเร็ว:

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:

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

ไม่ได้แก้ไข

มันเป็นอย่างไร

  • การสืบค้นนั้นรวดเร็ว
  • ย้ายแบบสอบถามไปยังมุมมองและการเลือกจากมุมมองนั้นรวดเร็ว
  • เลือกจากมุมมองจากกระบวนงานที่เก็บไว้จะช้ากว่าไหม?

ฉันพยายามย้ายคำจำกัดความของมุมมองไปยังขั้นตอนการจัดเก็บโดยตรง (ละเมิดกฎธุรกิจ 3 ข้อและทำลายการห่อหุ้มที่สำคัญ) และนั่นทำให้มันช้าลงประมาณ 6 เท่าเท่านั้น

ทำไมเวอร์ชั่นของโพรซีเดอร์ที่เก็บไว้จึงช้ามาก? บัญชี SQL Server ที่ใช้งาน ad-hoc SQL เร็วกว่า ad-hoc SQL ประเภทอื่นคืออะไร?

ฉันไม่อยาก

  • ฝัง 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)

คนเลวกำลังสปูล 6 ล้านแถว; อีกอันหนึ่งไม่ใช่

หมายเหตุ:นี่ไม่ใช่คำถามเกี่ยวกับการปรับแต่งแบบสอบถาม ฉันมีข้อความค้นหาที่ทำงานเร็วมาก ฉันแค่ต้องการให้ SQL Server ทำงานได้อย่างรวดเร็วจากกระบวนงานที่เก็บไว้


ฉันสังเกตเห็นทุกครั้งที่คุณใช้พารามิเตอร์และมอบหมายให้อีกครั้งจากนั้นใช้ในแบบสอบถามในภายหลังซึ่งอาจเกิดขึ้นได้และเป็นคำตอบที่แนะนำให้ปรับให้เหมาะสมสำหรับ @ "someparamname" ไม่รู้จักสามารถทำงานได้
JustDave

คำตอบ:


404

ฉันมีปัญหาเช่นเดียวกับโปสเตอร์ต้นฉบับ แต่คำตอบที่ยกมาไม่ได้แก้ปัญหาให้ฉัน แบบสอบถามยังคงทำงานช้ามากจากขั้นตอนการจัดเก็บ

ฉันพบคำตอบอื่นที่นี่ " Pariff 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 วินาที


23
+1 แต่สิ่งนี้แปลกมากและมีคำถามมากมายเช่นเราควรทำสิ่งนี้สำหรับทุกขั้นตอนหรือไม่และเมื่อใดควรทำอย่างไร
gotqn

31
ฉันเป็นคนเดียวที่งงงวยกับพฤติกรรมนี้หรือไม่? ต้องการให้ประกาศตัวแปรท้องถิ่นเพื่อป้องกันการดักจับพารามิเตอร์ SQL Server ไม่ควรฉลาดพอที่จะป้องกันไม่ให้สิ่งนี้เกิดขึ้นตั้งแต่แรก? นี่เป็นสาเหตุที่ทำให้โค้ดไม่จำเป็นโดยการออกแบบสั้นของไมโครซอฟท์ IMHO
l46kok

4
15 นาที -> 8 วินาที! ช่วยชีวิต
Tony Brix

3
@BennettDill WITH RECOMPILEไม่ได้สร้างความแตกต่างสำหรับฉันเพียงพารามิเตอร์ท้องถิ่น
mrogers

8
ตอนนี้สามารถทำได้โดยใช้คำใบ้แบบสอบถาม - ตัวเลือก (ปรับให้เหมาะสมสำหรับ (@varA UNKNOWN, @varB UNKNOWN)
เดฟ

131

ฉันพบปัญหานี่คือสคริปต์ของกระบวนการที่เก็บไว้ช้าและเร็ว:

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

หากคุณไม่เห็นความแตกต่างฉันจะไม่โทษคุณ ความแตกต่างไม่ได้อยู่ในขั้นตอนการจัดเก็บเลย ความแตกต่างที่เปลี่ยนการสอบถามค่าใช้จ่ายอย่างรวดเร็ว 0.5 เป็นสิ่งที่ทำให้แกนจัดคิวมีความกระตือรือร้น 6 ล้านแถว

ช้า: SET ANSI_NULLS OFF

อย่างรวดเร็ว: SET ANSI_NULLS ON


คำตอบนี้อาจทำให้เข้าใจได้เนื่องจากมุมมองมีส่วนคำสั่งการรวมที่กล่าวว่า:

(table.column IS NOT NULL)

ดังนั้นจึงมีบางNULLส่วนที่เกี่ยวข้อง


คำอธิบายได้รับการพิสูจน์เพิ่มเติมโดยกลับไปที่ Query 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

และแบบสอบถามช้า


ดังนั้นปัญหาจึงไม่เกิดขึ้นเนื่องจากมีการเรียกใช้แบบสอบถามจากกระบวนงานที่เก็บไว้ ปัญหาคือตัวเลือกการเริ่มต้นการเชื่อมต่อของ Enterprise Manager เป็นANSI_NULLS offมากกว่าANSI_NULLS onซึ่งเป็นค่าเริ่มต้นของ QA

Microsoft รับทราบข้อเท็จจริงนี้ในKB296769 (BUG: ไม่สามารถใช้ SQL Enterprise Manager เพื่อสร้างกระบวนงานที่เก็บไว้ซึ่งมีวัตถุเซิร์ฟเวอร์ที่เชื่อมโยง) วิธีแก้ปัญหารวมถึงANSI_NULLSตัวเลือกในกล่องโต้ตอบกระบวนงานที่เก็บไว้:

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

2
ฉันยังไม่เข้าใจว่าการกลึงANSI_NULLS ONทำให้ประสิทธิภาพแตกต่างกันมากขนาดไหน
Justin Helgerson

2
@ Ek0nomik เพราะคำสั่งที่อยู่ภายในมุมมองมีความหมายแตกต่างกันเมื่อJOIN ANSI_NULLS OFFทันใดนั้นแถวตรงกันทำให้เครื่องมือเพิ่มประสิทธิภาพเรียกใช้แบบสอบถามแตกต่างกันโดยสิ้นเชิง ลองจินตนาการว่าแทนที่จะกำจัด 99.9% ของแถวทั้งหมดพวกเขากลับมาทันที
Ian Boyd

2
หมายเหตุ: ANSI_NULLS OFFเลิกใช้แล้วและถือว่าเป็นการปฏิบัติที่ไม่ดี
jean

2
ลิงก์ "ใน SQL Server เวอร์ชันอนาคต ANSI_NULLS จะเปิดอยู่เสมอและแอปพลิเคชันใด ๆ ที่ตั้งค่าตัวเลือกเป็น OFF อย่างชัดเจนจะสร้างข้อผิดพลาดหลีกเลี่ยงการใช้คุณสมบัตินี้ในงานพัฒนาใหม่และวางแผนที่จะแก้ไขแอปพลิเคชันที่ใช้คุณสมบัตินี้ "
sotn

ไม่ได้ช่วยในกรณีของฉัน
st_stefanov

19

ทำเช่นนี้สำหรับฐานข้อมูลของคุณ ฉันมีปัญหาเดียวกัน - ทำงานได้ดีในฐานข้อมูลหนึ่ง แต่เมื่อฉันคัดลอกฐานข้อมูลนี้ไปยังอีกฐานข้อมูลโดยใช้การนำเข้า SSIS (ไม่ใช่การกู้คืนปกติ) ปัญหานี้เกิดขึ้นกับขั้นตอนการจัดเก็บส่วนใหญ่ของฉัน ดังนั้นหลังจาก googling บางมากขึ้นผมพบว่าบล็อกของ Pinal เดฟ (ซึ่ง BTW ผมพบมากที่สุดของการโพสต์ของเขาและได้ช่วยเหลือฉันมากขอบคุณ Pinal เดฟ)

ฉันเรียกใช้แบบสอบถามด้านล่างในฐานข้อมูลของฉันและแก้ไขปัญหาของฉัน:

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

หวังว่านี่จะช่วยได้ แค่ผ่านความช่วยเหลือจากคนอื่นที่ช่วยฉัน


2
เพียงแค่ FYI สำหรับผู้อ่านในอนาคต: DBCC REINDEXเลิกใช้แล้วดังนั้นคุณควรมองหาทางเลือกอื่น
gvee

1
แก้ไขปัญหาของฉันขอบคุณ (1m20s ลงเหลือ 2 วินาที!) Re: DBCC DBREINDEXMS กล่าวว่า: "คุณสมบัตินี้จะถูกลบใน Microsoft SQL Server รุ่นอนาคตห้ามใช้คุณสมบัตินี้ในงานพัฒนาใหม่และปรับเปลี่ยนแอปพลิเคชั่นที่ใช้คุณสมบัตินี้โดยเร็วที่สุดใช้ ALTER INDEX แทน"
AjV Jsy

ไม่ทราบว่านี่เป็นคำตอบที่ดีที่สุด แต่ในกรณีของฉัน sp_updatestats คือทั้งหมดที่ใช้ดังนั้น +1
Todd Menier

.. ใช่และอย่าลืมว่าการสร้างดัชนีใหม่อาจใช้เวลาและเนื้อที่ว่างดังนั้นก่อนที่คุณจะดำเนินการสิ่งนี้บนเซิร์ฟเวอร์ที่ใช้งานจริงคุณต้องแน่ใจว่าคุณสามารถชะลอตัวลงได้ ฉันขอแนะนำให้ดู REORGANIZE หรือ REBUILD WITH (ออนไลน์ = ON)
มิลาน

14

ฉันพบปัญหาเดียวกัน & โพสต์นี้มีประโยชน์กับฉันมาก แต่ไม่มีคำตอบที่โพสต์แก้ไขปัญหาเฉพาะของฉัน ฉันต้องการโพสต์วิธีแก้ปัญหาที่ใช้งานได้สำหรับฉันด้วยความหวังว่าจะสามารถช่วยเหลือคนอื่นได้

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

ในตอนท้ายของการค้นหาของคุณให้เพิ่ม OPTION (ปรับให้เหมาะสมสำหรับ (@now UNKNOWN))


4

เวลานี้คุณพบปัญหาของคุณ หากครั้งถัดไปที่คุณโชคดีน้อยและไม่สามารถคิดออกได้คุณสามารถใช้การแช่แข็งตามแผนและหยุดกังวลเกี่ยวกับแผนการดำเนินการที่ไม่ถูกต้อง


ตามรายการบล็อกนั้นการแช่แข็งตามแผนมีไว้สำหรับ MS SQL 2005 ขึ้นไปเท่านั้นดังนั้นจึงไม่ช่วย OP ได้
Coxy

ปัญหาคือมันใช้แผนแบบสอบถามที่ไม่ถูกต้อง ฉันไม่ต้องการแช่แข็งให้กับสิ่งที่ผิด
เอียนบอยด์

4

ฉันกำลังประสบปัญหานี้ ข้อความค้นหาของฉันดูเหมือน:

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 และ voila! ไปจาก 30 นาทีถึง 1 นาที!

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

2
ขอบคุณมากลีสิ่งนี้ช่วยชีวิตฉันไว้! นี่คือวิธีที่ฉันได้รับเพียงส่วนวันที่ของเขตข้อมูลวันที่และเวลา: DATEADD (dd, 0, DATEDIFF (dd, 0, table.field))
Julien B.

1
นี่แก้ไขปัญหาของฉัน ฉันมี varchar คอลัมน์ (20) และพารามิเตอร์ของฉันคือ nvarchar (50) เมื่อฉันทำพารามิเตอร์ประเภทเดียวกับประเภทคอลัมน์ - ไม่มีความล่าช้าอีกต่อไป
st_stefanov

3

คุณได้ลองสร้างสถิติและ / หรือดัชนีใหม่ในตาราง Report_Opener แล้วหรือยัง recomplies ทั้งหมดของ SP จะไม่คุ้มค่าถ้าสถิติยังคงแสดงข้อมูลจากเมื่อฐานข้อมูลถูก inauguarated ครั้งแรก

คิวรีเริ่มต้นทำงานได้อย่างรวดเร็วเนื่องจากเครื่องมือเพิ่มประสิทธิภาพสามารถเห็นได้ว่าพารามิเตอร์จะไม่มีวันว่าง ในกรณีของ SP ตัวเพิ่มประสิทธิภาพไม่สามารถแน่ใจได้ว่าพารามิเตอร์จะไม่เป็นโมฆะ


มีวิธีระบุในการประกาศโพรซีเดอร์ที่เก็บไว้ซึ่งพารามิเตอร์ i ไม่สามารถเป็นโมฆะได้หรือไม่? และไม่ใช่สิ่งที่จะแก้ไขโดย sp_executesql?
เอียนบอยด์

ใน word nope ไม่ใช่ในปี 2000 2005 เพิ่มคำใบ้การสืบค้นที่คุณสามารถให้ค่าตัวอย่างสำหรับพารามิเตอร์ตัวเพิ่มประสิทธิภาพจะปรับให้เหมาะสมราวกับว่ารู้ว่ามีการใช้พารามิเตอร์เสมอ ต้องบอกว่าโดยทั่วไปฉันได้พบสิ่งนี้เป็นปัญหาสถิติ
AnthonyWJones

หากเป็นปัญหาสถิติพวกเขาทำงานได้ดีจาก QA เมื่อฉันเรียกใช้ ad-hoc, sp_executesql, exec () และทำไมพวกเขาทั้งหมดจึงทำงานได้ไม่ดีเมื่อโพรซีเดอร์ที่เก็บมี ad-hoc sql, sp_executesql, exec ()
เอียนบอยด์

1

แม้ว่าโดยทั่วไปแล้วฉันจะไม่เห็นด้วย (แม้ว่าในกรณีนี้ดูเหมือนว่าคุณมีเหตุผลที่แท้จริง) แต่คุณได้ลองให้คำแนะนำเกี่ยวกับแบบสอบถามในเวอร์ชัน SP ของคำถามหรือไม่ ถ้า SQL Server กำลังเตรียมแผนการดำเนินการที่แตกต่างกันในสองอินสแตนซ์เหล่านั้นคุณสามารถใช้คำใบ้เพื่อบอกดัชนีที่จะใช้เพื่อให้แผนนั้นตรงกับแผนแรก

สำหรับตัวอย่างบางส่วนที่คุณสามารถไปที่นี่

แก้ไข: หากคุณสามารถโพสต์แผนแบบสอบถามของคุณที่นี่บางทีเราสามารถระบุความแตกต่างระหว่างแผนที่บอก

วินาที: อัปเดตลิงก์ให้เฉพาะ SQL-2000 คุณจะต้องเลื่อนดูวิธีต่างๆ แต่มีหัวข้อที่สองที่ชื่อว่า "คำแนะนำตาราง" ซึ่งเป็นสิ่งที่คุณกำลังมองหา

สาม: ข้อความค้นหา "ไม่ดี" ดูเหมือนจะไม่สนใจ [IX_Openers_SessionGUID] ในตาราง "Openers" - โอกาสใดก็ตามที่เพิ่มคำใบ้ INDEX เพื่อบังคับให้ใช้ดัชนีนั้นจะเปลี่ยนแปลงสิ่งต่างๆ


คำแนะนำการสืบค้นที่มีประโยชน์ที่สุดในการอ้างอิงนั้นไม่มีอยู่ใน SQL 2000 ซึ่งเป็นรุ่นที่เป็นปัญหาที่นี่
AnthonyWJones

นอกจากนี้ต้องการคำแนะนำอะไร? SQL Server สามารถคิดออกได้อย่างไม่มีปัญหาเมื่อฉันเรียกใช้แบบเฉพาะกิจ
เอียนบอยด์

แน่นอนและนั่นก็เป็นประสบการณ์ของฉันเช่นกัน อย่างไรก็ตามในกรณีนี้เขาบอกว่ามันกำลังเกิดขึ้นกับแผนบริหารที่แตกต่างกันโดยสิ้นเชิง อาจมีดัชนีที่ใช้เฉพาะกิจ แต่ด้วยเหตุผลบางอย่างที่ถูกเพิกเฉยใน proc เขาสามารถบังคับให้ SQL Server ใช้ดัชนีด้วยคำแนะนำ "INDEX"
SqlRyan

1

สิ่งนี้อาจไม่น่าเป็นไปได้ แต่เนื่องจากพฤติกรรมที่คุณสังเกตเห็นนั้นผิดปกติต้องมีการตรวจสอบและไม่มีใครพูดถึงมัน

คุณเป็นอย่างแน่ใจว่าวัตถุทั้งหมดเป็นเจ้าของโดย dbo และคุณไม่ได้มีสำเนาโกงเป็นเจ้าของด้วยตัวเองหรือเป็นของขวัญของผู้ใช้ที่แตกต่างกันเช่นกัน?

เพียงบางครั้งเมื่อฉันเห็นพฤติกรรมแปลก ๆ นั่นเป็นเพราะมีวัตถุสองชุดและสิ่งที่คุณได้รับนั้นขึ้นอยู่กับสิ่งที่ระบุไว้และผู้ที่คุณเข้าสู่ระบบด้วย ตัวอย่างเช่นเป็นไปได้อย่างสมบูรณ์ที่จะมีสองสำเนาของมุมมองหรือโพรซีเดอร์ที่มีชื่อเดียวกัน แต่เป็นเจ้าของโดยเจ้าของที่แตกต่างกัน - สถานการณ์ที่อาจเกิดขึ้นเมื่อคุณไม่ได้เข้าสู่ระบบฐานข้อมูลเป็น dbo และลืมระบุ dbo เป็นเจ้าของวัตถุเมื่อ คุณสร้างวัตถุ

โปรดทราบว่าในข้อความที่คุณกำลังเรียกใช้บางสิ่งโดยไม่ระบุเจ้าของเช่น

sp_recompile ViewOpener

ถ้ามีตัวอย่างที่ viewOpener สองชุดปรากฏอยู่เป็นเจ้าของโดย dbo และ [ผู้ใช้อื่น ๆ ] จากนั้นเป็นที่หนึ่งที่คุณ recompile จริงถ้าคุณไม่ได้ระบุขึ้นอยู่กับสถานการณ์ เหมือนกันกับมุมมอง Report_Opener - หากมีสองชุด (และพวกเขาอาจแตกต่างกันในสเปคหรือแผนการดำเนินการ) สิ่งที่ใช้ขึ้นอยู่กับสถานการณ์ - และในขณะที่คุณไม่ได้ระบุเจ้าของเป็นไปได้อย่างสมบูรณ์ที่แบบสอบถาม adhoc ของคุณอาจใช้ ขั้นตอนการรวบรวมอาจใช้ใช้อื่น ๆ

อย่างที่ฉันพูดอาจเป็นไปได้ยาก แต่ก็เป็นไปได้และควรตรวจสอบเพราะปัญหาของคุณอาจเป็นไปได้ว่าคุณกำลังมองหาข้อผิดพลาดในที่ที่ผิด


1

สิ่งนี้อาจฟังดูไร้สาระและดูเหมือนชัดเจนจากชื่อ SessionGUID แต่คอลัมน์เป็นตัวระบุที่ไม่ซ้ำกันใน Report_Opener หรือไม่ หากไม่เป็นเช่นนั้นคุณอาจต้องการลองคัดเลือกมันให้เป็นประเภทที่ถูกต้องและให้ช็อตหรือประกาศตัวแปรของคุณให้เป็นประเภทที่ถูกต้อง

แผนที่สร้างขึ้นเป็นส่วนหนึ่งของ sproc อาจทำงานโดยไม่ได้ตั้งใจและทำการส่งภายในภายในตารางขนาดใหญ่


มันไม่ใช่. แต่ฉันได้เห็นปัญหาเกี่ยวกับประสิทธิภาพการทำงานกับส่วนคำสั่งที่เปรียบเทียบvarcharคอลัมน์กับnvarcharค่า (เช่นWHERE CustomerName = N'zrendall') SQL Server ต้องแปลงค่าทุกคอลัมน์เป็นค่าnvarcharก่อนการเปรียบเทียบ
Ian Boyd

0

ฉันมีความคิดอื่น ถ้าคุณสร้างฟังก์ชั่นที่อิงกับตารางนี้:

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

แล้วเลือกจากมันโดยใช้คำสั่งต่อไปนี้ (แม้วางไว้ใน SP ของคุณ):

SELECT *
FROM tbfSelectFromView(@SessionGUID)

ดูเหมือนว่าสิ่งที่เกิดขึ้น (ซึ่งทุกคนได้ให้ความเห็นไว้แล้ว) คือ SQL Server เพิ่งตั้งสมมติฐานที่ใดที่หนึ่งผิดและบางทีนี่อาจเป็นการบังคับให้แก้ไขข้อสมมติฐาน ฉันเกลียดที่จะเพิ่มขั้นตอนพิเศษ แต่ฉันไม่แน่ใจว่ามีสาเหตุอะไรอีกบ้าง


0

- นี่คือทางออก:

create procedure GetOrderForCustomers(@CustID varchar(20))

as

begin

select * from orders

where customerid = ISNULL(@CustID, '')

end

-- แค่นั้นแหละ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.