SQL Serverはクエリの計算値をキャッシュしますか?


10

このタイプのクエリに遭遇するたびに、SQL Serverがどのように機能するかを常に疑問に思っています。私は計算を必要とし、その後、例えば、複数の場所でその値を使用したクエリのいずれかのタイプを実行した場合selectorder by、SQL Serverは、行ごとに二回、それが計算されますかそれがキャッシュされますか?さらに、これはユーザー定義関数でどのように機能しますか?

例:

SELECT CompanyId, Count(*)
FROM Sales
ORDER BY Count(*) desc

SELECT Geom.BufferWithTolerance(@radius, 0.01, 0).STEnvelope().STPointN(1).STX, Geom.BufferWithTolerance(@radius, 0.01, 0).STEnvelope().STPointN(1).STY
FROM Table

SELECT Id, udf.MyFunction(Id)
FROM Table
ORDER BY udf.MyFunction(Id)

それをより効率的にする方法はありますか、それともSQL Serverはそれを処理するのに十分スマートですか?


「それは依存する」ここに1つの展示rextester.com/DXOB90032
Martin Smith


@MartinSmithは非決定的関数を使用していませんか?もしそうなら、私はSQLがそれを2回実行することを期待します。
Jonas Stawski

常に例外があります!あなたは試すことができますSELECT RAND() FROM Sales order by RAND()-これは非決定的であり、実行時定数であるため、一度だけ評価されます。
マーティンスミス

回答:


11

SQL Serverクエリオプティマイザーは、繰り返し計算された値を1つの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;

クエリプランに基づいて、これは行ごとに1回、または100回実行されます。

クエリプラン1

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回実行されることを示唆しています。

クエリプラン2

の結果はsys.dm_exec_function_stats、関数が200回実行されたことを示しています。

クエリプランを使用して、計算スカラーが実行される回数を常に把握できるとは限らないことに注意してください。次の引用は、「スカラー、式、実行プランのパフォーマンスを計算する」からのものです。

これにより、Compute Scalarは他のほとんどの演算子と同じように動作するものと考えられます。行が流れると、Compute Scalarに含まれるすべての計算結果がストリームに追加されます。これは一般的には当てはまりません。名前にかかわらず、Compute Scalarは常に何も計算するわけではなく、単一のスカラー値を常に含むわけではありません(たとえば、ベクトル、エイリアス、またはブール述語の場合もあります)。多くの場合、Compute Scalarは単純に式を定義します。実際の計算は、実行プランの後半で結果が必要になるまで延期されます。

別の例を試してみましょう。次のクエリでは、UDFが1回計算されることを望みます。

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;

クエリプランは、1回だけ計算されることを示唆しています。

クエリプラン

しかし、DMVは真実を明らかにします。計算スカラーは、結合演算子にある必要になるまで延期されます。100回評価されます。

また、オプティマイザが同じ式を何度も再計算しないようにするために何ができるかを尋ねました。コードでスカラーUDFを使用するのを避けるのが最善の方法です。これらには、この問題以外にも、メモリ許可の増大、クエリ全体の実行を強制する、MAXDOP 1カーディナリティの見積もりが悪い、CPUの使用率が高くなるなど、多くのパフォーマンスの問題があります。UDFを使用する必要があり、そのUDFの値が定数である場合は、クエリの外部で計算してローカル変数に入れることができます。

UDFのないクエリの場合、同じ結果を返すが、まったく同じ方法で入力されない式を記述しないようにすることができます。この次の例では、一般に入手可能なAdventureworksDW2016CTP3データベースを使用していますが、実際にはどのデータベースでも使用できます。COUNT(*)このクエリでは何回計算されますか?

SELECT OrderDateKey, COUNT(*) 
FROM dbo.FactResellerSales
GROUP BY OrderDateKey
ORDER BY COUNT(*) DESC;

このクエリでは、ハッシュマッチ(集計)演算子を調べることでこれを把握できます。

ハッシュマッチ

COUNT(*)それぞれのユニークな値のために一度計算されますOrderDateKeyORDER BY句を含めても、2回計算されることはありません。ここで実行計画を見ることができます

ここで、まったく同じ結果を返すが別の方法で記述されたクエリを考えてみます。

SELECT OrderDateKey, SUM(1)
FROM dbo.FactResellerSales
GROUP BY OrderDateKey
ORDER BY COUNT(*) DESC;

クエリオプティマイザーはそれらを組み合わせるほどスマートではないため、追加の作業が行われます。

ハッシュマッチ2

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.