병렬 실행을 위해 스칼라 기능을 TVF 기능으로 변환


10

내 쿼리 중 하나가 릴리스 후 직렬 실행 모드에서 실행 중이었고 응용 프로그램에서 생성 된 LINQ to SQL 쿼리에서 참조되는 두 가지 새로운 기능이보기에 사용 된 것을 알았습니다. 그래서 나는 그 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로 변환 할 수 있습니까?

예. 아래와 같은 것이 그렇게 할 것입니다.

그것은 여전히 ​​꽤 무겁고 실행 된 상관 관계가 있으면 비효율적 일 것입니다. Aaron이 주석에서 지적한 것처럼 상수 값으로 이것을 호출하고 있지만 쿼리 계획 에이 값이 반영되어 한 번만 실행되기를 바랍니다.

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는 함수가 사용하는 테이블 변수에 대한 수정 사항을 병렬화 할 수 없습니다.

SQL Server 2017의 Interleaved Execution 이전에는 Multi-Statement Table Valued Functions의 행 추정치가 매우 낮았습니다.

이로 인한 부작용 중 하나는 계획이 저가에서 비용이 매우 낮고 병렬 처리에 대한 비용 임계 값을 위반하지 않는 경우가 많다는 것입니다.


1
스칼라 함수를 보면 스칼라 함수를 인라인 TVF로 변환 할 수 있습니까?
user9516827

1
@ user9516827 아마도 CTE를 함께 연결하여 비슷한 것을 할 수는 있지만 병렬로 수행하고 더 잘 수행하는지 등은 알 수 없습니다. 테스트하는 것은 당신에게 달려 있습니다.
Erik Darling

@MartinSmith : 내 실제 쿼리는 많은 조인 및 뷰에 대한 조인이있는 매우 복잡한 쿼리입니다.이 함수는 기본 쿼리의 선택 열에 사용되므로 고정 된 것을 얻으려고하는 이유입니다. -SQL (exec sp_executesql form) 생성 쿼리 및 .net 코드를 해킹하지 않는 제한이 있습니다. 감사합니다
user9516827 2009

10

SQL Server는 다중 문 TVF를 병렬 처리 할 수 ​​없습니다. 인라인 TVF 만 병렬화 할 수 있습니다.


1
거기에 변수가 있기 때문에 스칼라 함수를 Inline TVF로 변환 할 수 없습니까?
user9516827
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.