Mengkonversi fungsi Skalar ke fungsi TVF untuk eksekusi paralel-Masih berjalan dalam mode Serial

10

Salah satu permintaan saya sedang berjalan dalam mode eksekusi serial setelah rilis dan saya perhatikan bahwa dua fungsi baru digunakan dalam tampilan yang direferensikan dalam LINQ ke SQL Query yang dihasilkan dari aplikasi. Jadi saya mengonversi fungsi-fungsi SCALAR ke fungsi TVF, tetapi masih query berjalan dalam mode serial.

Sebelumnya saya telah melakukan konversi Scalar ke TVF di beberapa pertanyaan lain dan itu memecahkan masalah eksekusi serial paksa.

Inilah fungsi skalar:

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

Berikut adalah fungsi TVF yang dikonversi.

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

Apakah ada yang salah dengan penerapan fungsi TVF yang mencegah kueri berjalan dalam mode paralel.

Saya menggunakan fungsi TVF dalam kueri seperti di bawah ini;

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

Permintaan aktual saya yang menggunakan tampilan cukup rumit dan jika saya mengomentari bagian fungsi dalam tampilan dan mengeksekusi, permintaan berjalan secara paralel. Jadi itu adalah fungsi yang memaksa permintaan untuk berjalan secara paralel.

Permintaan saya yang sebenarnya adalah dalam format di bawah ini.

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

Bantuan apa pun dihargai.

pengguna9516827
sumber
3
Apa yang dikatakan rencana permintaan?
David Browne - Microsoft
2
Selain ada perbedaan besar antara TVF in-line dan TVF multi-pernyataan, jika TVF Anda mengembalikan baris yang sama untuk setiap baris dalam kueri luar (karena hanya membutuhkan konstanta), dan Anda hanya peduli pada satu kolom output , mengapa Anda memasukkannya ke dalam subquery di daftar pilih? Ini hanya memungkinkan untuk mengeksekusi berulang-ulang tanpa alasan. Tetapkan output ke variabel dan kemudian gunakan variabel dalam kueri Anda.
Aaron Bertrand

Jawaban:

5

apakah mungkin untuk mengubah fungsi skalar saya ke Inline TVF?

Iya. Sesuatu seperti di bawah ini akan melakukannya.

Itu masih lumayan dan jika dijalankan berkorelasi kemungkinan akan sangat tidak efisien. Seperti yang ditunjukkan Aaron dalam komentar Anda menyebutnya dengan nilai konstan, jadi semoga saja rencana kueri mencerminkan ini dan hanya menjalankannya sekali.

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); 
Martin Smith
sumber
11

Forrest sebagian besar benar, tetapi detail yang lebih halus adalah:

SQL Server tidak dapat memparalelkan modifikasi ke variabel tabel, yang digunakan fungsi Anda.

Sebelum Eksekusi Interleaved SQL Server 2017 , estimasi baris dari Multi-Statement Table Valued Functions sangat rendah.

Salah satu efek samping dari ini adalah bahwa rencana dibiayai sangat buruk pada kelas bawah, dan seringkali tidak akan melanggar ambang biaya untuk paralelisme.

Erik Darling
sumber
1
OkayCan Anda suggets solusi alternatif sehingga permintaan utama saya dapat berjalan dalam mode paralel. Melihat fungsi skalar saya, apakah mungkin untuk mengubah fungsi skalar saya ke TVF Inline?
user9516827
1
@ user9516827 Anda mungkin dapat menghubungkan beberapa CTE bersama untuk melakukan sesuatu yang serupa, tapi saya tidak tahu apakah itu akan paralel, berkinerja lebih baik, dll. Mengujinya terserah Anda.
Erik Darling
@ MartinSmith: Permintaan saya yang sebenarnya adalah permintaan yang sangat kompleks dengan banyak bergabung dan bergabung dengan tampilan dll. Fungsi ini digunakan untuk kolom pilih dalam permintaan utama dan itulah sebabnya saya mencoba untuk memperbaikinya. Juga ini adalah LINQ -SQL (exec sp_executesql form) menghasilkan permintaan dan saya memiliki batasan untuk tidak meretas kode .net. Terima kasih
user9516827
10

SQL Server tidak dapat memparalelkan TVFs multi-pernyataan, yang merupakan milik Anda. Hanya TVFs sebaris yang dapat diparalelkan.

Forrest
sumber
1
Karena saya memiliki variabel di sana saya tidak dapat mengubah fungsi skalar ke TVF Inline?
user9516827