แปลงฟังก์ชัน Scalar เป็นฟังก์ชัน TVF สำหรับการประมวลผลแบบขนาน - ยังคงทำงานในโหมดอนุกรม


10

หนึ่งในแบบสอบถามของฉันทำงานในโหมดการดำเนินการแบบอนุกรมหลังจากวางจำหน่ายและฉันสังเกตเห็นว่ามีการใช้ฟังก์ชันใหม่สองรายการในมุมมองซึ่งอ้างอิงใน LINQ ไปยัง SQL Query ที่สร้างขึ้นจากแอปพลิเคชัน ดังนั้นฉันจึงแปลงฟังก์ชัน SCALAR เหล่านั้นเป็นฟังก์ชั่น TVF แต่คำถามก็ยังคงทำงานอยู่ในโหมดอนุกรม

ก่อนหน้านี้ฉันได้แปลง Scalar เป็น TVF ในข้อความค้นหาอื่นและแก้ปัญหาการบังคับให้เรียกใช้งานอนุกรม

นี่คือฟังก์ชันสเกลาร์:

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

นี่คือฟังก์ชั่น TVF ที่แปลงแล้ว

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

มีอะไรผิดปกติกับการใช้งานฟังก์ชั่น TVF ของฉันซึ่งป้องกันไม่ให้แบบสอบถามทำงานในโหมดขนาน

ฉันใช้ฟังก์ชัน TVF ในแบบสอบถามดังต่อไปนี้

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

แบบสอบถามจริงของฉันที่ใช้มุมมองนั้นค่อนข้างซับซ้อนและถ้าฉันใส่ความคิดเห็นส่วนของฟังก์ชั่นในมุมมองและในการดำเนินการแบบสอบถามทำงานในแบบขนานดังนั้นมันเป็นฟังก์ชั่นที่บังคับให้แบบสอบถามทำงานในแบบขนาน

ข้อความค้นหาจริงของฉันอยู่ในรูปแบบด้านล่าง

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

ความช่วยเหลือใด ๆ ที่ชื่นชม


3
แผนแบบสอบถามพูดว่าอย่างไร
David Browne - Microsoft

2
นอกเหนือจากความแตกต่างอย่างมากระหว่าง TVF แบบอินไลน์และหลายคำสั่ง TVF หาก TVF ของคุณส่งคืนแถวเดียวกันสำหรับทุกแถวในเคียวรีด้านนอก (เนื่องจากใช้ค่าคงที่เท่านั้น) และคุณสนใจคอลัมน์เอาต์พุตเพียงคอลัมน์เดียว ทำไมคุณวางไว้ในแบบสอบถามย่อยในรายการที่เลือก? สิ่งนี้ทำให้สามารถดำเนินการซ้ำแล้วซ้ำอีกได้โดยไม่มีเหตุผล กำหนดเอาต์พุตให้กับตัวแปรจากนั้นใช้ตัวแปรในเคียวรีของคุณ
Aaron Bertrand

คำตอบ:


5

เป็นไปได้ไหมที่จะแปลงฟังก์ชันสเกลาร์ของฉันเป็น Inline TVF

ใช่. สิ่งที่ต้องการด้านล่างจะทำ

มันก็ยังค่อนข้างแข็งแรงและถ้าการวิ่งที่มีความสัมพันธ์กันน่าจะค่อนข้างไม่มีประสิทธิภาพ แอรอนชี้ให้เห็นในความคิดเห็นที่คุณเรียกใช้ด้วยค่าคงที่ แต่หวังว่าแผนแบบสอบถามจะแสดงสิ่งนี้และเรียกใช้เพียงครั้งเดียว

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

ฟอร์เรสต์นั้นถูกต้องส่วนใหญ่แต่รายละเอียดที่ละเอียดกว่าคือ:

SQL Server ไม่สามารถทำการปรับเปลี่ยนตัวแปรตารางได้อย่างขนานซึ่งฟังก์ชันของคุณใช้

ก่อนการเรียกใช้Interleaved Executionของ SQL Server 2017 การประมาณแถวจากฟังก์ชัน Multi-Statement Table ที่ประเมินค่าได้ต่ำมาก

ผลข้างเคียงหนึ่งของสิ่งนี้คือแผนมีราคาต่ำมากในตอนท้ายและมักจะไม่ทำลายเกณฑ์ต้นทุนสำหรับการขนาน


1
Okay คุณสามารถแนะนำวิธีแก้ปัญหาอื่น ๆ เพื่อให้คิวรีหลักของฉันสามารถทำงานในโหมดคู่ขนานได้หรือไม่เมื่อดูที่ scalar funciton ฉันสามารถแปลงฟังก์ชัน scalar ของฉันเป็น Inline TVF ได้หรือไม่
user9516827

1
@ user9516827 คุณอาจเชื่อมโยง CTE บางอย่างเข้าด้วยกันเพื่อทำสิ่งที่คล้ายกัน แต่ฉันไม่รู้ว่ามันจะขนานกันทำงานได้ดีขึ้นหรือไม่ ฯลฯ การทดสอบมันขึ้นอยู่กับคุณ
Erik ที่รัก

@MartinSmith: แบบสอบถามจริงของฉันเป็นแบบสอบถามที่ซับซ้อนมากที่มีจำนวนมากเข้าร่วมและเชื่อมต่อกับมุมมอง ฯลฯ ฟังก์ชั่นนี้ใช้สำหรับคอลัมน์เลือกในแบบสอบถามหลักและนั่นคือเหตุผลที่ฉันพยายามรับการแก้ไขยังนี่คือ LINQ -SQL (รูปแบบ exec sp_executesql) สร้างแบบสอบถามและฉันมีข้อ จำกัด ของการไม่แฮ็ครหัส. net ขอบคุณ
user9516827

10

SQL Server ไม่สามารถขนาน TVF หลายงบซึ่งเป็นของคุณได้ เฉพาะ Inline TVFs เท่านั้นที่สามารถทำการขนานกันได้


1
เนื่องจากฉันมีตัวแปรจึงไม่สามารถแปลงฟังก์ชันสเกลาร์เป็น Inline TVF ได้?
user9516827
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.