다중 문 테이블 반환 함수는 결과를 테이블 변수로 반환합니다.
이러한 결과가 재사용되거나 함수가 호출 될 때마다 항상 완전히 평가됩니까?
다중 문 테이블 반환 함수는 결과를 테이블 변수로 반환합니다.
이러한 결과가 재사용되거나 함수가 호출 될 때마다 항상 완전히 평가됩니까?
답변:
다중 문 테이블 반환 함수 (msTVF)의 결과는 문 (또는 연결)에서 캐시되거나 재사용 되지 않지만 msTVF 결과가 같은 문 내 에서 재사용 될 수있는 몇 가지 방법이 있습니다 . 그 정도로 msTVF가 호출 될 때마다 다시 채워지는 것은 아닙니다.
이 (고의로 비효율적 인) msTVF는 각 행에 타임 스탬프와 함께 지정된 범위의 정수를 반환합니다.
IF OBJECT_ID(N'dbo.IntegerRange', 'TF') IS NOT NULL
DROP FUNCTION dbo.IntegerRange;
GO
CREATE FUNCTION dbo.IntegerRange (@From integer, @To integer)
RETURNS @T table
(
n integer PRIMARY KEY,
ts datetime DEFAULT CURRENT_TIMESTAMP
)
WITH SCHEMABINDING
AS
BEGIN
WHILE @From <= @To
BEGIN
INSERT @T (n)
VALUES (@From);
SET @From = @From + 1;
END;
RETURN;
END;
함수 호출에 대한 모든 매개 변수가 상수 (또는 런타임 상수) 인 경우 실행 계획은 테이블 변수 결과를 한 번 채 웁니다. 계획의 나머지 부분은 테이블 변수에 여러 번 액세스 할 수 있습니다. 테이블 변수의 정적 특성은 실행 계획에서 인식 할 수 있습니다. 예를 들면 다음과 같습니다.
SELECT
IR.n,
IR.ts
FROM dbo.IntegerRange(1, 5) AS IR
ORDER BY
IR.n;
다음과 유사한 결과를 반환합니다.
실행 계획은 다음과 같습니다.
Sequence 연산자는 먼저 Table Valued Function 연산자를 호출하여 테이블 변수를 채 웁니다 (이 연산자는 행을 반환하지 않습니다). 그런 다음 시퀀스는 두 번째 입력을 호출하여 테이블 변수의 내용을 반환합니다 (이 경우 클러스터형 인덱스 스캔 사용).
계획이 '정적'테이블 변수 결과를 사용하고 있다는 사실은 시퀀스 아래의 테이블 값 함수 연산자입니다. 테이블 변수는 계획의 나머지가 진행되기 전에 한 번 완전히 채워 져야합니다.
테이블 변수 결과가 두 번 이상 액세스됨을 표시하기 위해 행 번호가 1에서 5까지 인 두 번째 테이블을 사용합니다.
IF OBJECT_ID(N'dbo.T', 'U') IS NOT NULL
DROP TABLE dbo.T;
CREATE TABLE dbo.T (i integer NOT NULL);
INSERT dbo.T (i)
VALUES (1), (2), (3), (4), (5);
그리고이 테이블을 함수에 조인하는 새로운 쿼리 (이것은으로도 쓰일 수 있습니다 APPLY
) :
SELECT T.i,
IR.n,
IR.ts
FROM dbo.T AS T
JOIN dbo.IntegerRange(1, 5) AS IR
ON IR.n = T.i;
결과는 다음과 같습니다.
실행 계획 :
이전과 마찬가지로 시퀀스는 테이블 변수 msTVF 결과를 먼저 채 웁니다. 다음으로 중첩 루프를 사용하여 테이블의 각 행을 T
msTVF 결과의 행 으로 조인합니다 . 함수 정의에 테이블 변수에 유용한 인덱스가 포함되어 있으므로 인덱스 탐색을 사용할 수 있습니다.
요점은 msTVF에 대한 매개 변수가 상수 (변수 및 매개 변수 포함)이거나 실행 엔진에 의한 명령문에 대한 런타임 상수로 처리 될 때 계획에 msTVF 테이블 변수 결과에 대해 두 개의 별도 연산자가있는 것입니다. 표; 다른 하나는 결과에 액세스하여 테이블에 여러 번 액세스하고 함수 정의에 선언 된 인덱스를 사용할 수 있습니다.
상관 된 매개 변수 (외부 참조) 또는 비 일관적인 함수 매개 변수를 사용할 때의 차이점을 강조 T
하기 위해 함수가 훨씬 더 많은 작업을 수행 할 수 있도록 테이블의 내용을 변경합니다 .
TRUNCATE TABLE dbo.T;
INSERT dbo.T (i)
VALUES (50001), (50002), (50003), (50004), (50005);
다음 수정 된 쿼리는 이제 T
함수 매개 변수 중 하나 에서 테이블 에 대한 외부 참조를 사용합니다 .
SELECT T.i,
IR.n,
IR.ts
FROM dbo.T AS T
CROSS APPLY dbo.IntegerRange(1, T.i) AS IR
WHERE IR.n = T.i;
이 쿼리는 다음과 같은 결과를 반환하는 데 약 8 초가 걸립니다 .
열의 행 간 시간 차이를 확인하십시오 ts
. 이 WHERE
절은 합리적인 크기의 출력에 대한 최종 결과를 제한하지만 비효율적 인 함수는 테이블 변수를 50,000-odd 행으로 채우는 데 여전히 시간이 걸립니다 ( i
from table 의 상관 값에 따라 다름 T
).
실행 계획은 다음과 같습니다.
Sequence 연산자가 없다는 것을 알 수 있습니다. 이제 테이블 변수를 채우고 중첩 루프 조인의 각 반복 에서 해당 행 을 반환하는 단일 테이블 반환 함수 연산자가 있습니다.
테이블 T에 5 개의 행만 있으면 Table Valued Function 연산자가 5 번 실행됩니다. 첫 번째 반복에서 50,001 개의 행을 생성하고 두 번째 반복에서 50,002 개의 행을 생성합니다. 테이블 변수는 반복 사이에 '제거 (잘려)'되므로 5 개의 호출 각각은 전체 모집단입니다. 그렇기 때문에 속도가 느리고 각 행이 결과에 표시되는 데 거의 같은 시간이 걸립니다.
사이드 노트 :
당연히, 위의 시나리오는 msTVF가 각 반복에서 많은 행을 채울 때 성능이 얼마나 저하 될 수 있는지를 보여주기 위해 의도적으로 고안되었습니다.
합리적인 위의 코드의 구현은 설정합니다 모두 에 msTVF 매개 변수를 i
하고, 중복 제거 WHERE
절을. 테이블 변수는 반복 할 때마다 잘리고 다시 채워지지만 매번 한 행 씩만 채워집니다.
또한 이전 단계에서 최소값과 최대 i
값을 가져와 T
변수에 저장할 수 있습니다. 상관 된 매개 변수 대신 변수를 사용하여 함수를 호출하면 앞에서 언급 한대로 '정적'테이블 변수 패턴을 사용할 수 있습니다.
시퀀스 정적 패턴을 사용할 수없는 원래 질문을 다시 한 번 리턴하기 위해 SQL Server는 중첩 루프 조인의 이전 반복 이후에 상관 된 매개 변수가 변경되지 않은 경우 msTVF 테이블 변수를 자르고 다시 채우는 것을 피할 수 있습니다 .
이를 입증하기 위해 내용을 T
5 개의 동일한 i
값으로 대체 합니다.
TRUNCATE TABLE dbo.T;
INSERT dbo.T (i)
VALUES (50005), (50005), (50005), (50005), (50005);
상관 된 매개 변수가있는 쿼리 :
SELECT T.i,
IR.n,
IR.ts
FROM dbo.T AS T
CROSS APPLY dbo.IntegerRange(1, T.i) AS IR
WHERE IR.n = T.i;
이번에는 약 1.5 초 후에 결과가 나타납니다 .
각 행에서 동일한 타임 스탬프에 유의하십시오. 테이블 변수의 캐시 된 결과는 상관 된 값 i
이 변경되지 않은 후속 반복에 재사용됩니다 . 매번 50,005 개의 행을 삽입하는 것보다 결과를 재사용하는 것이 훨씬 빠릅니다.
실행 계획은 이전과 매우 유사합니다.
주요 차이점은 Table Valued Function 연산자 의 Actual Rebinds 및 Actual Rewinds 속성에 있습니다.
상관 된 매개 변수가 변경되지 않으면 SQL Server는 테이블 변수에서 현재 결과를 재생 (되감기) 할 수 있습니다. 상관 관계가 변경되면 SQL Server는 테이블 변수를 잘라내어 다시 채워야합니다 (리 바인드). 하나의 리 바인드는 첫 번째 반복에서 발생합니다. 값 T.i
이 변경되지 않기 때문에 4 개의 후속 반복은 모두 되감기 됩니다.