[sql] 다중 명령문 테이블 값 함수 대 인라인 테이블 값 함수

다음과 같은 경우를 보여줄 몇 가지 예 :

인라인 테이블 평가

CREATE FUNCTION MyNS.GetUnshippedOrders()
RETURNS TABLE
AS 
RETURN SELECT a.SaleId, a.CustomerID, b.Qty
    FROM Sales.Sales a INNER JOIN Sales.SaleDetail b
        ON a.SaleId = b.SaleId
        INNER JOIN Production.Product c ON b.ProductID = c.ProductID
    WHERE a.ShipDate IS NULL
GO

다중 명세서 테이블 평가

CREATE FUNCTION MyNS.GetLastShipped(@CustomerID INT)
RETURNS @CustomerOrder TABLE
(SaleOrderID    INT         NOT NULL,
CustomerID      INT         NOT NULL,
OrderDate       DATETIME    NOT NULL,
OrderQty        INT         NOT NULL)
AS
BEGIN
    DECLARE @MaxDate DATETIME

    SELECT @MaxDate = MAX(OrderDate)
    FROM Sales.SalesOrderHeader
    WHERE CustomerID = @CustomerID

    INSERT @CustomerOrder
    SELECT a.SalesOrderID, a.CustomerID, a.OrderDate, b.OrderQty
    FROM Sales.SalesOrderHeader a INNER JOIN Sales.SalesOrderHeader b
        ON a.SalesOrderID = b.SalesOrderID
        INNER JOIN Production.Product c ON b.ProductID = c.ProductID
    WHERE a.OrderDate = @MaxDate
        AND a.CustomerID = @CustomerID
    RETURN
END
GO

한 유형 (인라인 또는 다중 명령문)을 다른 유형보다 사용하는 것이 유리합니까? 하나가 다른 것보다 낫거나 차이가 순전히 구문적인 시나리오가 있습니까? 두 가지 예제 쿼리가 다른 작업을 수행한다는 것을 알고 있지만 그런 식으로 작성 해야하는 이유가 있습니까?

그들에 대한 독서와 장점 / 차이는 실제로 설명되지 않았습니다.



답변

Matt의 의견을 조사하면서 본인의 원래 진술을 수정했습니다. 그는 맞습니다. 둘 다 SELECT 문을 단순히 실행하더라도 인라인 테이블 값 함수 (ITVF)와 다중 명령문 테이블 값 함수 (MSTVF)간에 성능에 차이가있을 것입니다. SQL Server는 ITVF를VIEW문제의 테이블에 대한 최신 통계를 사용하여 실행 계획을 계산한다는 점에서 MSTVF는 SELECT 문의 전체 내용을 테이블 변수에 채우고 조인하는 것과 같습니다. 따라서 컴파일러는 MSTVF의 테이블에서 테이블 통계를 사용할 수 없습니다. 따라서 ITVF는 MSTVF보다 성능이 우수 할 것입니다. 내 테스트에서 완료 시간의 성능 차이는 무시할 수 있었지만 통계 측면에서 볼 때 눈에 띄었습니다.

귀하의 경우 두 기능은 기능적으로 동일하지 않습니다. MSTV 함수는 호출 될 때마다 추가 쿼리를 수행하고 가장 중요한 것은 고객 ID를 필터링합니다. 큰 쿼리에서 옵티마이 저는 전달 된 각 customerId에 대해 함수를 호출해야하므로 다른 유형의 조인을 이용할 수 없습니다. 그러나 MSTV 기능을 다음과 같이 다시 쓴 경우 :

CREATE FUNCTION MyNS.GetLastShipped()
RETURNS @CustomerOrder TABLE
    (
    SaleOrderID    INT         NOT NULL,
    CustomerID      INT         NOT NULL,
    OrderDate       DATETIME    NOT NULL,
    OrderQty        INT         NOT NULL
    )
AS
BEGIN
    INSERT @CustomerOrder
    SELECT a.SalesOrderID, a.CustomerID, a.OrderDate, b.OrderQty
    FROM Sales.SalesOrderHeader a 
        INNER JOIN Sales.SalesOrderHeader b
            ON a.SalesOrderID = b.SalesOrderID
        INNER JOIN Production.Product c 
            ON b.ProductID = c.ProductID
    WHERE a.OrderDate = (
                        Select Max(SH1.OrderDate)
                        FROM Sales.SalesOrderHeader As SH1
                        WHERE SH1.CustomerID = A.CustomerId
                        )
    RETURN
END
GO

쿼리에서 옵티마이 저는 해당 기능을 한 번 호출하여 더 나은 실행 계획을 작성할 수 있지만 매개 변수가없는 동등한 ITVS 또는 a보다 나은 것은 아닙니다 VIEW.

가능하면 MSTVF보다 ITVF를 선호해야합니다. 테이블의 열에서 데이터 유형, Null 허용 및 데이터 정렬을 사용하는 반면 다중 문 테이블 값 함수에서 이러한 속성을 선언하면 ITVF에서 더 나은 실행 계획을 얻을 수 있기 때문입니다. 내 경험상, ITVF가 VIEW보다 더 나은 옵션 인 많은 상황을 발견하지 못했지만 마일리지가 다를 수 있습니다.

Matt에게 감사합니다.

덧셈

