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


198

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

인라인 테이블 평가

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

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

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


또한 인라인 함수의 큰 장점 중 하나는 ROWID (TIMESTAMP) 열을 선택할 수 있지만 다중 명령문 함수의 리턴 테이블에 TIMESTAMP 데이터를 삽입 할 수 없다는 것입니다!
Artru

3
훌륭한 스레드 주셔서 감사합니다. 나는 많은 것을 배웠다. 그러나 명심해야 할 것은 ITV 인 기능을 MSTV로 대체 할 때 프로파일 러는 ITV를 변경한다고 생각합니다. MSTV 관점에서 구문을 올바르게 얻기 위해 무엇을 하든지, 일반적으로 BEGIN 다음의 첫 번째 명령문 주위에서 재 컴파일이 항상 실패합니다. 이 문제를 해결할 수있는 유일한 방법은 이전 기능을 삭제하고 새 기능을 MSTV로 작성하는 것입니다.
Fandango68

답변:


141

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에서 복사


40
다중 문 함수는 쿼리 옵티마이 저가 통계를 사용하지 못하게하기 때문에 성능이 크게 저하되는 경우가 많습니다. 다중 문 기능 사용으로 인해 실행 계획의 선택이 매우 열악한 것을 보았을 때마다 1 달러를 지불하면 (대개 반환 된 행 수를 1로 추정하기 때문에) 소형차를 구입하기에 충분합니다.
Matt Whitfield

내가 찾은 가장 좋은 설명은 첫 번째 답변과 관련 게시물에 있습니다 : stackoverflow.com/questions/4109152/… 관련 문서를 놓치지 마십시오. 빠르게 읽을 수 있으며 매우 흥미 롭습니다.
JotaBe

1
SQL Server 2017에 대한이 답변에 대한 업데이트가 있습니까? : youtube.com/watch?time_continue=2&v=szTmo6rTUjM
Ralph

29

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

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

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

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

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


2
다중 명령문 테이블 값 함수를 스토어드 프로 시저와 유사하게 처리 할 수 ​​있지만 기능적으로 동일한 스토어드 프로 시저가 큰 데이터 세트의 테이블 값 함수보다 훨씬 빠릅니다. 다중 문 테이블 값 함수보다 저장된 procs를 고수하고 있습니다.
Kekoa

6
해당 결과를 다른 쿼리에 조인해야하지 않는 한.
Guillermo Gutiérrez

왜 둘 다 사용하지 않습니까? 다중 문 테이블 반환 함수의 결과를 반환하는 저장 프로 시저 두 세계의 최고.
Robino

13

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


3

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

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


답변 해주셔서 감사합니다. 따라서 기본적으로 다중 문은 가독성을 위해 인라인 함수에서 가능한 것보다 함수가 더 복잡한 경우에만 사용해야합니다. 다중 문에 성능상의 이점이 있습니까?
AndrewC

모르겠지만 그렇게 생각하지는 않습니다. SQL Server가 변수, 임시 테이블 등을 사용하여 수동으로 시도 할 수있는 최적화를 파악하도록하는 것이 좋습니다. 특정 경우에이를 입증 / 반증하기 위해 성능 테스트를 수행 할 수도 있습니다.
Ray

다시 한번 감사드립니다. 시간이 더 있으면 더 자세히 살펴볼 수 있습니다! :)
AndrewC


0

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


0

여러 줄 함수를 사용하는 또 다른 경우는 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 함수를 사용하기로 결정한 것을 제외하고는 모두 좋아 보입니다. 그래서 마음이 혼란 스러울 때 변환 오류가 발생합니다. 다중 명령문 테이블 함수는이를 방지 할 수 있습니다.


0

아마도 매우 응축 된 방식 일 것입니다. ITVF (인라인 TVF) : u가 DB 인 경우 더 많은 매개 변수보기이며 단일 SELECT st를 사용하십시오.

MTVF (Multi-statement TVF) : 개발자, 테이블 변수를 작성 및로드합니다.


-2

쿼리를 수행하려는 경우 다음과 같이 인라인 테이블 값 함수에 조인 할 수 있습니다.

SELECT
    a.*,b.*
    FROM AAAA a
        INNER JOIN MyNS.GetUnshippedOrders() b ON a.z=b.z

오버 헤드가 거의 발생하지 않고 잘 작동합니다.

다중 쿼리 테이블 값을 유사한 쿼리에 사용하려고하면 성능 문제가 발생합니다.

SELECT
    x.a,x.b,x.c,(SELECT OrderQty FROM MyNS.GetLastShipped(x.CustomerID)) AS Qty
    FROM xxxx   x

리턴 된 각 행에 대해 함수를 1 회 실행하기 때문에 결과 세트가 커질수록 느리게 실행됩니다.


아, 그래서 인라인이 성능면에서 훨씬 낫다고 말할 것입니까?
AndrewC

1
아니요, 둘 다 테이블을 반환하므로 테이블을 열에 넣으려고 할 때 두 번째 SQL이 유효하지 않습니다.
cjk

1
@ck, yo에 대해 언급 한 쿼리를 업데이트했습니다. 두 번째 함수에 사용 된 함수의 매개 변수는 하위 쿼리로 사용되도록하여 성능이 저하됩니다.
KM.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.