Paralel yürütme için Skaler işlevi TVF işlevine dönüştürdü - Seri modda çalışıyor


10

Benim sorguda bir sürümden sonra seri yürütme modunda çalışıyordu ve uygulamadan oluşturulan SQL Query LINQ başvurulan bir görünümde iki yeni işlev kullanıldığını fark ettim. Bu yüzden SCALAR işlevlerini TVF işlevlerine dönüştürdüm, ancak yine de sorgu seri modda çalışıyor.

Daha önce bazı diğer sorgularda Skaler TVF dönüşüm yaptım ve zorla seri yürütme sorunu çözüldü.

İşte skaler fonksiyon:

CREATE FUNCTION [dbo].[FindEventReviewDueDate]
(
       @EventNumber VARCHAR(20),
       @EventID VARCHAR(25),
          @EventIDDate BIT
)

RETURNS DateTime
AS
BEGIN

DECLARE @CurrentEventStatus VARCHAR(20)
DECLARE @EventDateTime DateTime
DECLARE @ReviewDueDate DateTime


SELECT @CurrentEventStatus = (SELECT cis.EventStatus
                                 FROM CurrentEventStatus cis 
                                 INNER JOIN Event1 r WITH (NOLOCK) ON (cis.Event1Id = r.Id)
                                 WHERE (r.EventNumber = @EventNumber) AND r.EventID = @EventID)

SELECT @EventDateTime = (SELECT EventDateTime FROM Event1 r 
                          WHERE (r.EventNumber = @EventNumber) AND r.EventID = @EventID)

IF @CurrentEventStatus IN ('0','6') AND EventIDDate = 1
BEGIN

       SET @ReviewDueDate = DATEADD(DAY, 30, @EventDateTime)

       WHILE @ReviewDueDate < getdate() 
             SET @ReviewDueDate = DATEADD(DAY, 30, @ReviewDueDate)

       DECLARE @EventDateJournalDate DateTime

       SELECT @EventDateJournalDate = (SELECT TOP 1 ij.Date
                                       FROM EventPage_EventJournal ij 
                                       INNER JOIN EventJournalPages p ON ij.PageId = p.Id 
                                       INNER JOIN Journal f ON p.FormId = f.Id 
                                       INNER JOIN Event1 r WITH (NOLOCK) ON (f.Event1Id = r.Id)
                                       WHERE (r.EventNumber = @EventNumber AND r.EventID = @EventID) AND ij.ReviewType = 'Supervisor Monthly Review' ORDER BY ij.Date DESC)

      IF(DATEADD(DAY, 30, @EventDateTime) < getdate() AND
           (@EventDateJournalDate is null OR DATEADD(DAY, 30, @EventDateJournalDate) < getdate()) AND
              DATEADD(DAY, 14, @ReviewDueDate) > DATEADD(DAY, 30, getdate()))
                  SET @ReviewDueDate = DATEADD(DAY, -30, @ReviewDueDate)
         ELSE IF((@EventDateJournalDate is not null ) AND (DATEADD(DAY, 30, @EventDateJournalDate) >= @ReviewDueDate))
                  SET @ReviewDueDate = DATEADD(DAY, 30, @ReviewDueDate)

END
RETURN @ReviewDueDate

END

İşte dönüştürülmüş TVF işlevi.

CREATE FUNCTION [dbo].[FindEventReviewDueDate_test]
(
       @EventNumber VARCHAR(20),
       @EventID VARCHAR(25),
          @EventIDDate BIT
)

RETURNS @FunctionResultTableVairable TABLE (
 CurrentEventStatus varchar(20),
 Event1DateTime DateTime,
 ReviewDueDate DateTime
 )
AS 
BEGIN

DECLARE @CurrentEventStatus VARCHAR(20)
DECLARE @EventDateTime DateTime
DECLARE @ReviewDueDate DateTime


SELECT @CurrentEventStatus = (SELECT cis.EventStatus
                                 FROM CurrentEventStatus cis 
                                 INNER JOIN Event1 r WITH (NOLOCK) ON (cis.Event1Id = r.Id)
                                 WHERE (r.EventNumber = @EventNumber) AND r.EventID = @EventID)

SELECT @EventDateTime = (SELECT EventDateTime FROM Event1 r 
                          WHERE (r.EventNumber = @EventNumber) AND r.EventID = @EventID)

