Chọn * từ Xem mất 4 phút


11

Tôi đang gặp phải một vấn đề khi tôi chạy truy vấn đối với chế độ xem mất hơn 4 phút. Tuy nhiên, khi tôi chạy hết can đảm của truy vấn thì nó kết thúc sau 1 giây.

Điều duy nhất tôi không chắc chắn là các bảng được nối là cả hai bảng tạm thời.

Gói truy vấn đặc biệt: https://www.brentozar.com/pastetheplan/?id=BykohB2p4

Xem gói truy vấn: https://www.brentozar.com/pastetheplan/?id=Sk IfTHh6E

Bất kỳ đề xuất về nơi để thử và tìm ra điều này?

Xem mã:

ALTER VIEW [dbo].[vwDealHistoryPITA]
AS
SELECT ROW_NUMBER() OVER (PARTITION BY cm.CodeMasterID ORDER BY cm.CodeMasterID, cm.LastUpdateDate) AS Deal_HistoryID,
       cm.CodeMasterID,
       cm.ProjectName,
       cm.[Status],
       d.CompanyID,
       d.DealTypeMasterID,
       cm.[Description],
       d.PassiveInd,
       d.ApproxTPGOwnership,
       d.NumberBoardSeats,
       d.FollowonInvestmentInd,
       d.SocialImpactInd,
       d.EquityInd,
       d.DebtInd,
       d.RealEstateInd,
       d.TargetPctgReturn,
       d.ApproxTotalDealSize,
       cm.CurrencyCode,
       d.ConflictCheck,
       cm.CreatedDate,
       cm.CreatedBy,
       cm.LastUpdateDate,
       cm.LastUpdateBy,
       d.ExpensesExceedThresholdDate,
       d.CurrentTPGCheckSize,
       d.PreferredEquityInd,
       d.ConvertibleDebtInd,
       d.OtherRealAssetsInd,
       d.InitialTPGCheckSize,
       d.DirectLendingInd,
       cm.NameApproved,
       cm.FolderID,
       cm.CodaProcessedDateTime,
       cm.DeadDate,
       d.SectorMasterID,
       d.DTODataCompleteDate,
       cm.ValidFrom AS CodeMasterValidFrom,
       cm.ValidTo   AS CodeMasterValidTo,
       d.validFrom  AS DealValidFrom,
       d.validTo    AS DealValidTo
FROM dbo.CodeMaster FOR SYSTEM_TIME ALL cm 
INNER JOIN dbo.Deal FOR SYSTEM_TIME ALL d ON cm.CodeMasterID = d.CodeMasterID;
GO

Đã thêm Phân vùng theo và nhận kết quả tương tự với truy vấn ad hoc.

Câu trả lời:


18

Sự khác biệt hiệu suất chính

Sự khác biệt chính ở đây là truy vấn hoạt động tốt hơn đang đẩy xuống vị từ tìm kiếm CodeMasterIDtrên tất cả 4 bảng (2 bảng tạm thời (thực tế & lịch sử)) trong đó lựa chọn trên chế độ xem dường như không làm điều đó cho đến khi kết thúc (toán tử bộ lọc) .

TL DR;

Vấn đề là do các tham số không được đẩy xuống các chức năng của cửa sổ trong một số trường hợp nhất định như chế độ xem. Giải pháp đơn giản nhất là thêm OPTION(RECOMPILE)vào lệnh gọi xem để làm cho trình tối ưu hóa 'xem' các thông số trong thời gian chạy nếu đó là một khả năng. Nếu quá tốn kém để biên dịch lại kế hoạch thực hiện cho mỗi cuộc gọi truy vấn, sử dụng hàm có giá trị bảng nội tuyến dự kiến ​​tham số có thể là một giải pháp. Có một Blogpost tuyệt vời của Paul White về điều này. Để biết cách chi tiết hơn về việc tìm kiếm và giải quyết vấn đề cụ thể của bạn, hãy tiếp tục đọc.


Truy vấn thực hiện tốt hơn

Bảng Codemaster

nhập mô tả hình ảnh ở đây

nhập mô tả hình ảnh ở đây

Bảng thỏa thuận

nhập mô tả hình ảnh ở đây

nhập mô tả hình ảnh ở đây

Tôi yêu mùi tìm kiếm vị ngữ vào buổi sáng


Truy vấn xấu lớn

Bảng Codemaster

nhập mô tả hình ảnh ở đây

nhập mô tả hình ảnh ở đây

Đây là một khu vực chỉ vị ngữ

Bảng giao dịch

nhập mô tả hình ảnh ở đây

Nhưng trình tối ưu hóa đã không đọc art Nghệ thuật của thỏa thuận ™ "

nhập mô tả hình ảnh ở đây

... và không học hỏi từ quá khứ

