Görünümden * seçeneğini belirleyin 4 dakika sürer


11

Ben bir görünüm karşı bir sorgu çalıştırdığınızda 4 + dakika sürer bir sorun için çalışıyorum. Ancak, ben sorgu bağırsakları çalıştırdığınızda 1 saniye gibi bitiyor.

Hakkında emin değilim tek şey katıldı tablolar her ikisi de geçici tablolar olduğunu.

Özel sorgu planı: https://www.brentozar.com/pastetheplan/?id=BykohB2p4

Sorgu planını görüntüleyin: https://www.brentozar.com/pastetheplan/?id=SkIfTHh6E

Nerede denemek ve çözmek için herhangi bir öneriniz var mı?

Kodu görüntüle:

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

Tarafından Bölüm eklendi ve geçici sorguya benzer sonuçlar alın.

Yanıtlar:


18

Ana performans farklılıkları

Buradaki ana farklar, daha iyi performans gösteren sorgunun, CodeMasterIDgörünümdeki seçimin sonuna kadar (filtre operatörü) bunu yapmadığı göründüğü 4 tablodaki (2 geçici tablo (gerçek ve geçmiş)) arama yüklemesini aşağı itmesidir .

TL DR;

Sorun, görünümler gibi bazı durumlarda pencere işlevlerine basmayan parametrelerden kaynaklanmaktadır. En kolay çözüm OPTION(RECOMPILE), bir olasılık varsa, optimize edicinin çalışma zamanında parametreleri görmesini sağlamak için görüntüleme çağrısına eklemektir . Her sorgu çağrısı için yürütme planını yeniden derlemek çok pahalıysa, bir parametre bekleyen satır içi tablo değerli bir işlev kullanmak bir çözüm olabilir. Mükemmel bir var blogpost tarafından Paul Beyaz bu konuda. Sorununuzu bulma ve çözme konusunda daha ayrıntılı bir yol için okumaya devam edin.


Daha iyi performans gösteren sorgu

Codemaster tablosu

resim açıklamasını buraya girin

resim açıklamasını buraya girin

Anlaşma tablosu

resim açıklamasını buraya girin

resim açıklamasını buraya girin

Sabahları arama tahminlerinin kokusunu seviyorum


Büyük Kötü Sorgu

Codemaster tablosu

resim açıklamasını buraya girin

resim açıklamasını buraya girin

Bu tek yüklem bölgesi

Anlaşma tablosu

resim açıklamasını buraya girin

Ancak optimize edici 'Anlaşma sanatı ™'

resim açıklamasını buraya girin

... ve geçmişten ders almıyor

Tüm bu veriler filtre operatörüne ulaşıncaya kadar

resim açıklamasını buraya girin


Peki, ne veriyor?

Buradaki ana sorun, optimize edicinin, görünümdeki pencere işlevleri nedeniyle çalışma zamanında parametreleri 'görmemesi' ve kullanamamasıdır SelOnSeqPrj (sıra projesinde seçin, referans için bu yazıda daha aşağıda) .

Aynı sonuçları bir test örneği ile çoğaltmayı SP_EXECUTESQLve görünüme yapılan çağrıyı parametreleştirmeyi kullanarak başardım . DDL / DML için eke bakın

pencere fonksiyonuna sahip bir test görünümüne karşı bir sorgu yürütme ve INNER JOIN

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

Sonuç olarak 4.5s cpu süresi ve 3.2s geçen süre

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

Tatlı kucağını eklediğimizde 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; 

Her şey yolunda.

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

Neden

Bu @P1, filtre operatörü ile sonuçlanan pencere fonksiyonu ve parametreleştirmesi nedeniyle tabloları yüklemeyi uygulayamama noktasını tekrar desteklemektedir.

resim açıklamasını buraya girin resim açıklamasını buraya girin

Yalnızca geçici tablolar için bir sorun değil

Zeyilname 2'ye bakınız

Geçici tablolar kullanılmasa bile, bu olur: resim açıklamasını buraya girin

Sorgu şöyle yazılırken aynı sonuç görülür:

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

Yine, optimize edici pencere işlevini uygulamadan önce yüklemi aşağıya itmiyor.

ROW_NUMBER () parametresini atlarken

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;

Herşey iyi

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.

Peki bütün bunlar bizi nereye bırakıyor?

ROW_NUMBER()Filtre kötü sorgulara uygulanmadan önce hesaplanır.

Ve tüm bunlar bizi Paul White tarafından pencere fonksiyonları ve görünümleri üzerine 2013'ten bu blog yayınına götürüyor .

Örneğimiz için önemli kısımlardan biri bu ifadedir:

Ne yazık ki, SelOnSeqPrj sadeleştirme kuralı yalnızca yüklem, bir sabitle karşılaştırma yaptığında çalışır. Bu nedenle, aşağıdaki sorgu SQL Server 2008 ve sonraki sürümlerde en uygun olmayan planı üretir:

DECLARE @ProductID INT = 878;

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

resim açıklamasını buraya girin

Bu bölüm, parametreyi kendimiz beyan ederken / SP_EXECUTESQLgörünümde kullanırken gördüklerimize karşılık gelir .


Gerçek çözümler

1: SEÇENEK (TAVSİYE)

Çalışma OPTION(RECOMPILE)zamanında değeri 'görmek' için bir olasılık olduğunu biliyoruz . Her sorgu çağrısı için yürütme planını yeniden derlemek çok pahalı olduğunda, başka çözümler de vardır.

2: Bir parametre ile satır içi tablo değerli işlev

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

Beklenen arama sonuçlarının ortaya çıkması

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

Testimde yaklaşık 9 mantıksal okuma ile

3: Bir görünüm kullanmadan sorguyu yazma.

Diğer 'çözüm', bir görünüm kullanmadan sorguyu tamamen yazmak olabilir.

4: ROW_NUMBER()İşlevi görünümde tutmamak, bunun yerine görünüm çağrısında belirtmek.

Buna bir örnek:

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;

Bu konuyla ilgili başka yaratıcı yollar olmalı, önemli olan buna neyin sebep olduğunu bilmektir.


Zeyil # 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

Zeyilname # 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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.