Palindrome 질문 에 대한 답변 중 일부를 비교하면 (답을 삭제 한 이후 10k + 사용자 만 해당) 혼란스러운 결과가 나타납니다.
나는 표준 함수를 실행하는 것보다 더 빠를 것으로 생각 되는 다중 문, 스키마 바운드 TVF 를 제안 했습니다. 또한 아래에서 볼 수 있듯이 다중 명령문 TVF가 "인라인"될 것이라는 인상을 받았습니다. 이 질문은 두 가지 TVF 스타일의 성능 차이에 관한 것입니다. 먼저 코드를 확인해야합니다.
다음은 다중 문장 TVF입니다.
IF OBJECT_ID('dbo.IsPalindrome') IS NOT NULL
DROP FUNCTION dbo.IsPalindrome;
GO
CREATE FUNCTION dbo.IsPalindrome
(
@Word NVARCHAR(500)
)
RETURNS @t TABLE
(
IsPalindrome BIT NOT NULL
)
WITH SCHEMABINDING
AS
BEGIN
DECLARE @IsPalindrome BIT;
DECLARE @LeftChunk NVARCHAR(250);
DECLARE @RightChunk NVARCHAR(250);
DECLARE @StrLen INT;
DECLARE @Pos INT;
SET @RightChunk = '';
SET @IsPalindrome = 0;
SET @StrLen = LEN(@Word) / 2;
IF @StrLen % 2 = 1 SET @StrLen = @StrLen - 1;
SET @Pos = LEN(@Word);
SET @LeftChunk = LEFT(@Word, @StrLen);
WHILE @Pos > (LEN(@Word) - @StrLen)
BEGIN
SET @RightChunk = @RightChunk + SUBSTRING(@Word, @Pos, 1)
SET @Pos = @Pos - 1;
END
IF @LeftChunk = @RightChunk SET @IsPalindrome = 1;
INSERT INTO @t VALUES (@IsPalindrome);
RETURN
END
GO
인라인 TVF :
IF OBJECT_ID('dbo.InlineIsPalindrome') IS NOT NULL
DROP FUNCTION dbo.InlineIsPalindrome;
GO
CREATE FUNCTION dbo.InlineIsPalindrome
(
@Word NVARCHAR(500)
)
RETURNS TABLE
WITH SCHEMABINDING
AS RETURN (
WITH Nums AS
(
SELECT
N = number
FROM
dbo.Numbers
)
SELECT
IsPalindrome =
CASE
WHEN EXISTS
(
SELECT N
FROM Nums
WHERE N <= L / 2
AND SUBSTRING(S, N, 1) <> SUBSTRING(S, 1 + L - N, 1)
)
THEN 0
ELSE 1
END
FROM
(SELECT LTRIM(RTRIM(@Word)), LEN(@Word)) AS v (S, L)
);
GO
Numbers
위 함수 의 테이블은 다음과 같이 정의됩니다.
CREATE TABLE dbo.Numbers
(
Number INT NOT NULL
);
참고 : 숫자 테이블에는 인덱스가없고 기본 키가 없으며 1,000,000 개의 행이 있습니다.
테스트 베드 임시 테이블 :
IF OBJECT_ID('tempdb.dbo.#Words') IS NOT NULL
DROP TABLE #Words;
GO
CREATE TABLE #Words
(
Word VARCHAR(500) NOT NULL
);
INSERT INTO #Words(Word)
SELECT o.name + REVERSE(w.name)
FROM sys.objects o
CROSS APPLY (
SELECT o.name
FROM sys.objects o
) w;
내 테스트 시스템에서 위의 INSERT
결과는 16,900 개의 행이 #Words
테이블 에 삽입됩니다 .
두 변형을 테스트하려면 SET STATISTICS IO, TIME ON;
다음을 사용하십시오.
SELECT w.Word
, p.IsPalindrome
FROM #Words w
CROSS APPLY dbo.IsPalindrome(w.Word) p
ORDER BY w.Word;
SELECT w.Word
, p.IsPalindrome
FROM #Words w
CROSS APPLY dbo.InlineIsPalindrome(w.Word) p
ORDER BY w.Word;
InlineIsPalindrome
버전이 훨씬 빠를 것으로 예상 했지만 다음 결과는 해당 가정을 지원하지 않습니다.
다중 문장 TVF :
테이블 '# A1CE04C3'. 스캔 횟수 16896, 논리적 읽기 16900, 물리적 읽기 0, 미리 읽기 0, lob 논리적 읽기 0, lob 물리적 읽기 0, lob 미리 읽기 0.
테이블 '작업 테이블'. 스캔 횟수 0, 논리적 읽기 0, 물리적 읽기 0, 미리 읽기 0, lob 논리적 읽기 0, lob 물리적 읽기 0, lob 미리 읽기 0.
테이블 '#Words'. 스캔 카운트 1, 논리적 읽기 88, 물리적 읽기 0, 미리 읽기 0, lob 논리적 읽기 0, lob 물리적 읽기 0, lob 미리 읽기 0.SQL Server 실행 시간 :
CPU 시간 = 1700ms, 경과 시간 = 2022ms
SQL Server 구문 분석 및 컴파일 시간 :
CPU 시간 = 0ms, 경과 시간 = 0ms
인라인 TVF :
테이블 '숫자'. 스캔 카운트 1, 논리적 읽기 1272030, 물리적 읽기 0, 미리 읽기 0, lob 논리적 읽기 0, lob 물리적 읽기 0, lob 미리 읽기 0.
테이블 '작업 테이블'. 스캔 횟수 0, 논리적 읽기 0, 물리적 읽기 0, 미리 읽기 0, lob 논리적 읽기 0, lob 물리적 읽기 0, lob 미리 읽기 0.
테이블 '#Words'. 스캔 카운트 1, 논리적 읽기 88, 물리적 읽기 0, 미리 읽기 0, lob 논리적 읽기 0, lob 물리적 읽기 0, lob 미리 읽기 0.SQL Server 실행 시간 :
CPU 시간 = 137874ms, 경과 시간 = 139415ms
SQL Server 구문 분석 및 컴파일 시간 :
CPU 시간 = 0ms, 경과 시간 = 0ms
실행 계획은 다음과 같습니다.
이 경우 인라인 변형이 다중 문 변형보다 훨씬 느린 이유는 무엇입니까?
@AaronBertrand의 의견에 dbo.InlineIsPalindrome
따라 CTE가 반환 한 행을 입력 단어의 길이와 일치하도록 제한하는 기능을 수정했습니다 .
CREATE FUNCTION dbo.InlineIsPalindrome
(
@Word NVARCHAR(500)
)
RETURNS TABLE
WITH SCHEMABINDING
AS RETURN (
WITH Nums AS
(
SELECT
N = number
FROM
dbo.Numbers
WHERE
number <= LEN(@Word)
)
SELECT
IsPalindrome =
CASE
WHEN EXISTS
(
SELECT N
FROM Nums
WHERE N <= L / 2
AND SUBSTRING(S, N, 1) <> SUBSTRING(S, 1 + L - N, 1)
)
THEN 0
ELSE 1
END
FROM
(SELECT LTRIM(RTRIM(@Word)), LEN(@Word)) AS v (S, L)
);
@MartinSmith가 제안했듯이 기본 키 및 클러스터형 인덱스를 dbo.Numbers
테이블에 추가했습니다. 이는 분명히 프로덕션 환경에서 예상되는 것과 비슷하고 더 가깝습니다.
위의 테스트를 다시 실행하면 다음과 같은 통계가 나타납니다.
CROSS APPLY dbo.IsPalindrome(w.Word) p
:
(17424 행에 영향을 미침)
표 '# B1104853'. 스캔 카운트 17420, 논리적 읽기 17424, 물리적 읽기 0, 미리 읽기 0, lob 논리적 읽기 0, lob 물리적 읽기 0, lob read-ahead 읽기 0.
테이블 '작업 테이블'. 스캔 횟수 0, 논리적 읽기 0, 물리적 읽기 0, 미리 읽기 0, lob 논리적 읽기 0, lob 물리적 읽기 0, lob 미리 읽기 0.
테이블 '#Words'. 스캔 횟수 1, 논리적 읽기 90, 물리적 읽기 0, 미리 읽기 0, lob 논리적 읽기 0, lob 물리적 읽기 0, lob 미리 읽기 0SQL Server 실행 시간 :
CPU 시간 = 1763ms, 경과 시간 = 2192ms
dbo.FunctionIsPalindrome(w.Word)
:
(17424 행에 영향을 미침)
테이블 '작업 테이블'. 스캔 횟수 0, 논리적 읽기 0, 물리적 읽기 0, 미리 읽기 0, lob 논리적 읽기 0, lob 물리적 읽기 0, lob 미리 읽기 0.
테이블 '#Words'. 스캔 횟수 1, 논리적 읽기 90, 물리적 읽기 0, 미리 읽기 0, lob 논리적 읽기 0, lob 물리적 읽기 0, lob 미리 읽기 0SQL Server 실행 시간 :
CPU 시간 = 328ms, 경과 시간 = 424ms
CROSS APPLY dbo.InlineIsPalindrome(w.Word) p
:
(17424 행에 영향을 미침)
테이블 '숫자'. 스캔 카운트 1, 논리적 읽기 237100, 물리적 읽기 0, 미리 읽기 0, lob 논리적 읽기 0, lob 물리적 읽기 0, lob read-ahead 읽기 0.
테이블 '작업 테이블'. 스캔 횟수 0, 논리적 읽기 0, 물리적 읽기 0, 미리 읽기 0, lob 논리적 읽기 0, lob 물리적 읽기 0, lob 미리 읽기 0.
테이블 '#Words'. 스캔 횟수 1, 논리적 읽기 90, 물리적 읽기 0, 미리 읽기 0, lob 논리적 읽기 0, lob 물리적 읽기 0, lob 미리 읽기 0SQL Server 실행 시간 :
CPU 시간 = 17737ms, 경과 시간 = 17946ms
SQL Server 2012 SP3, v11.0.6020, Developer Edition에서 이것을 테스트하고 있습니다.
다음은 기본 키와 클러스터형 인덱스가있는 숫자 테이블의 정의입니다.
CREATE TABLE dbo.Numbers
(
Number INT NOT NULL
CONSTRAINT PK_Numbers
PRIMARY KEY CLUSTERED
);
;WITH n AS
(
SELECT v.n
FROM (
VALUES (1)
,(2)
,(3)
,(4)
,(5)
,(6)
,(7)
,(8)
,(9)
,(10)
) v(n)
)
INSERT INTO dbo.Numbers(Number)
SELECT ROW_NUMBER() OVER (ORDER BY n1.n)
FROM n n1
, n n2
, n n3
, n n4
, n n5
, n n6;