Cho đến khi tất cả dữ liệu đó đến được toán tử lọc

nhập mô tả hình ảnh ở đây


Vì vậy, những gì cho?

Vấn đề chính ở đây là trình tối ưu hóa không 'nhìn thấy' các tham số khi chạy do các chức năng của cửa sổ trong chế độ xem và không thể sử dụng SelOnSeqPrj (chọn dự án trình tự, tiếp tục trong bài này để tham khảo) .

Tôi đã có thể sao chép các kết quả tương tự với một mẫu thử nghiệm và sử dụng SP_EXECUTESQLđể tham số hóa cuộc gọi đến chế độ xem. Xem phụ lục cho DDL / DML

thực hiện một truy vấn đối với chế độ xem thử nghiệm với chức năng cửa sổ và INNER JOIN

SET STATISTICS IO, TIME ON;
EXEC SP_EXECUTESQL
N'SELECT * FROM  dbo.Bad
Where CodeMasterID = @P1',N'@P1 INT',@P1 = 37155;

Kết quả là khoảng 4,5 giây thời gian cpu và thời gian trôi qua 3,2 giây

 SQL Server Execution Times:
   CPU time = 4595 ms,  elapsed time = 3209 ms.

Khi chúng ta thêm cái ôm ngọt ngào của 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; 

Tất cả đều tốt.

 SQL Server Execution Times:
   CPU time = 16 ms,  elapsed time = 98 ms.

Tại sao

Tất cả điều này một lần nữa hỗ trợ điểm không thể áp dụng biến @P1vị ngữ cho các bảng do chức năng cửa sổ & tham số hóa dẫn đến toán tử bộ lọc

nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây

Không chỉ là một vấn đề cho các bảng thời gian

Xem phụ lục 2

Ngay cả khi không sử dụng bảng tạm thời, điều này vẫn xảy ra: nhập mô tả hình ảnh ở đây

Kết quả tương tự được nhìn thấy khi viết truy vấn như thế này:

DECLARE @P1 int = 37155
SELECT * FROM  dbo.Bad2
Where CodeMasterID = @P1;

Một lần nữa, trình tối ưu hóa không đẩy xuống vị ngữ trước khi áp dụng chức năng cửa sổ.

Khi bỏ qua 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;

Tất cả đều tốt

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.

Vậy tất cả những gì rời bỏ chúng ta?

Các ROW_NUMBER()được tính toán trước khi bộ lọc được áp dụng trên các truy vấn xấu.

Và tất cả điều này dẫn chúng ta đến blogpost này từ năm 2013 bởi Paul White về các chức năng và khung nhìn của cửa sổ.

Một trong những phần quan trọng cho ví dụ của chúng tôi là tuyên bố này:

Thật không may, quy tắc đơn giản hóa SelOnSeqPrj chỉ hoạt động khi vị từ thực hiện so sánh với hằng số. Vì lý do đó, truy vấn sau đây tạo ra gói tối ưu phụ trên SQL Server 2008 trở lên:

DECLARE @ProductID INT = 878;

SELECT
    mrt.ProductID,
    mrt.TransactionID,
    mrt.ReferenceOrderID,
    mrt.TransactionDate,
    mrt.Quantity
FROM dbo.MostRecentTransactionsPerProduct AS mrt 
WHERE
    mrt.ProductID = @ProductID;

nhập mô tả hình ảnh ở đây

Phần này tương ứng với những gì chúng ta đã thấy khi tự khai báo tham số / sử dụng SP_EXECUTESQLtrên khung nhìn.


Các giải pháp thực tế

1: TÙY CHỌN (KHUYẾN NGHỊ)

Chúng tôi biết rằng OPTION(RECOMPILE)để 'thấy' giá trị trong thời gian chạy là một khả năng. Khi biên dịch lại kế hoạch thực hiện cho mỗi cuộc gọi truy vấn quá tốn kém, có các giải pháp khác.

2: Hàm nội tuyến có giá trị với một tham số

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

Kết quả trong các vị từ tìm kiếm dự kiến

     SQL Server Execution Times:
       CPU time = 0 ms,  elapsed time = 0 ms.

Với khoảng 9 lần đọc logic trong bài kiểm tra của tôi

3: Viết truy vấn mà không sử dụng chế độ xem.

'Giải pháp' khác có thể viết hoàn toàn truy vấn mà không cần sử dụng chế độ xem.

4: Không giữ ROW_NUMBER()chức năng trong chế độ xem, thay vào đó chỉ định chức năng đó trong cuộc gọi đến chế độ xem.

Một ví dụ về điều này sẽ là:

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;

Cần có những cách sáng tạo khác xung quanh vấn đề này, phần quan trọng là biết nguyên nhân gây ra nó.


Phụ lục số 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

Phụ lục số 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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.