주요 성능 차이
여기서 주요 차이점은 쿼리 성능이 향상 CodeMasterID되면 뷰에서 선택이 끝날 때까지 (필터 연산자) 보이지 않는 4 개의 모든 테이블 (2 개의 임시 테이블 (실제 및 히스토리))에서 탐색 술어를 푸시 다운 한다는 것입니다 .
TL DR;
문제는 뷰와 같은 특정 경우에 매개 변수가 창 기능으로 아래로 밀리지 않기 때문입니다. 가장 쉬운 해결책은 OPTION(RECOMPILE)옵티마이 저가 가능한 경우 런타임에 매개 변수를 '볼'수 있도록 뷰 호출에 추가 하는 것입니다. 각 쿼리 호출에 대한 실행 계획을 다시 컴파일하기에 너무 비싸면 매개 변수가 솔루션이 될 수있는 인라인 테이블 값 함수를 사용하십시오. 훌륭한이 블로그 게시물 에 의해 폴 화이트 이것에가. 특정 문제를 찾아서 해결하는 자세한 방법은 계속 읽으십시오.
실적이 우수한 검색어
코드 마스터 테이블


거래 테이블


아침에 술어 찾기 냄새가 좋아
큰 나쁜 질문
코드 마스터 테이블


이것은 술어 전용 영역입니다
거래 테이블

그러나 옵티마이 저는 'The art of the deal ™'을 읽지 않았습니다.

... 과거에서 배우지 않는다
모든 데이터가 필터 연산자에 도달 할 때까지

