SQL Server 쿼리 최적화 프로그램은 반복 계산 된 값을 단일 Compute Scalar 연산자로 결합 할 수 있습니다. 이를 수행할지 여부는 쿼리 계획 원가 계산 및 계산 된 값의 속성에 따라 다릅니다. 예상 한대로 비 결정적이며 계산 된 값 (예 : 등)에는이 작업을 수행하지 않습니다 RAND(). 또한 사용자 정의 함수에 대해서는이 작업을 수행하지 않습니다.
사용자 정의 함수 예제로 시작하겠습니다. 다음은 사용자 정의 함수의 훌륭한 예입니다.
CREATE OR ALTER FUNCTION dbo.NULL_FUNCTION (@N BIGINT) RETURNS BIGINT
WITH SCHEMABINDING
AS
BEGIN
RETURN NULL;
END;
또한 테이블을 만들고 여기에 100 개의 행을 넣고 싶습니다.
CREATE TABLE X_100 (N BIGINT NOT NULL);
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 X_100 WITH (TABLOCK)
SELECT n
FROM Nums WHERE n <= 100;
dbo.NULL_FUNCTION함수 determistic이다. 다음 쿼리에 대해 몇 번이나 실행됩니까?
SELECT n, dbo.NULL_FUNCTION(n)
FROM X_100;
쿼리 계획에 따라 각 행마다 한 번 또는 100 번 실행됩니다.

SQL Server 2016에는 sys.dm_exec_function_stats DMV가 도입되었습니다 . 해당 DMV의 스냅 샷을 작성하여 쿼리에서 UDF가 몇 번 실행되는지 확인할 수 있습니다.
SELECT execution_count
FROM sys.dm_exec_function_stats
WHERE object_id = OBJECT_ID('NULL_FUNCTION');
그 결과는 100이므로 함수가 100 번 실행되었습니다.
또 다른 간단한 쿼리를 시도해 봅시다.
SELECT n, dbo.NULL_FUNCTION(n), dbo.NULL_FUNCTION(n)
FROM X_100;
쿼리 계획은 기능이 200 회 실행될 것이라고 제안합니다.

결과는 sys.dm_exec_function_stats함수가 200 회 실행되었음을 암시합니다.
계산 스칼라가 몇 번 실행되는지 파악하기 위해 항상 쿼리 계획을 사용할 수는 없습니다. 다음 인용문은 " 컴퓨팅 스칼라, 표현식 및 실행 계획 성능 " 에서 인용 한 것입니다 .
따라서 사람들은 Compute Scalar가 다른 대부분의 연산자처럼 동작한다고 생각하게됩니다. 행이 흐름에 따라 Compute Scalar에 포함 된 계산 결과가 스트림에 추가됩니다. 이것은 일반적으로 사실이 아닙니다. 이름에도 불구하고 Compute Scalar는 항상 아무것도 계산하지 않으며 항상 단일 스칼라 값을 포함하지는 않습니다 (예 : 벡터, 별칭 또는 부울 술어 일 수 있음). 종종 Compute Scalar는 단순히 표현식을 정의합니다. 실제 계산은 나중에 실행 계획의 결과가 필요할 때까지 연기됩니다.
다른 예를 봅시다. 다음 쿼리의 경우 UDF가 한 번 계산되기를 바랍니다.
WITH NULL_FUNCTION_CTE (NULL_VALUE) AS
(
SELECT DISTINCT dbo.NULL_FUNCTION(0)
)
SELECT n , cte.NULL_VALUE
FROM X_100
CROSS JOIN NULL_FUNCTION_CTE cte;
쿼리 계획은 한 번만 계산되도록 제안합니다.

그러나 DMV는 진실을 밝힙니다. 계산 스칼라는 조인 연산자에있는 계산 스칼라가 필요할 때까지 지연됩니다. 100 회 평가됩니다.
또한 최적화 프로그램이 동일한 표현식을 여러 번 다시 계산하지 않도록 권장하기 위해 수행 할 수있는 작업도 요청했습니다. 코드에서 스칼라 UDF를 사용하지 않는 것이 가장 좋습니다. 메모리 부여 확대, 전체 쿼리 실행 MAXDOP 1, 잘못된 카디널리티 추정 및 추가 CPU 활용 으로이 문제 이외의 여러 성능 문제가 있습니다 . UDF를 사용해야하고 해당 UDF의 값이 상수 인 경우 쿼리 외부에서이를 계산하여 로컬 변수에 넣을 수 있습니다.
UDF가없는 조회의 경우 동일한 결과를 리턴하지만 정확히 같은 방식으로 입력되지 않는 표현식을 작성하지 않도록 시도 할 수 있습니다. 이 다음 예제에서는 공개적으로 이용 가능한 AdventureworksDW2016CTP3 데이터베이스를 사용하고 있지만 실제로는 모든 데이터베이스가 사용합니다. COUNT(*)이 쿼리에 대해 몇 번 계산됩니까?
SELECT OrderDateKey, COUNT(*)
FROM dbo.FactResellerSales
GROUP BY OrderDateKey
ORDER BY COUNT(*) DESC;
이 쿼리에서는 Hash Match (aggregate) 연산자를 보면이를 알 수 있습니다.

의 COUNT(*)각 고유 값에 대해 한 번 계산됩니다 OrderDateKey. ORDER BY절을 포함 한다고해도 두 번 계산되지는 않습니다. 여기 에서 실행 계획을 볼 수 있습니다 .
이제 똑같은 결과를 반환하지만 다른 방식으로 작성된 쿼리를 고려하십시오.
SELECT OrderDateKey, SUM(1)
FROM dbo.FactResellerSales
GROUP BY OrderDateKey
ORDER BY COUNT(*) DESC;
쿼리 최적화 프로그램은 이들을 결합하기에 충분하지 않으므로 추가 작업이 수행됩니다.
