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

다시 다시, 실제로 재 컴파일하도록 속이려고합니다.

새 계획을 생성하기 위해 저장 프로 시저를 삭제하고 다시 만들었습니다.

decoy 변수를 사용하여 강제로 다시 컴파일 하고 매개 변수 스니핑을 방지 하려고했습니다 .

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보다 ad-hoc 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 Server를 실행할 수없는 이유는 무엇입니까?


나의 다음 시도해야하는 것입니다 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

나는 원래 포스터와 같은 문제가 있었지만 인용 된 답변으로 문제가 해결되지 않았습니다. 저장 프로 시저에서 쿼리가 여전히 느리게 실행되었습니다.

여기에 다른 답변이 있습니다. "Parameter Sniffing" , Thanks 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가 처음에 이런 일이 발생하지 않도록 충분히 똑똑하지 않아야합니까? 이것은 Microsoft의 근시안적인 디자인 IMHO에 의해 불필요한 코드 팽창을 유발합니다.
l46kok

4
15 분-> 8 초! 생명의 은인
Tony Brix

3
@ BennettDill WITH RECOMPILE은 로컬 매개 변수 만 나에게 차이를주지 않았습니다.
mrogers

8
이것은 쿼리 힌트-OPTION (OPTIMIZE FOR (@varA UNKNOWN, @varB UNKNOWN)
Dave

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 onQA의 기본이다.

Microsoft는 KB296769 에서이 사실을 인정합니다 (버그 : SQL Enterprise Manager를 사용하여 연결된 서버 개체를 포함하는 저장 프로 시저를 만들 수 없음). 해결 방법은 ANSI_NULLS저장 프로 시저 대화 상자에 옵션 이 포함되어 있습니다.

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

2
터닝 ANSI_NULLS ON이 어떻게 그렇게 큰 성능 차이를 만들어 내는지 아직도 이해하지 못합니다 .
저스틴 Helgerson

2
@ Ek0nomik JOIN뷰 내부 의 절은 when 의미가 다르기 때문 ANSI_NULLS OFF입니다. 갑자기 행이 일치하여 옵티마이 저가 쿼리를 완전히 다르게 실행합니다. 모든 행의 99.9 %를 제거하지 않고 갑자기 돌아 온다고 상상해보십시오.
Ian Boyd

2
참고 : ANSI_NULLS OFF더 이상 사용되지 않으며 나쁜 습관으로 간주됩니다
jean

2
link "향후의 SQL Server 버전에서는 ANSI_NULLS가 항상 ON이고 옵션을 명시 적으로 OFF로 설정 한 응용 프로그램은 오류를 생성합니다. 새로운 개발 작업에서는이 기능을 사용하지 말고 현재이 기능을 사용하는 응용 프로그램은 수정하십시오. "
sotn

내 경우에는 도움이되지 않았습니다.
st_stefanov

19

데이터베이스에이를 수행하십시오. 같은 문제가 있습니다-한 데이터베이스에서는 정상적으로 작동하지만 SSIS 가져 오기 (일반적인 복원 아님)를 사용 하여이 데이터베이스를 다른 데이터베이스에 복사하면이 문제는 대부분의 저장 프로 시저에서 발생합니다. 그래서 더 인터넷 검색을 한 후, 나는 Pinal Dave블로그를 찾았습니다 (btw, 나는 그의 게시물의 대부분을 만났고 Pinal Dave에게 많은 도움을주었습니다) .

내 데이터베이스에서 아래 쿼리를 실행하고 문제가 해결되었습니다.

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

도움이 되었기를 바랍니다. 나에게 도움이 된 다른 사람들의 도움을 전달하십시오.


2
미래의 독자들을위한 FYI : DBCC REINDEX더 이상 사용되지 않으므로 대안을 찾아야합니다.
gvee

1
고맙습니다 (1m20s에서 2s로!). 재 : DBCC DBREINDEX, MS는 말한다 : "이 기능은 마이크로 소프트 SQL Server의 이후 버전에서 제거 될 예정입니다 새 개발 작업에서는이 기능을 사용하여, 현재 곧 가능한 대신 사용 ALTER INDEX 등으로이 기능을 사용하는 응용 프로그램은 수정하지 마십시오..."
AjV Jsy

이것이 가장 좋은 대답인지 모르지만 제 경우에는 sp_updatestats가 전부입니다. +1
Todd Menier

.. 그렇습니다. 인덱스를 다시 작성하는 데 시간과 공간이 소요될 수 있으므로 프로덕션 서버에서 인덱스를 실행하기 전에 속도 저하가 발생할 수 있는지 확인하십시오. REORGANIZE 또는 REBUILD WITH (ONLINE = ON)을 살펴볼 것을 제안합니다.
Milan

14

나는 같은 문제에 직면하고 있었고이 게시물은 나에게 매우 도움이되었지만 게시 된 답변 중 어느 것도 내 특정 문제를 해결하지 못했습니다. 다른 사람을 도울 수 있기를 희망하는 나를 위해 일한 솔루션을 게시하고 싶었습니다.

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

검색어 끝에 OPTION (OPTIMIZE FOR (@now UNKNOWN))을 추가하십시오.


4

이번에는 문제를 발견했습니다. 다음에 운이 좋지 않고 파악할 수 없으면 계획 동결을 사용 하고 잘못된 실행 계획에 대해 걱정하지 않아도됩니다.


이 블로그 항목에 따르면 계획 동결은 MS SQL 2005 이상에서만 가능하므로 OP에 도움이되지 않습니다.
Coxy

문제는 잘못된 쿼리 계획을 사용하고 있었다는 것입니다. 나는 그것을 잘못된 것으로 얼고 싶지 않습니다.
Ian Boyd

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 테이블에서 통계 및 / 또는 인덱스를 다시 작성해 보셨습니까? 통계가 데이터베이스가 처음 취임했을 때의 데이터를 표시하는 경우 SP의 모든 재 준수는 아무 가치가 없습니다.

옵티마이 저가 매개 변수가 널이 아님을 알 수 있기 때문에 초기 쿼리 자체가 빠르게 작동합니다. SP의 경우 옵티마이 저는 매개 변수가 널이되지 않을 것이라고 확신 할 수 없습니다.


저장 프로 시저 선언에서 i 매개 변수가 null 일 수 없다는 것을 나타내는 방법이 있습니까? 그리고 그것은 sp_executesql에 의해 고쳐질 것이 아닌가?
Ian Boyd

2000 년이 아니라 nope라는 단어에서 2005 년에는 매개 변수의 예제 값을 제공 할 수있는 쿼리 힌트가 추가되었는데, 옵티마이 저는 매개 변수가 항상 사용되었다는 것을 알고있는 것처럼 최적화합니다. 일반적으로 이런 종류의 통계는 통계 문제라고 생각했습니다.
AnthonyWJones

통계 문제라면 ad-hoc, sp_executesql, exec ()를 실행할 때 QA에서 제대로 작동합니다. 그리고 저장 프로 시저에 ad-hoc sql, sp_executesql, exec ()가 포함 된 경우 왜 모두 제대로 실행되지 않습니까?
Ian Boyd

1

나는 일반적으로 반대하지만 (이 경우 진짜 이유가있는 것처럼 보이지만) SP 버전의 쿼리에 대한 쿼리 힌트를 제공하려고 시도 했습니까? SQL Server가이 두 인스턴스에서 다른 실행 계획을 준비하는 경우 힌트를 사용하여 사용할 인덱스를 알려 계획이 첫 번째 계획과 일치하도록 할 수 있습니까?

몇 가지 예 를 보려면 여기로 이동하십시오 .

편집 : 쿼리 계획을 여기에 게시 할 수 있다면 아마도 계획과의 차이점을 알 수 있습니다.

두 번째 : SQL-2000 전용 링크를 업데이트했습니다. 길을 아래로 스크롤해야하지만 원하는 "테이블 힌트"라는 두 번째 제목이 있습니다.

셋째 : "나쁜"쿼리는 "Openers"테이블의 [IX_Openers_SessionGUID]를 무시하고있는 것 같습니다. 인덱스를 사용하도록 INDEX 힌트를 추가하면 변경 될 수 있습니까?


해당 참조에서 가장 유용한 쿼리 힌트는 여기에서 해당 버전 인 SQL 2000에서 사용할 수 없습니다.
AnthonyWJones

또한 어떤 힌트가 필요합니까? SQL Server는 임시로 실행할 때 문제가 없다는 것을 알 수 있습니다.
Ian Boyd

물론, 그것은 항상 저의 경험이기도합니다. 그러나이 경우 그는 완전히 다른 실행 계획을 내놓고 있다고합니다. 어쩌면 임시로 사용되는 색인이있을 수 있지만 어떤 이유로 proc에서 무시되고 있습니다. SQL Server에서 "INDEX"힌트와 함께 인덱스를 사용하도록 할 수 있습니다.
SqlRyan

1

이것은 아마도 가능성이 낮지 만 관찰 된 행동이 비정상적인 경우 점검해야하며 다른 사람은 언급하지 않았습니다.

당신은 절대적으로 확실 모든 개체 DBO에 의해 소유되는 당신은 자신이나뿐만 아니라 다른 사용자의 존재가 소유 한 불량 사본을 가지고 있지 않은거야?

가끔 이상한 동작을 보았을 때 실제로 두 개의 객체 사본이 있었으며 어떤 것이 지정되었는지와 로그온 한 사람에 따라 달라집니다. 예를 들어, 동일한 이름이지만 다른 소유자가 소유 한보기 또는 프로 시저의 두 사본을 갖는 것이 가능합니다. 데이터베이스에 dbo로 로그온하지 않고 dbo를 오브젝트 소유자로 지정하지 않은 경우에 발생할 수있는 상황 객체를 만듭니다.

텍스트에서 소유자를 지정하지 않고 몇 가지를 실행하고 있음에 유의하십시오.

sp_recompile ViewOpener

예를 들어 dbo와 [일부 다른 사용자]가 소유 한 viewOpener의 두 사본이있는 경우, 지정하지 않은 경우 실제로 다시 컴파일하는 사본은 상황에 따라 다릅니다. Report_Opener보기를 사용하여 Ditto-두 사본 (사양 또는 실행 계획이 다를 수 있음)이있는 경우 상황에 따라 사용되며 소유자를 지정하지 않으면 adhoc 쿼리가 하나를 사용할 수 있습니다. 컴파일 된 프로시 저는 다른 것을 사용할 수 있습니다.

내가 말했듯이, 아마도 가능하지는 않지만 가능한 일이며 확인해야합니다. 문제는 단순히 잘못된 장소에서 버그를 찾는 것일 수 있기 때문입니다.


1

이것은 바보처럼 들릴 수 있으며 SessionGUID라는 이름에서 분명해 보이지만 Report_Opener의 열은 고유 식별자입니까? 그렇지 않은 경우 올바른 유형으로 캐스트하고 샷을 제공하거나 변수를 올바른 유형으로 선언하려고 할 수 있습니다.

sproc의 일부로 작성된 계획은 직관적이지 않게 작동하고 큰 테이블에서 내부 캐스트를 수행 할 수 있습니다.


그렇지 않습니다. 그러나 varchar열을 nvarchar값 (Eg WHERE CustomerName = N'zrendall') 과 비교하는 where 절에서 성능 문제가 발생했습니다 . 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.