IF @CurrentEventStatus IN ('0','6') AND EventIDDate = 1
BEGIN

       SET @ReviewDueDate = DATEADD(DAY, 30, @EventDateTime)

       WHILE @ReviewDueDate < getdate() 
             SET @ReviewDueDate = DATEADD(DAY, 30, @ReviewDueDate)

       DECLARE @EventDateJournalDate DateTime

       SELECT @EventDateJournalDate = (SELECT TOP 1 ij.Date
                                       FROM EventPage_EventJournal ij 
                                       INNER JOIN EventJournalPages p ON ij.PageId = p.Id 
                                       INNER JOIN Journal f ON p.FormId = f.Id 
                                       INNER JOIN Event1 r WITH (NOLOCK) ON (f.Event1Id = r.Id)
                                       WHERE (r.EventNumber = @EventNumber AND r.EventID = @EventID) AND ij.ReviewType = 'Supervisor Monthly Review' ORDER BY ij.Date DESC)

      IF(DATEADD(DAY, 30, @EventDateTime) < getdate() AND
           (@EventDateJournalDate is null OR DATEADD(DAY, 30, @EventDateJournalDate) < getdate()) AND
              DATEADD(DAY, 14, @ReviewDueDate) > DATEADD(DAY, 30, getdate()))
                  SET @ReviewDueDate = DATEADD(DAY, -30, @ReviewDueDate)
         ELSE IF((@EventDateJournalDate is not null ) AND (DATEADD(DAY, 30, @EventDateJournalDate) >= @ReviewDueDate))
                  SET @ReviewDueDate = DATEADD(DAY, 30, @ReviewDueDate)
                   insert into @FunctionResultTableVairable
      select @CurrentEventStatus,@EventDateTime,@ReviewDueDate          

END
return;
END

GO

Sorgunun paralel modda çalışmasını engelleyen TVF işlevi uygulamamla ilgili yanlış bir şey var mı?

Aşağıdaki sorguda TVF işlevini kullanın;

select ReviewDueDate from dbo.FunctionResultTableVairable('a','b','c')

Görünümü kullanan gerçek sorgu oldukça karmaşık ve görünümünde ve yürütme işlev parçası yorum yaparsanız, sorgu paralel olarak çalışır.Bu yüzden sorguyu paralel çalışmaya zorlayan bir işlevdir.

Asıl sorgum aşağıdaki formatta.

select 
dv.column1,
dv.column2,
---------
---------
--------
(select ReviewDueDate from dbo.FunctionResultTableVairable('a','b','c')) AS 'Columnx'
from
DemoView dv
Where 
condition1
conditon 2

Herhangi bir yardım takdir.


3
Sorgu planı ne diyor?
David Browne - Microsoft

2
TVF'niz dış sorgudaki her satır için aynı satırı döndürürse (yalnızca sabitleri aldığı için) bir satır içi TVF ile çok ifadeli TVF arasında büyük bir fark olmasının yanı sıra, yalnızca bir çıkış sütununa önem veriyorsunuz , neden seçim listesindeki bir alt sorguya koyuyorsunuz? Bu sebepsiz yere tekrar tekrar çalıştırmayı mümkün kılar. Çıkışı bir değişkene atayın ve ardından sorgunuzda değişkeni kullanın.
Aaron Bertrand

Yanıtlar:


5

skaler fonksiyonumu Inline TVF'ye dönüştürmek mümkün mü?

Evet. Aşağıdaki gibi bir şey bunu yapardı.

Hâlâ oldukça ağırdır ve koşula bağlıysa muhtemelen oldukça verimsiz olacaktır. Aaron'un yorumlarda belirttiği gibi, bunu sabit değerlerle çağırıyorsunuz, ancak umarım sorgu planı bunu yansıtır ve yalnızca bir kez çalıştırır.

CREATE FUNCTION [dbo].[FindEventReviewDueDateInline] (@EventNumber VARCHAR(20),
                                                      @EventID     VARCHAR(25),
                                                      @EventIDDate BIT)
