스칼라 UDF 결과에 대해 필터링 해야하는 쿼리가 있습니다. 쿼리는 단일 문으로 전송되어야하며 (UDF 결과를 로컬 변수에 할당 할 수 없음) TVF를 사용할 수 없습니다. 스칼라 UDF로 인한 성능 문제를 알고 있습니다. 스칼라 UDF는 전체 계획을 순차적으로 강제 실행, 과도한 메모리 부여, 카디널리티 추정 문제 및 인라인 부족을 포함합니다. 이 질문에 대해 스칼라 UDF를 사용해야한다고 가정하십시오.
UDF 자체는 호출하는 데 비용이 많이 들지만 이론적으로 쿼리는 함수를 한 번만 계산하면되는 방식으로 옵티 마이저에 의해 논리적으로 쿼리를 구현할 수 있습니다. 이 질문에 대해 크게 단순화 된 예를 모의했습니다. 다음 쿼리는 내 컴퓨터에서 실행하는 데 6152ms가 걸립니다.
SELECT x1.ID
FROM dbo.X_100_INTEGERS x1
WHERE x1.ID >= dbo.EXPENSIVE_UDF();
쿼리 계획 의 필터 연산자는 각 행에 대해 함수가 한 번 평가되었음을 제안합니다.
DDL 및 데이터 준비 :
CREATE OR ALTER FUNCTION dbo.EXPENSIVE_UDF () RETURNS INT
AS
BEGIN
DECLARE @tbl TABLE (VAL VARCHAR(5));
-- make the function expensive to call
INSERT INTO @tbl
SELECT [VALUE]
FROM STRING_SPLIT(REPLICATE(CAST('Z ' AS VARCHAR(MAX)), 20000), ' ');
RETURN 1;
END;
GO
DROP TABLE IF EXISTS dbo.X_100_INTEGERS;
CREATE TABLE dbo.X_100_INTEGERS (ID INT NOT NULL);
-- insert 100 integers from 1 - 100
WITH
L0 AS(SELECT 1 AS c UNION ALL SELECT 1),
L1 AS(SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B),
L2 AS(SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B),
L3 AS(SELECT 1 AS c FROM L2 AS A CROSS JOIN L2 AS B),
L4 AS(SELECT 1 AS c FROM L3 AS A CROSS JOIN L3 AS B),
L5 AS(SELECT 1 AS c FROM L4 AS A CROSS JOIN L4 AS B),
Nums AS(SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS n FROM L5)
INSERT INTO dbo.X_100_INTEGERS WITH (TABLOCK)
SELECT n FROM Nums WHERE n <= 100;
코드를 실행하는 데 약 18 초가 걸리지 만 위 예제 의 db 바이올린 링크 는 다음과 같습니다 .
경우에 따라 공급 업체에서 제공 한 함수 코드를 편집하지 못할 수도 있습니다. 다른 경우에는 변경할 수 있습니다. 쿼리에서 스칼라 UDF를 한 번만 평가하도록하려면 어떻게해야합니까?