그래서 무엇을 제공합니까?
여기서 주요 문제는 뷰의 창 기능으로 인해 런타임에 매개 변수를 '보지'않는 옵티 마이저입니다 SelOnSeqPrj (시퀀스 프로젝트에서 선택하십시오 .
테스트 샘플을 사용 SP_EXECUTESQL하여 뷰에 대한 호출을 매개 변수화하는 데 동일한 결과를 복제 할 수있었습니다 . DDL / DML에 대한 부록 참조
윈도우 함수와 테스트 뷰로 테스트 뷰에 대한 쿼리 실행 INNER JOIN
SET STATISTICS IO, TIME ON;
EXEC SP_EXECUTESQL
N'SELECT * FROM dbo.Bad
Where CodeMasterID = @P1',N'@P1 INT',@P1 = 37155;
약 4.5 초의 CPU 시간과 3.2 초의 경과 시간
SQL Server Execution Times:
CPU time = 4595 ms, elapsed time = 3209 ms.
우리가 달콤한 포옹을 추가하면 OPTION(RECOMPILE)
SET STATISTICS IO, TIME ON;
EXEC SP_EXECUTESQL
N'SELECT * FROM dbo.Bad
Where CodeMasterID = @P1 OPTION(RECOMPILE)',N'@P1 INT',@P1 = 37155;
모두 좋다.
SQL Server Execution Times:
CPU time = 16 ms, elapsed time = 98 ms.
왜
이것은 @P1필터 함수를 초래하는 창 함수 및 매개 변수화로 인해 테이블에 술어를 적용 할 수 없다는 점을 다시 지원합니다.

임시 테이블의 문제뿐만 아니라
부록 2 참조
임시 테이블을 사용하지 않는 경우에도 다음이 발생합니다.

다음과 같이 쿼리를 작성할 때도 동일한 결과가 나타납니다.
DECLARE @P1 int = 37155
SELECT * FROM dbo.Bad2
Where CodeMasterID = @P1;
다시, 옵티마이 저는 창 기능을 적용하기 전에 술어를 내리지 않습니다.
ROW_NUMBER ()를 생략 할 때
CREATE VIEW dbo.Bad3
as
SELECT
cm.CodeMasterID,CM.ManagerID,cm.ParentDeptID,d.DealID, d.CodeMasterID as dealcodemaster,d.EvenMoreBlaID
FROM dbo.CodeMaster2 cm
INNER JOIN dbo.Deal2 d ON cm.CodeMasterID = d.CodeMasterID;
다 잘
SET STATISTICS IO, TIME ON;
EXEC SP_EXECUTESQL
N'SELECT * FROM dbo.Bad3
Where CodeMasterID = @P1',N'@P1 INT',@P1 = 37155
SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 33 ms.
그래서 우리를 떠나는 모든 것이 어디입니까?
는 ROW_NUMBER()필터가 나쁜 쿼리에 적용되기 전에 계산됩니다.
이 모든 것이
창 기능과 뷰에 관한 Paul White의 2013 년 블로그 포스트로 연결 됩니다.
이 예에서 중요한 부분 중 하나는 다음과 같습니다.
불행하게도 SelOnSeqPrj 단순화 규칙은 술어가 상수와 비교를 수행 할 때만 작동합니다. 이러한 이유로 다음 쿼리는 SQL Server 2008 이상에서 차선책을 생성합니다.
DECLARE @ProductID INT = 878;
SELECT
mrt.ProductID,
mrt.TransactionID,
mrt.ReferenceOrderID,
mrt.TransactionDate,
mrt.Quantity
FROM dbo.MostRecentTransactionsPerProduct AS mrt
WHERE
mrt.ProductID = @ProductID;

이 부분은 매개 변수를 선언 SP_EXECUTESQL하거나 뷰에서 사용할 때 본 내용 에 해당합니다.
실제 솔루션
1 : 옵션 (권장)
우리 OPTION(RECOMPILE)는 런타임에 가치를 '볼'가능성이 있음 을 알고 있습니다. 각 쿼리 호출에 대한 실행 계획을 다시 컴파일하는 것이 너무 비싸면 다른 솔루션이 있습니다.
2 : 매개 변수가있는 인라인 테이블 값 함수
CREATE FUNCTION dbo.BlaBla
(
@P1 INT
)
RETURNS TABLE
WITH SCHEMABINDING AS
RETURN
(
SELECT
ROW_NUMBER() OVER (PARTITION BY cm.CodeMasterID ORDER BY cm.CodeMasterID) AS Deal_HistoryID,
cm.CodeMasterID,CM.ManagerID,
cm.ParentDeptID,d.DealID,
d.CodeMasterID as dealcodemaster,
d.EvenMoreBlaID
FROM dbo.CodeMaster2 cm
INNER JOIN dbo.Deal2 d ON cm.CodeMasterID = d.CodeMasterID
Where cm.CodeMasterID = @P1
)
EXEC SP_EXECUTESQL
N'SELECT * FROM dbo.BlaBLa(@P1)',N'@P1 INT',@P1 = 37155
예상 탐색 술어 결과
SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 0 ms.
내 테스트에서 약 9 개의 논리적 읽기로
3 :보기를 사용하지 않고 쿼리 작성
다른 '솔루션'은 뷰를 사용하지 않고 쿼리를 완전히 작성할 수 있습니다.
4 : ROW_NUMBER()뷰 에서 함수를 유지하지 않고 뷰 호출에서 함수를 지정하십시오.
이에 대한 예는 다음과 같습니다.
CREATE VIEW dbo.Bad2
as
SELECT
cm.CodeMasterID,CM.ManagerID,cm.ParentDeptID,d.DealID, d.CodeMasterID as dealcodemaster,d.EvenMoreBlaID
FROM dbo.CodeMaster2 cm
INNER JOIN dbo.Deal2 d ON cm.CodeMasterID = d.CodeMasterID;
GO
SET STATISTICS IO, TIME ON;
EXEC SP_EXECUTESQL
N'SELECT ROW_NUMBER() OVER (PARTITION BY CodeMasterID ORDER BY CodeMasterID) AS Deal_HistoryID,* FROM dbo.Bad2
Where CodeMasterID = @P1',N'@P1 INT',@P1 = 37155;
이 문제와 관련하여 다른 창의적인 방법이 있어야합니다. 중요한 부분은 문제의 원인을 아는 것입니다.
부록 # 1
CREATE TABLE dbo.Codemaster
(
CodeMasterID int NOT NULL PRIMARY KEY CLUSTERED
, ManagerID INT NULL
, ParentDeptID int NULL
, SysStartTime datetime2 GENERATED ALWAYS AS ROW START NOT NULL
, SysEndTime datetime2 GENERATED ALWAYS AS ROW END NOT NULL
, PERIOD FOR SYSTEM_TIME (SysStartTime,SysEndTime)
)
WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.Codemaster_History))
;
CREATE TABLE dbo.Deal
(
DealID int NOT NULL PRIMARY KEY CLUSTERED
, CodeMasterID INT NULL
, EvenMoreBlaID int NULL
, SysStartTime datetime2 GENERATED ALWAYS AS ROW START NOT NULL
, SysEndTime datetime2 GENERATED ALWAYS AS ROW END NOT NULL
, PERIOD FOR SYSTEM_TIME (SysStartTime,SysEndTime)
)
WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.Deal_History))
;
INSERT INTO dbo.Codemaster(CodeMasterID,ManagerID,ParentDeptID)
SELECT TOP(1000000) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum1,
ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum2,
ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum3
FROM MASTER..spt_values as spt1
CROSS JOIN MASTER..spt_values as spt2;
INSERT INTO dbo.Deal(DealID,CodeMasterID,EvenMoreBlaID)
SELECT TOP(1000000) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum1,
ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum2,
ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum3
FROM MASTER..spt_values as spt1
CROSS JOIN MASTER..spt_values as spt2;
CREATE INDEX IX_CodeMasterID
ON dbo.Deal(CodeMasterId);
CREATE INDEX IX_CodeMasterID
ON dbo.Deal_History(CodeMasterId);
CREATE INDEX IX_CodeMasterID
ON dbo.Codemaster(CodeMasterId);
CREATE INDEX IX_CodeMasterID
ON dbo.Codemaster_History(CodeMasterId);
SELECT ROW_NUMBER() OVER (PARTITION BY cm.CodeMasterID ORDER BY cm.CodeMasterID, cm.SysStartTime) AS Deal_HistoryID,
cm.*, d.*
FROM dbo.CodeMaster FOR SYSTEM_TIME ALL cm
INNER JOIN dbo.Deal FOR SYSTEM_TIME ALL d ON cm.CodeMasterID = d.CodeMasterID
Where cm.CodeMasterID = 37155;
-- Guud
GO
CREATE VIEW dbo.Bad
as
SELECT ROW_NUMBER() OVER (PARTITION BY cm.CodeMasterID ORDER BY cm.CodeMasterID, cm.SysStartTime) AS Deal_HistoryID,
cm.CodeMasterID,CM.ManagerID,cm.ParentDeptID,d.DealID, d.CodeMasterID as dealcodemaster,d.EvenMoreBlaID
FROM dbo.CodeMaster FOR SYSTEM_TIME ALL cm
INNER JOIN dbo.Deal FOR SYSTEM_TIME ALL d ON cm.CodeMasterID = d.CodeMasterID
GO
EXEC SP_EXECUTESQL
N'SELECT * FROM dbo.Bad
Where CodeMasterID = @P1',N'@P1 INT',@P1 = 37155
-- Very bad shame on you
부록 # 2
CREATE TABLE dbo.Codemaster2
(
CodeMasterID int NOT NULL PRIMARY KEY CLUSTERED
, ManagerID INT NULL
, ParentDeptID int NULL
);
CREATE TABLE dbo.Deal2
(
DealID int NOT NULL PRIMARY KEY CLUSTERED
, CodeMasterID INT NULL
, EvenMoreBlaID int NULL
);
INSERT INTO dbo.Codemaster2(CodeMasterID,ManagerID,ParentDeptID)
SELECT TOP(1000000) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum1,
ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum2,
ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum3
FROM MASTER..spt_values as spt1
CROSS JOIN MASTER..spt_values as spt2;
INSERT INTO dbo.Deal2(DealID,CodeMasterID,EvenMoreBlaID)
SELECT TOP(1000000) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum1,
ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum2,
ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum3
FROM MASTER..spt_values as spt1
CROSS JOIN MASTER..spt_values as spt2;
CREATE INDEX IX_CodeMasterID
ON dbo.Deal2(CodeMasterId);
CREATE INDEX IX_CodeMasterID
ON dbo.Codemaster2(CodeMasterId);
SELECT ROW_NUMBER() OVER (PARTITION BY cm.CodeMasterID ORDER BY cm.CodeMasterId) AS Deal_HistoryID,
cm.*, d.*
FROM dbo.CodeMaster2 cm
INNER JOIN dbo.Deal2 d ON cm.CodeMasterID = d.CodeMasterID
Where cm.CodeMasterID = 37155;
-- Guud
GO
CREATE VIEW dbo.Bad2
as
SELECT ROW_NUMBER() OVER (PARTITION BY cm.CodeMasterID ORDER BY cm.CodeMasterID) AS Deal_HistoryID,
cm.CodeMasterID,CM.ManagerID,cm.ParentDeptID,d.DealID, d.CodeMasterID as dealcodemaster,d.EvenMoreBlaID
FROM dbo.CodeMaster2 cm
INNER JOIN dbo.Deal2 d ON cm.CodeMasterID = d.CodeMasterID
GO
SET STATISTICS IO, TIME ON;
EXEC SP_EXECUTESQL
N'SELECT * FROM dbo.Bad2
Where CodeMasterID = @P1',N'@P1 INT',@P1 = 37155