web-dev-qa-db-ja.com

DMLステートメントを検証することは可能ですか?

'abccba'という名前のユーザーをデータベースから削除するとします。したがって、次のクエリを実行します。

DELETE FROM table WHERE fname = 'abc' AND last name = 'cba'; commit;

今、私が知らないこの名前のユーザーが増える可能性があります。当然の選択は、事前にselectステートメントを実行して、本当に削除したいものを削除するかどうかを確認することですが、私はまだ人間であり、それを忘れて、失われたデータを取得するためにバックアップを検索することになります。

私の質問は、DMLステートメントを実行しないステートメントに特定のルールを課す方法はありますか(たとえば、削除された行数が特定のしきい値を超えた場合は削除を実行しないでください)?

1
kyooryu

このようなルールをdbレベルに課すことは、リソースの点で非常にコストがかかる可能性があるため、どこでも機能する標準的な方法はないと思います。

BEFORE DELETEトリガー(またはAFTER DELETEですが、この場合はBEFOREトリガーの方が適切なようです)の本体内でエラーを発生させるよりも良い方法を提案することはできません。残念ながら、それはRDMSにかなり依存しています。たとえば、Oracleでは、トリガー内の同じテーブルにアクセスする場合、ステートメントレベルのトリガーを使用する必要があります。 SQLServerは、このようなトリガーをBEFOREではなくINSTEAD OFと呼びます。

3
a1ex07

私はSQLServerについてのみ話すことができますが、これを行うことができる唯一の方法は、DMLトリガーを使用することです(私が知る限り)。以下を例にとってみましょう。

use TestDB;
go

create table dbo.TestTriggerTable
(
    id int identity(1, 1) not null,
    some_int int not null
        default 1
);
go

insert into dbo.TestTriggerTable
default values;
go 1000

create trigger DeleteMaxRows
on dbo.TestTriggerTable
after delete
as

    if (select count(*) from deleted) > 10
    begin
        raiserror('Cannot delete more than 10 rows', 16, 1);
        rollback;
    end
go

select *
from dbo.TestTriggerTable;

-- this fails and rolls back
delete from dbo.TestTriggerTable
where id < 20;
go

-- this is successful
delete from dbo.TestTriggerTable
where id < 8;
go

ただし、このアプローチにはいくつかの問題があり、これが優れた設計であるとは思いません。まず、トリガー内にハードコードされた要件があります(設定された行数を実行しているのか、行のパーセンテージを実行しているのか...それはまだハードコードされています)。異なるロジックで再試行できるように、それに応じてアプリケーションを設計する必要があります。

つまり、データベースが正しく設計され、DMLステートメントが正しく構築されて、削除された行の行数を確認する代わりに、変更されるデータが目的のデータであることを確認できます。

私の意見では、このチェックが不要になるように、再設計の方法を理解するために設計時間を費やす必要があります。

5
Thomas Stringer