3

ビット値の列を持つテーブルがあります。関連付けられたアイテムのすべてのレコードが true の場合に true を返す関数を作成したいと考えています。

私が見つけた1つの方法は次のとおりです。

Select @Ret = CAST(MIN(CAST(IsCapped as tinyInt)) As Bit) 
    from ContractCover cc
    Inner join  ContractRiskVersion crv on cc.ContractRiskId = crv.ContractRiskId
    WHERE crv.ContractVersionId = @ContractVersionId
    AND cc.IsActive = 1
    return @ret

しかし、最小値を取得するために int にキャストするのは高価ですか? 代わりに、次のように基づいてクエリを実行する必要があります。

(count(Id) where IsCapped = 0 > 0) 複数のキャストを行うのではなく、false を返しますか?

実行計画では、この関数の呼び出しが実行に重いようには見えません (しかし、私はクエリ計画の分析にあまり慣れていません - 2 のようなストアド プロシージャの別のセクションと同じ % コストを持っているようです) %)。

編集 - 関数を呼び出すストアド プロシージャを実行して実行プランを確認すると、関数を呼び出す部分のクエリ コストは (バッチと比較して) 1% で、ストアド プロシージャの他のセクションに匹敵します。私が間違ったことを見ていなければ:)

ありがとう!!

4

3 に答える 3

4

IsCapped = 0クエリが常にすべてのデータを読み取るレコードが1つ見つかった瞬間からクエリから飛び出すため、existsステートメントを使用してこれを行います。

CREATE FUNCTION dbo.fn_are_contracts_capped(@ContractVersionId int)
RETURNS bit
WITH SCHEMABINDING
AS
BEGIN
    DECLARE @return_value bit

    IF EXISTS(
      SELECT 1 
        FROM dbo.ContractCover cc
        JOIN dbo.ContractRiskVersion crv 
          ON cc.ContractRiskId = crv.ContractRiskId
       WHERE crv.ContractVersionId = @ContractVersionId
         AND cc.IsActive = 1
         AND IsCapped = 0)
      BEGIN
        SET @return_value = 0
      END
    ELSE
      BEGIN
        SET @return_value = 1
      END

    RETURN @return_value
  END

データの読み取りに必要な IO と比較すると、キャストによって多くのオーバーヘッドが追加されることはありません。

編集:コードをスカラー関数にラップしました。

于 2012-06-01T07:39:17.210 に答える
2

にキャストすると、 SELECTCPUとメモリが制限されます。この場合、どれだけかはわかりません。通常の状況では、通常、最初にIOを最適化してから、次にCPUとメモリについて心配します。ですから、私はあなたに明確な答えを持っていません。

とは言うものの、あなたの問題に対するこの特定の解決策の問題は、それが短絡しないということです。SQL Serverは、ContractVersionId=@ContractVersionIdおよびIsActive=1であるすべての行を読み取り、IsCappedをINTに変換し、最小値を取ります。実際には、IsCapped=0である単一の行を見つけたらすぐに終了できます。 ContactVersionIdの選択性が高く、テーブルのごく一部しか返さない場合、またはほとんどの行に上限が設定されている場合は、非常に重要です。ただし、ContactVersionIdの選択性が高くない場合、または行の高い割合で上限が設定されていない場合は、SQLServerに多くの作業を実行するように要求しています。

2番目の考慮事項は、スカラー値関数はSQLServerでの悪名高いパフォーマンスの低下であるということです。可能であれば、インラインテーブル関数として作成することをお勧めします。例:

create function AreAllCapped(@ContractVersionId int)
returns table as return (
select 
  ContractVersionId = @ContractVersionId 
, AreAllCapped = case when exists (
      select * 
      from ContractCover cc 
      join ContractRiskVersion crv on cc.ContractRiskId = crv.ContractRiskId
      where crv.ContractVersionId = @ContractVersionId
        and cc.IsActive = 1
        and IsCapped = 0
      ) 
    then 0 else 1 end
)

次に、FROM句で使用して呼び出すことができますCROSS APPLY(SQL 2005以降を想定)。

最後の注意:IsCapped = 0の場合にカウントすると、同様の問題が発生します。慣れている場合は、LINQのAny()とCount()の違いのようです。Any()は短絡し、Count()は実際にすべての要素をカウントする必要があります。先に進む必要があるのは1行だけですが、それでもすべての行SELECT COUNT(*) ... WHERE IsCapped = 0カウントする必要があります。

于 2012-06-01T13:19:41.540 に答える
1

もちろん、bit列を引数として集計関数に渡すことができないことは既知の事実です (したがって、渡す必要がある場合は、最初に整数としてキャストする必要があります)。ただし、bit列は並べ替えることができます。に。したがって、クエリは次のように書き直すことができます。

SELECT TOP 1 @Ret = IsCapped
FROM ContractCover cc
INNER JOIN ContractRiskVersion crv on cc.ContractRiskId = crv.ContractRiskId
WHERE crv.ContractVersionId = @ContractVersionId
  AND cc.IsActive = 1
ORDER BY IsCapped;

この特定のクエリでは、 をIsCappedNULL にすることはできないと想定されていることに注意してください。可能であれば、追加のフィルターを WHERE 句に追加する必要があります。

AND IsCapped IS NOT NULL

もちろん、実際に 0 ではなく NULL を返したい場合を除きます。

キャスティングのコストに関しては、Filip と Peter が既に述べたことに追加するものは何もありません。データを集計する前にキャストする必要があるのは面倒bitですが、それは決して主要な関心事ではありません。

于 2012-06-01T18:27:51.497 に答える