0

SQL サーバー 2008。

pk id 1 の親行があります。他のすべての DB ユーザーをブロックしている間 (これはクリーンアップ操作であるため、リソースの競合は問題になりません)、新しい行を挿入し、すべての子行を取得してそれらを変更したいと考えています。新しい行への fk 列。たとえば、以下の DDL では、新しい行を挿入し、すべての #chi.parid 値に「3」の値を与えて、基本的に新しい行に属し、古い行を削除できるようにします。

ヘルプ!

create table #par ( parid int identity(1,1) , note varchar(8) )
create table #chi ( chiid int identity(1,1) , parid int , thing varchar(8) )
insert into #par values ( 'note1' )
insert into #par values ( 'note2' )
insert into #chi values ( 1 , 'a' )
insert into #chi values ( 1 , 'b' )
insert into #chi values ( 1 , 'c' )
4

5 に答える 5

3

私は、自然キーまたは FK を優先して代理キーを避ける傾向があります。IDENTITYまた、人工的な識別子は避けます。正直なところ、私は少数派であり、FK を使用して一括挿入を行う方法について疑問に思っていIDENTITYました。

Alan Barkerの回答によると、SCOPE_IDENTITY()このRBARを実行したい場合にのみ利用できます(行を苦しめます)。「これはクリーンアップ操作です」とあなたは言うので、おそらく手続き的な解決策が受け入れられます。

私が自分で問題を回避した方法は、潜在的なIDENTITY値のシーケンスを手動で生成し (たとえば、ステージング テーブルで) SET IDENTITY_INSERT TargetTable ON、値を強制的に使用することです。明らかに、提案された値が実際に使用されないようにする必要があります。他のINSERTすべてのユーザーを引き続きブロックする必要があります。

注目すべき点がいくつかあります。列の必須のUNIQUE制約IDENTITYが欠落している場合があるため、衝突がないことを自分で確認する必要がある場合があります。また、サロゲートが好きな人は、値がシーケンシャルでない (そして正の範囲にある!) 場合、またはさらに悪いことに、完全な値に依存するアプリケーション ロジックがある場合、少し「混乱」する可能性があることを発見しました。シーケンスまたは IDENTITY値をビジネスに公開しました (この場合、注文番号などの「偽造」エンタープライズ キー値は、実際の監査人に狙われる可能性があります)。

編集: 今朝別の SO の質問への回答を読んで、テーブル内のOUTPUTすべての自動生成された値をキャプチャする SQL Server 2008 の句について思い出しました。IDENTITY

CREATE TABLE #InsertedBooks
(
 ID INTEGER NOT NULL UNIQUE, -- surrogate
 isbn_13 CHAR(13) NOT NULL UNIQUE -- natural key
);

WITH InsertingBooks (isbn_13)
AS 
(
 SELECT '9781590597453'
 UNION ALL
 SELECT '9780596523060'
 UNION ALL
 SELECT '9780192801425'
)
INSERT INTO Books (isbn_13)
  OUTPUT inserted.ID, inserted.isbn_13   -- <--
  INTO #InsertedBooks (ID, isbn_13)      -- <--
SELECT isbn_13
  FROM InsertingBooks;

INSERT INTO AnotherTable...
SELECT T1.ID, ...
  FROM #InsertedBooks AS T1...;

DROP TABLE #InsertedBooks
于 2010-07-12T07:32:47.623 に答える
1

次のような更新が必要だと思います:

UPDATE chi SET parid = 2 WHERE parid = 1

ここでは FKeys は問題になりません。

于 2010-07-12T02:47:48.670 に答える
0

Kalium のソリューションに加えて、SCOPE_IDENTITY() 関数を使用して、最後のテーブル挿入の IDENTITY 値を取得できます。

begin tran    

   insert into #par values ('New Parent')  

   update #chi set parid= SCOPE_IDENTITY()

   delete from #par where parid = <OLD_ID>    

commit

このようにして、これをストアド プロシージャとしてコーディングして、すべてを実行できます。

CREATE PROCEDURE CleanUp @newNote varchar(8), @IDToDelete int 
AS
BEGIN

    BEGIN TRAN

    INSERT INTO #par VALUES (@newNote)      
    UPDATE #chi SET parid= SCOPE_IDENTITY()    
    DELETE FROM #par WHERE parid = @IDToDelete

    COMMIT
END

そして簡単に:

exec CleanUp 'Alan',1
于 2010-07-12T03:08:02.800 に答える
0

その場合は、単に Cursor を使用できます。パフォーマンスには最適ではありませんが、とにかくこれはダウンタイムのクリーンアップ ジョブのようです。

CREATE PROCEDURE CleanUp
AS
BEGIN

    -- BUILD YOUR TEMP TABLE(S) HERE:
    --
    --

    DECLARE @delete_parent_id int;

    -- SELECT ON TEMP TABLE (AS A CURSOR):
    -- Put your specific Select statement here:
    DECLARE delete_cursor CURSOR FOR 
    SELECT parid
    FROM #TEMPTABLE
    WHERE <...> ;


    OPEN delete_cursor;

    BEGIN TRAN
        -- Loop round each selected parent, create new parent, update children and delete old parent.
        FETCH NEXT FROM delete_cursor 
        INTO @delete_parent_id;

        WHILE @@FETCH_STATUS = 0
        BEGIN

            INSERT INTO #par VALUES ('Some new Text') --New Parent Row     
            UPDATE #chi SET parid= SCOPE_IDENTITY() where parid = @delete_parent_id   -- Adjust FK ref on child
            DELETE FROM #par WHERE parid = @delete_parent_id -- delete old parent.

            FETCH NEXT FROM delete_cursor 
            INTO @delete_parent_id;
        END

    COMMIT

    CLOSE delete_cursor;
    DEALLOCATE delete_cursor;
END
于 2010-07-13T03:06:18.293 に答える
0

ご意見ありがとうございます。これに対して、SQL Svr 2008 でセットベースの操作を「できない」ように見えるので、ループを使用して RBAR ソリューションを実行しました (カーソルよりもパフォーマンスが優れていると思います)。try..catch でこれをより安全にすることについてコメントできる人、またはセットでこれを行うことについてもっと教えてくれる人は、コメントしてください。:)

ありがとう。

    Select 
                [parid]
            ,   [name]
    Into    #redo
    From    partable 
    Where   DateDiff( Hour , donewhen ,SysDateTimeOffset() ) > 23
Begin Transaction
Declare @rows int  = ( Select COUNT(*) From #redo )
Declare @parid int 
Create Clustered Index redoix on #redo([parid]) With FillFactor = 100
While @rows > 0
Begin
    Select Top 1 @parid = [parid] from #redo Order By parid Asc

        Insert partable 
            (
                [name]
            )
        Select 
                [name]      
        From #redo 
        Where parid = @parid
        Update chitable
            Set parid = Scope_Identity()
            Where   parid = @parid
        Delete From partable
            Where   parid = @parid

    Delete from #redo where [parid] = @parid 
    Set @rows  = ( Select COUNT(*) From #redo )
End
Commit Transaction
于 2010-07-20T13:56:11.507 に答える