RETURNS TABLE
AS
    RETURN
      WITH X
           AS (SELECT cis.EventStatus AS CurrentEventStatus,
                      r.EventDateTime
               FROM   CurrentEventStatus cis
                      INNER JOIN Event1 r
                              ON cis.Event1Id = r.Id
               WHERE  r.EventNumber = @EventNumber
                      AND r.EventID = @EventID
                      AND cis.EventStatus IN ( '0', '6' )
                      AND @EventIDDate = 1)
      SELECT X.CurrentEventStatus,
             X.EventDateTime,
             CA4.ReviewDueDate
      FROM   X
             --SET @ReviewDueDate = DATEADD(DAY, 30, @EventDateTime)
             CROSS APPLY(VALUES(DATEADD(DAY, 30, X.EventDateTime))) CA1(ReviewDueDate)
             -- WHILE @ReviewDueDate < getdate() 
             --       SET @ReviewDueDate = DATEADD(DAY, 30, @ReviewDueDate)
             CROSS APPLY(VALUES( IIF(CA1.ReviewDueDate >= GETDATE(), CA1.ReviewDueDate, DATEADD(DAY, 30 * CEILING(( IIF(CAST(GETDATE() AS TIME) > CAST(CA1.ReviewDueDate AS TIME), 1, 0)
                                                                                                           + DATEDIFF(DAY, CA1.ReviewDueDate, GETDATE()) ) / 30.0), CA1.ReviewDueDate)))) CA2(ReviewDueDate)
             --SELECT @EventDateJournalDate = ....
             CROSS APPLY(SELECT TOP 1 ij.Date
                         FROM   EventPage_EventJournal ij
                                INNER JOIN EventJournalPages p
                                        ON ij.PageId = p.Id
                                INNER JOIN Journal f
                                        ON p.FormId = f.Id
                                INNER JOIN Event1 r WITH (NOLOCK)
                                        ON ( f.Event1Id = r.Id )
                         WHERE  ( r.EventNumber = @EventNumber
                                  AND r.EventID = @EventID )
                                AND ij.ReviewType = 'Supervisor Monthly Review'
                         ORDER  BY ij.Date DESC) CA3(EventDateJournalDate)
             -- IF(DATEADD(DAY, 30, @EventDateTime) < getdate()
             CROSS APPLY(VALUES ( CASE
                          WHEN ( DATEADD(DAY, 30, X.EventDateTime) < GETDATE()
                                 AND ( CA3.EventDateJournalDate IS NULL
                                        OR DATEADD(DAY, 30, CA3.EventDateJournalDate) < GETDATE() )
                                 AND DATEADD(DAY, 14, CA2.ReviewDueDate) > DATEADD(DAY, 30, GETDATE()) )
                            THEN DATEADD(DAY, -30, CA2.ReviewDueDate)
                          WHEN( ( CA3.EventDateJournalDate IS NOT NULL )
                                AND ( DATEADD(DAY, 30, CA3.EventDateJournalDate) >= CA2.ReviewDueDate ) )
                            THEN DATEADD(DAY, 30, CA2.ReviewDueDate)
                          ELSE CA2.ReviewDueDate
                        END )) CA4(ReviewDueDate); 

11

Forrest çoğunlukla doğrudur, ancak daha ince detaylar:

SQL Server, işlevinizin kullandığı tablo değişkenlerindeki değişiklikleri paralelleştiremez.

SQL Server 2017'nin Aralıklı Yürütülmesinden önce Çoklu Tablo Tablosu Değerli İşlevlerinden satır tahminleri çok düşüktü.

Bunun bir yan etkisi, planların düşük uçta çok düşük maliyetli olması ve genellikle paralellik için maliyet eşiğini kırmamasıdır.


1
Ana sorgumun paralel modda çalışabilmesi için alternatif bir çözüm önerebilir misiniz? Skaler funciton'ma baktığımda skaler fonksiyonumu Inline TVF'ye dönüştürmek mümkün mü?
user9516827

1
@ user9516827 Muhtemelen benzer bir şey yapmak için bazı CTE'leri birbirine bağlayabilirsiniz, ancak paralel gidip daha iyi performans göstermesi gibi bir fikrim yok. Test etmek size kalmış.
Erik Darling

@MartinSmith: Gerçek sorgum, birleştirme ve görünümlere katılma vb. İle çok karmaşık bir sorgu. Bu işlev, ana sorguda bir seçim sütunu için kullanılır ve bu yüzden bu sabit almaya çalışıyordum. -SQL (exec sp_executesql formu) sorgu oluşturulan ve i .net
kodu

10

SQL Server, çok ifadeli TVF'leri paralel hale getiremez. Yalnızca Inline TVF'ler paralelleştirilebilir.


1
Orada değişkenler var çünkü ben skaler fonksiyonu Inline TVF dönüştürmek mümkün değil mi?
user9516827
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.