2

有名なNortwindデータベースを見てみましょう。私が実行するとしますDELETE FROM Clients

MSAccessでは、参照整合性制約のあるテーブルに対してDELETEステートメントを実行すると、Jetは削除できるレコードを削除し、他のレコードは残します。この場合、Clientsがないものだけを削除しOrdersます。
SQL Serverでは、これを実行すると失敗するように見え、そのことを示すメッセージが表示されThe DELETE statement conflicted with the REFERENCE constraint ....ます。

したがって、私の質問は次のとおりです。SQLServerに、削除可能なレコードだけを削除させる簡単な方法はありますか?または、追加する必要がありWHERE ClientId NOT IN (SELECT Id FROM Clients)ますか?
つまり、SQL ServerDELETEをJetのように機能させることはできますDELETEか?

情報:私はそれほど怠惰ではありませんが、そこには多くの制約があり、コードを単純に保ちたいと思います...

4

6 に答える 6

1

オプションは次のとおりです。

  1. カスケード削除-これらのクライアントに依存するすべてのレコードを削除します。
  2. ドロップ制約(これはお勧めしません:))
  3. 削除可能であり、競合が発生しないことを事前に確認してください
于 2012-04-12T15:06:09.070 に答える
1

FK参照を持つ行を残したい場合は、いくつかのオプションしかありませんが、どれもきれいではありません。

  1. 削除を行う前に制約を確認してください
  2. 質問で述べたように、FKのwhere句が含まれるようにクエリを変更します
  3. ロジックを変更して、一度に1行ずつ削除し、各行をコミットして、失敗した場合は削除をロールバックします。

「最もお粗末な」オプションは、実際には、そこにあるFKの数、削除する行の数、および行にFKの依存関係がある可能性に依存します。それが比較的まれなイベントである場合は、オプション#3が最適かもしれませんが、最初の2つのオプションに傾倒する傾向があります。

于 2012-04-12T15:15:37.047 に答える
1

別のアプローチは、参照された行の削除に関する問題を無視するために、ブロックをCURSOR使用してループ内で(行ごとに)削除することです。TRY .. CATCH

このアプローチでは、既存および将来の制約をモデル化する必要はありません。

例:

SET NOCOUNT ON; -- use not to have "(N row(s) affected)" for each deleted row

DECLARE del_cursor CURSOR
FOR SELECT ClientID FROM Clients

DECLARE @CurrentClientID INT -- use your proper type
DECLARE @message VARCHAR(200) -- just for building messages

OPEN del_cursor

FETCH NEXT FROM del_cursor
INTO @CurrentClientID

WHILE @@FETCH_STATUS = 0
BEGIN
    BEGIN TRY
        DELETE FROM Clients WHERE CURRENT OF del_cursor
    END TRY
    BEGIN CATCH
        SET @message = 'Row ' + CAST(@CurrentClientID AS VARCHAR) + ' cannot be deleted - skipping.'
        PRINT @message
    END CATCH

    FETCH NEXT FROM del_cursor
    INTO @CurrentClientID
END

CLOSE del_cursor
DEALLOCATE del_cursor

上記の例をラップして、代わりにCREATE PROCEDURE DeleteClients使用することができますEXEC DeleteClientsDELETE FROM Clients

于 2012-04-12T18:28:33.603 に答える
0

私はそれが少し複雑な解決策であることを知っていますが、あなたは一度だけそれをする必要があります。

削除を防ぐためのトリガーはどうですか?

create table parent(
id int not null primary key
)
create table son(
id int not null primary key,
idparent int)

alter table son add foreign key(idparent) references parent(id)

insert into parent values(1)
insert into parent values(2)
insert into parent values(3)

insert into son values(1,1)

--select * from parent
--select * from son

create trigger preventDelete
on parent
instead of delete
as
begin
  delete from parent where id not in (select idparent from son) and id in (select id from deleted)
end

delete from parent

レコード2と3は削除されます

于 2012-04-12T15:18:17.727 に答える
0

これを試してください、それはあなたのステートメントを生成します。私がそれを非常に速く書いたフォーマットをすみません:

DECLARE @i INT, @SQL NVARCHAR(2000), @TABLENAME NVARCHAR(100)
SET @i = 1
SET @TABLENAME = 'informer_alert'
SET @SQL = ''

DECLARE @col VARCHAR(50), @basecol VARCHAR(50), @tname VARCHAR(50)

DECLARE @TABLE TABLE ([table] VARCHAR(50), col VARCHAR(50), basecol VARCHAR(50))
INSERT INTO @TABLE
SELECT t.name, sc.name, sc2.name
FROM sys.foreign_key_columns fk
INNER JOIN sys.tables t ON fk.parent_object_id = t.OBJECT_ID
INNER JOIN syscolumns sc ON fk.parent_column_id = sc.colorder
AND sc.id = fk.parent_object_id
INNER JOIN syscolumns sc2 ON fk.referenced_object_id = sc2.id
AND fk.constraint_column_id = sc2.colorder
WHERE fk.referenced_object_id = (SELECT OBJECT_ID 
                                    FROM sys.tables 
                                    WHERE name = @TABLENAME)


WHILE (@i <= (SELECT COUNT(*) FROM @TABLE))
BEGIN

    SELECT @tname = [table], @col = col, @basecol = basecol
    FROM (SELECT    ROW_NUMBER() OVER(ORDER BY col) [N],
            [table], col, basecol
            FROM @TABLE) A
    WHERE A.N = @i

    SET @SQL = @SQL + ' DELETE FROM ' + @TABLENAME + ' WHERE ' + @basecol  + ' NOT IN (SELECT ' + @col+ ' FROM ' + @tname + ')'

    SET @i = @i + 1

END

SELECT @SQL
于 2012-04-12T15:59:13.687 に答える
0
  • コードを単純にしたい場合(あなたが言ったように)CREATE VIEW、テーブルの上に置いてください。
  • WHERE削除可能な行のみを取得する句を定義します。
  • その後、あなたはただDELETE FROM ClientsDeletable
  • 新しいビューのDELETE権限について覚えておいてください。

スクリプトの例:

CREATE VIEW ClientsDeletable
AS
SELECT * 
FROM Clients
WHERE
ClientID NOT IN (SELECT CliID FROM ForeignTab1)
AND
ClientID NOT IN (SELECT CliID FROM ForeignTab2)

JOINを含めることはできないことに注意してください。そうしないと、FROMエラーが発生します。

Msg 4405, Level 16, State 1, Line 1
View or function 'ClientsDeletable' is not updatable because the modification affects multiple base tables.
于 2012-04-12T16:43:05.840 に答える