0

前回のチェック(1日1回)以降に何かが変更された場合にユーザーに情報メッセージを追加するために、特定のクエリ(ちなみに複数のテーブルが含まれています)を定期的にチェックしています。

私はそれをで動作させようとしましたchecksum_agg(binary_checksum(*))が、それは役に立たないので、私は次のケース(過度に単純化されている)があるので、この質問はあまり役に立ちません:

select  checksum_agg(binary_checksum(*))
from    
(
    select  1 as id,
            1 as status

    union all

    select  2 as id,
            0 as status
) data

select  checksum_agg(binary_checksum(*))
from    
(
    select  1 as id,
            0 as status

    union all

    select  2 as id,
            1 as status
) data

上記の両方のケースで同じチェックサム49が得られ、データが変更されていることは明らかです。

これは単純な関数や単純なソリューションである必要はありませんが、SQLServer2000でこれらのような違いを一意に識別する方法が必要です。

4

1 に答える 1

1

checksum_agg は、binary_checksum の結果をすべての行に対して単純に加算しているように見えます。各行は変更されていますが、2 つのチェックサムの合計は変更されていません (つまり、17+32 = 16+33)。これは実際には更新を確認するための標準ではありませんが、私が思いつく推奨事項は次のとおりです。

  1. を使用する代わりにchecksum_agg、チェックサムを区切られた文字列に連結し、文字列を比較しますSELECT binary_checksum(*) + ',' FROM MyTable FOR XML PATH('')。チェックして保存する文字列ははるかに長くなりますが、誤検出の可能性ははるかに低くなります。
  2. 組み込みのチェックサム ルーチンを使用する代わりに、HASHBYTES を使用して 8000 バイト ブロックで MD5 チェックサムを計算し、結果を xor します。これにより、チェックサムの弾力性が大幅に向上しますが、完全な防御ではありません (つまり、誤った一致が得られる可能性はありますが、その可能性は非常に低くなります)。以下に書いた HASHBYTES デモコードを貼り付けます。
  3. 最後のオプションであり、絶対的な最後の手段は、実際にテーブル table を XML 形式で保存し、それを比較することです。これは、誤った一致がないことを完全に確認できる唯一の方法ですが、スケーラブルではなく、大量のデータを保存して比較する必要があります。

あなたが始めたものを含め、すべてのアプローチには長所と短所があり、さまざまな程度のデータサイズと精度に対する処理要件があります。必要な精度のレベルに応じて、適切なオプションを使用してください。100% の精度を得る唯一の方法は、すべてのテーブル データを保存することです。

別の方法として、各テーブルに date_modified フィールドを追加することもできます。このフィールドは、after insert および update トリガーを使用して GetDate() に設定されます。できますSELECT COUNT(*) FROM #test WHERE date_modified > @date_last_checked。これは、更新を確認するためのより一般的な方法です。これの欠点は、削除を追跡できないことです。

もう 1 つの方法は、追跡するテーブルごとに 1 つの行を含む、table_name (VARCHAR) フィールドと is_modified (BIT) フィールドを使用して、変更されたテーブルを作成することです。挿入、更新、および削除のトリガーを使用して、関連するテーブルに対するフラグを True に設定します。スケジュールを実行するときは、(同じトランザクションで) is_modified フラグを確認してリセットします - 次の行に沿ってSELECT @is_modified = is_modified, is_modified = 0 FROM tblModified

次のスクリプトは、3 つの結果セットを生成します。それぞれが、この応答で前述した番号付きリストに対応しています。SELECT ステートメントの直前に、どの出力がどのオプションに対応するかをコメントしました。出力がどのように導出されたかを確認するには、コードをさかのぼって作業します。

-- Create the test table and populate it
CREATE TABLE #Test (
    f1 INT,
    f2 INT
)
INSERT INTO #Test VALUES(1, 1)
INSERT INTO #Test VALUES(2, 0)
INSERT INTO #Test VALUES(2, 1)

/*******************
OPTION 1
*******************/
SELECT CAST(binary_checksum(*) AS VARCHAR) + ',' FROM #test FOR XML PATH('')

-- Declaration: Input and output MD5 checksums (@in and @out), input string (@input), and counter (@i)
DECLARE @in VARBINARY(16), @out VARBINARY(16), @input VARCHAR(MAX), @i INT

-- Initialize @input string as the XML dump of the table
-- Use this as your comparison string if you choose to not use the MD5 checksum
SET @input = (SELECT * FROM #Test FOR XML RAW)

/*******************
OPTION 3
*******************/
SELECT @input

-- Initialise counter and output MD5.
SET @i = 1
SET @out = 0x00000000000000000000000000000000
WHILE @i <= LEN(@input)
BEGIN
    -- calculate MD5 for this batch
    SET @in = HASHBYTES('MD5', SUBSTRING(@input, @i, CASE WHEN LEN(@input) - @i > 8000 THEN 8000 ELSE LEN(@input) - @i END))
    -- xor the results with the output
    SET @out = CAST(CAST(SUBSTRING(@in, 1, 4) AS INT) ^ CAST(SUBSTRING(@out, 1, 4) AS INT) AS VARBINARY(4)) +
        CAST(CAST(SUBSTRING(@in, 5, 4) AS INT) ^ CAST(SUBSTRING(@out, 5, 4) AS INT) AS VARBINARY(4)) +
        CAST(CAST(SUBSTRING(@in, 9, 4) AS INT) ^ CAST(SUBSTRING(@out, 9, 4) AS INT) AS VARBINARY(4)) +
        CAST(CAST(SUBSTRING(@in, 13, 4) AS INT) ^ CAST(SUBSTRING(@out, 13, 4) AS INT) AS VARBINARY(4))
    SET @i = @i + 8000
END

/*******************
OPTION 2
*******************/
SELECT @out
于 2012-05-03T13:22:49.323 に答える