최근에이 결과가 나타 났으므로 Wayne Sheffield가 수행 한 인라인 테이블 값 함수와 다중 문 함수의 성능 차이를 비교 한 훌륭한 분석이 있습니다.

그의 원래 블로그 게시물.

SQL Server Central에서 복사


답변

내부적으로 SQL Server는 뷰와 마찬가지로 인라인 테이블 값 함수를 처리하고 저장 프로 시저와 유사한 다중 문 테이블 값 함수를 처리합니다.

인라인 테이블 반환 함수가 외부 쿼리의 일부로 사용되면 쿼리 프로세서는 UDF 정의를 확장하고 이러한 개체의 인덱스를 사용하여 기본 개체에 액세스하는 실행 계획을 생성합니다.

다중 명령문 테이블 값 함수의 경우 함수 자체에 대한 실행 계획이 작성되고 실행 계획 캐시에 저장됩니다 (함수가 처음으로 실행 된 경우). 다중 문 테이블 값 함수가 더 큰 쿼리의 일부로 사용되면 옵티마이 저는 함수가 무엇을 반환하는지 알지 못하므로 일부 표준 가정을합니다. 실제로 함수는 단일 행을 반환하고 함수는 단일 행이있는 테이블에 대해 테이블 ​​스캔을 사용하여 액세스됩니다.

다중 문 테이블 값 함수가 제대로 수행하지 못하는 곳은 많은 수의 행을 반환하고 외부 쿼리에서 조인하는 경우입니다. 성능 문제는 주로 옵티마이 저가 단일 행이 리턴된다는 가정하에 계획을 작성한다는 사실에 기인하며, 이는 반드시 가장 적절한 계획은 아닙니다.

일반적으로 가능한 성능 문제로 인해 가능한 경우 인라인 테이블 값 함수를 다중 문 함수 (UPF가 외부 쿼리의 일부로 사용하는 경우)보다 우선적으로 사용해야한다는 것을 알았습니다.


답변

또 다른 차이점이 있습니다. 뷰와 마찬가지로 인라인 테이블 반환 함수를 삽입, 업데이트 및 삭제할 수 있습니다. 유사한 제한 사항이 적용됩니다-집계를 사용하여 함수를 업데이트 할 수없고 계산 된 열을 업데이트 할 수 없습니다 등


답변

귀하의 예는 질문에 매우 잘 대답합니다. 첫 번째 기능은 단일 선택으로 수행 할 수 있으며 인라인 스타일을 사용하는 것이 좋습니다. 두 번째는 단일 쿼리로 수행 될 수 있지만 (하위 쿼리를 사용하여 최대 날짜를 얻음) 일부 코더는 수행 한 것처럼 여러 명령문에서 읽기가 더 쉽고 자연 스럽습니다. 평범한 함수는 하나의 문장으로 수행 할 수 없으므로 다중 문 버전이 필요합니다.

가능한 한 가장 간단한 (인라인)을 사용하고 필요할 때 (분명히) 또는 개인 취향 / 가독성이 여분의 타이핑에 더 유리한 경우 다중 진술을 사용하는 것이 좋습니다.


답변

보고 비교 인라인 및 다중 문 테이블 반환 함수 당신이 좋은 설명 및 성능 벤치 마크를 찾을 수 있습니다


답변

이것을 테스트하지는 않았지만 다중 명령문 함수는 결과 세트를 캐시합니다. 옵티마이 저가 함수를 인라인하기에 너무 많은 경우가 있습니다. 예를 들어 “회사 번호”로 전달한 내용에 따라 다른 데이터베이스의 결과를 반환하는 함수가 있다고 가정합니다. 일반적으로 회사 번호로 필터링 한 유니온을 사용하여 뷰를 만들 수 있지만 때로는 SQL Server가 전체 유니온을 철회하고 하나의 선택을 호출하기에 충분하지 않은 경우가 있습니다. 테이블 함수에는 소스를 선택하는 논리가있을 수 있습니다.


답변

여러 줄 함수를 사용하는 또 다른 경우는 SQL 서버가 where 절을 누르지 않도록하는 것입니다.

예를 들어, 테이블 이름이있는 테이블이 있고 일부 테이블 이름은 C05_2019 및 C12_2018과 같은 형식이며 그 방식으로 포맷 된 모든 테이블은 동일한 스키마를 갖습니다. 모든 데이터를 하나의 테이블로 병합하고 05와 12를 CompNo 열과 2018,2019를 연도 열로 구문 분석하려고했습니다. 그러나 ACA_StupidTable과 같은 다른 테이블이 있는데 CompNo 및 CompYr을 추출 할 수 없으며 시도하면 변환 오류가 발생합니다. 그래서 내 쿼리는 두 부분으로 이루어졌으며 내부 쿼리는 ‘C_______’와 같은 형식의 테이블 만 반환했으며 외부 쿼리는 하위 문자열과 int 변환을 수행했습니다. 즉, CompNo로 Cast (Substring (2, 2)를 int로) SQL 서버가 결과가 필터링되기 전에 내 Cast 함수를 사용하기로 결정한 것을 제외하고는 모두 좋아 보입니다. 그래서 마음이 혼란 스러울 때 변환 오류가 발생합니다. 다중 명령문 테이블 함수는이를 방지 할 수 있습니다.