6

NEWID() を永続的な計算列と組み合わせて使用​​すると、奇妙な結果が得られます。私はいくつかの機能を間違って使用していますか?

列の作成時に永続化を使用しないため、列を選択するときに値を計算すると、正しい値が返されます。列 (col1) を更新すると、正しい値も返されます。

DECLARE @test TABLE (
    Col1 INT,
    Contains2 AS CASE WHEN 2 IN (Col1) THEN 1 ELSE 0 END PERSISTED)

INSERT INTO @test (Col1) VALUES
    (ABS(CHECKSUM(NEWID()) % 5)),
    (ABS(CHECKSUM(NEWID()) % 5)),
    (ABS(CHECKSUM(NEWID()) % 5)),
    (ABS(CHECKSUM(NEWID()) % 5)),
    (ABS(CHECKSUM(NEWID()) % 5))

SELECT * FROM @test
UPDATE @test SET Col1 = Col1*1
SELECT * FROM @test

/*
Col1    Contains2
2   0
2   0
0   1
4   0
3   0

Col1    Contains2
2   1
2   1
0   0
4   0
3   0
*/
4

2 に答える 2

4

どうやら、クエリ エンジンは乱数を行ごとに 2 回計算します。

1 回目はCol1、2 回目CASEは永続列のステートメントです。

NEWIDオプティマイザは、これが非決定論的関数であることを知らないか、気にせず、 2 回呼び出します。

実際には、選択の余地さえないかもしれません。オプティマイザーが舞台裏で一時テーブルを作成し、Col1乱数を生成する式の結果を入力し、その一時テーブルを読み戻して、これらの保存された中間結果を使用してCASE式の結果を計算し、最後に実行するようにしINSERTますか? この場合、中間結果をディスクに書き込まずに、オプティマイザーが式を 2 回計算する方が安価です。他の場合 (たとえば、50 億行ではなく 50 億行、または追加のインデックスがある場合) では、推定コストが異なる可能性があり、この動作が変わることがあります。

あなたがそれについてできることはあまりないと思います。この動作に注意してください。生成された乱数のセットを常に明示的にテーブルに保存してから、それらに基づいてさらに計算を実行します。

SQL Server 2008 と 2014 で再現しました。これは SQL Server 2008 で取得した実行計画ですが、あまり興味深いものではありません。2014 年も計画は同じですが、Topオペレーターはいません。

2008年計画

Constant Scanoperator はUnion1009リストを出力します。これはCompute Scalar後で使用されます。Constant Scanand/orCompute Scalar演算子の実装の詳細に行き着くと思います。

観測された動作は、newid()ここで行ごとに 2 回呼び出されていることを示しています。

于 2016-06-30T13:17:18.590 に答える