40

これが私の問題です-私は2つのテーブルを持っています:

  1. WORKER、列|ID|OTHER_STAF|あり、IDは主キーです
  2. FIRM、列|FPK|ID|SOMETHING_ELSE|あり。FPKとIDの組み合わせが主キーになり、IDはWORKER.IDを参照する外部キーです(nullではなく、WORKERと同じ値である必要があります)。

UPDATE_ID_WORKERWORKERの特定のIDの値を変更したいストアドプロシージャを作成したいのですが、FIRMの特定のIDの値のすべてのインスタンスでも変更したいと思います。

4

6 に答える 6

47

実際にはこれを行うべきではありませんが、代わりに新しいレコードを挿入して、そのように更新してください。
ただし、本当に必要な場合は、次の操作を実行できます。

  • FK 制約の適用を一時的に無効にする (例ALTER TABLE foo WITH NOCHECK CONSTRAINT ALL)
  • 次に、PK を更新します。
  • 次に、PK の変更に合わせて FK を更新します。
  • 最後に、バック エンフォーシング FK 制約を有効にします
于 2010-03-23T11:10:25.517 に答える
34

まず、主キーを形成するために安定した (静的ではない) データ列を選択します。これは、リレーショナル データベース (参照がキーによって行われる) でキーを更新することを避けたいからです。

  1. この問題では、キーがリレーショナル キー (「データから構成された」) であり、リレーショナル インテグリティ、パワー、およびスピードを備えているかどうか、または「キー」がレコード ID であり、何もないかどうかは関係ありません。その関係の整合性、パワー、スピードの。効果は同じです。

  2. 私がこれを述べるのは、無知な人たちによる多くの投稿があり、これがレコード ID がリレーショナル キーよりも優れている正確な理由であると示唆しているからです。

  3. ポイントは、参照が必要な場所にキーまたはレコード ID が移行されることです。

次に、キーまたはレコード ID の値を変更する必要がある場合は、それを変更する必要があります。OLTP 規格に準拠した方法は次のとおりです。ハイエンド ベンダーは「カスケード アップデート」を許可していないことに注意してください。

  • プロシージャを書きます。Foo_UpdateCascade_tr @ID、Foo はテーブル名

  • 取引を開始する

  • 最初に、新しいキーまたは RID 値を使用して、古い行から親テーブルの新しい行を INSERT-SELECT

  • 次に、すべての子テーブルについて、上から下へ、新しいキーまたは RID 値を使用して、古い行から新しい行を INSERT-SELECT します。

  • 3 番目に、古いキーまたは RID 値を持つ子テーブルの行を下から上に削除します。

  • 最後に、古いキーまたは RID 値を持つ親テーブルの行を削除します

  • トランザクションをコミットする

他の回答を見る

他の答えは間違っています。

  • 必要な行 (親とすべての子) を UPDAT した後に、制約を無効にしてから有効にすることは、雇用を維持したい場合、オンライン実稼働環境で行うことではありません。このアドバイスは、シングル ユーザー データベースに適しています。

  • キーまたは RID の値を変更する必要があることは、設計上の欠陥を示すものではありません。それは普通の必要性です。これは、安定した (静的ではない) キーを選択することで緩和されます。軽減することはできますが、なくすことはできません。

  • 自然キーを代用するサロゲートは、何の違いもありません。あなたが与えた例では、「キー」はサロゲートです。そして、更新する必要があります。

    • それぞれの単語が互いに矛盾するため、「代理キー」のようなものはありません。(データから作成された) キーであるか、そうでないかのいずれかです。サロゲートはデータから構成されるのではなく、明示的に非データです。キーのプロパティはありません。
  • 必要なすべての変更をカスケードすることについて、「トリッキー」なことは何もありません。上記の手順を参照してください。

  • 宇宙の変化を阻止できるものは何もありません。それは変わる。それに対処します。データベースは宇宙に関する事実の集まりなので、宇宙が変わればデータベースも変わらなければなりません。それは大都市での生活であり、新しいプレイヤー向けではありません。

  • 人々が結婚したり、ハリネズミが埋葬されたりすることは問題ではありません (そのような例が問題であることを示唆するために使用されているにもかかわらず)。名前をキーとして使用しないためです。ユニバース内のデータを識別するために使用されるような、小さくて安定した識別子を使用します。

    • 名前、説明などは、1 つの行に一度だけ存在します。キーは、移行された場所に存在します。また、「キー」が RID の場合、RID も移行された場所に存在します。
  • PK を更新しないでください。最近読んだ中で 2 番目に面白いものです。 新しい列を追加するのが一番です。

于 2015-08-10T12:40:23.587 に答える
7

主キーの値と一致するすべての外部キーを更新する必要がある場合は、設計全体を修正する必要があります。

必要なすべての外部キーの変更をカスケードするのは困難です。主キーを更新しないことをお勧めします。更新が必要な場合はSurrogate Primary Key、アプリケーション データから派生していないキーである を使用する必要があります。その結果、その値はビジネス ロジックとは無関係であり、変更する必要はありません (また、エンド ユーザーには見えないようにする必要があります)。その後、他の列を更新して表示できます。

例えば:

BadUserTable
UserID     varchar(20) primary key --user last name
other columns...

UserID への FK を持つ多くのテーブルを作成して、ユーザーが作業したすべてを追跡する場合、そのユーザーが結婚し、ID を新しい姓と一致させたい場合は、うまくいきません。

GoodUserTable
UserID    int identity(1,1) primary key
UserLogin varchar(20) 
other columns....

サロゲート主キーを他のすべてのテーブルに FK し、必要に応じて UserLogin を表示し、その値を使用してログインできるようにし、変更する必要がある場合は、1 行の 1 列のみで変更します。

于 2010-03-23T11:39:51.980 に答える
5

主キーを更新しないでください。データを参照している他のテーブルがある場合、データをそのまま維持することで多くの問題が発生する可能性があります。

理想的には、更新可能な一意のフィールドが必要な場合は、新しいフィールドを作成します。

于 2010-03-23T10:58:13.000 に答える
1

この再帰関数を使用して、必要な T-SQL スクリプトを生成できます。

CREATE FUNCTION dbo.Update_Delete_PrimaryKey
(
    @TableName      NVARCHAR(255),
    @ColumnName     NVARCHAR(255),
    @OldValue       NVARCHAR(MAX),
    @NewValue       NVARCHAR(MAX),
    @Del            BIT
)
RETURNS NVARCHAR 
(
    MAX
)
AS
BEGIN
    DECLARE @fks TABLE 
            (
                constraint_name NVARCHAR(255),
                table_name NVARCHAR(255),
                col NVARCHAR(255)
            );
    DECLARE @Sql                  NVARCHAR(MAX),
            @EnableConstraints     NVARCHAR(MAX);

    SET @Sql = '';
    SET @EnableConstraints = '';

    INSERT INTO @fks
      (
        constraint_name,
        table_name,
        col
      )
    SELECT oConstraint.name     constraint_name,
           oParent.name         table_name,
           oParentCol.name      col
    FROM   sys.foreign_key_columns sfkc
           --INNER JOIN sys.foreign_keys sfk
           --     ON  sfk.[object_id] = sfkc.constraint_object_id

           INNER JOIN sys.sysobjects oConstraint
                ON  sfkc.constraint_object_id = oConstraint.id
           INNER JOIN sys.sysobjects oParent
                ON  sfkc.parent_object_id = oParent.id
           INNER JOIN sys.all_columns oParentCol
                ON  sfkc.parent_object_id = oParentCol.object_id
                AND sfkc.parent_column_id = oParentCol.column_id
           INNER JOIN sys.sysobjects oReference
                ON  sfkc.referenced_object_id = oReference.id
           INNER JOIN sys.all_columns oReferenceCol
                ON  sfkc.referenced_object_id = oReferenceCol.object_id
                AND sfkc.referenced_column_id = oReferenceCol.column_id
    WHERE  oReference.name = @TableName
           AND oReferenceCol.name = @ColumnName
    --AND (@Del <> 1 OR sfk.delete_referential_action = 0)
    --AND (@Del = 1 OR sfk.update_referential_action = 0)

    IF EXISTS(
           SELECT 1
           FROM   @fks
       )
    BEGIN
        DECLARE @Constraint     NVARCHAR(255),
                @Table          NVARCHAR(255),
                @Col            NVARCHAR(255)  

        DECLARE Table_Cursor CURSOR LOCAL 
        FOR
            SELECT f.constraint_name,
                   f.table_name,
                   f.col
            FROM   @fks AS f

        OPEN Table_Cursor FETCH NEXT FROM Table_Cursor INTO @Constraint, @Table,@Col  
        WHILE (@@FETCH_STATUS = 0)
        BEGIN
            IF @Del <> 1
            BEGIN
                SET @Sql = @Sql + 'ALTER TABLE ' + @Table + ' NOCHECK CONSTRAINT ' + @Constraint + CHAR(13) + CHAR(10);
                SET @EnableConstraints = @EnableConstraints + 'ALTER TABLE ' + @Table + ' CHECK CONSTRAINT ' + @Constraint 
                    + CHAR(13) + CHAR(10);
            END

            SET @Sql = @Sql + dbo.Update_Delete_PrimaryKey(@Table, @Col, @OldValue, @NewValue, @Del);
            FETCH NEXT FROM Table_Cursor INTO @Constraint, @Table,@Col
        END

        CLOSE Table_Cursor DEALLOCATE Table_Cursor
    END

    DECLARE @DataType NVARCHAR(30);
    SELECT @DataType = t.name +
           CASE 
                WHEN t.name IN ('char', 'varchar', 'nchar', 'nvarchar') THEN '(' +
                     CASE 
                          WHEN c.max_length = -1 THEN 'MAX'
                          ELSE CONVERT(
                                   VARCHAR(4),
                                   CASE 
                                        WHEN t.name IN ('nchar', 'nvarchar') THEN c.max_length / 2
                                        ELSE c.max_length
                                   END
                               )
                     END + ')'
                WHEN t.name IN ('decimal', 'numeric') THEN '(' + CONVERT(VARCHAR(4), c.precision) + ',' 
                     + CONVERT(VARCHAR(4), c.Scale) + ')'
                ELSE ''
           END
    FROM   sys.columns c
           INNER JOIN sys.types t
                ON  c.user_type_id = t.user_type_id
    WHERE  c.object_id = OBJECT_ID(@TableName)
           AND c.name = @ColumnName

    IF @Del <> 1
    BEGIN
        SET @Sql = @Sql + 'UPDATE [' + @TableName + '] SET [' + @ColumnName + '] = CONVERT(' + @DataType + ', ' + ISNULL('N''' + @NewValue + '''', 'NULL') 
            + ') WHERE [' + @ColumnName + '] = CONVERT(' + @DataType + ', ' + ISNULL('N''' + @OldValue + '''', 'NULL') +
            ');' + CHAR(13) + CHAR(10);
        SET @Sql = @Sql + @EnableConstraints;
    END
    ELSE
        SET @Sql = @Sql + 'DELETE [' + @TableName + '] WHERE [' + @ColumnName + '] = CONVERT(' + @DataType + ', N''' + @OldValue 
            + ''');' + CHAR(13) + CHAR(10);
    RETURN @Sql;
END
GO

DECLARE @Result NVARCHAR(MAX);
SET @Result = dbo.Update_Delete_PrimaryKey('@TableName', '@ColumnName', '@OldValue', '@NewValue', 0);/*Update*/
EXEC (@Result)
SET @Result = dbo.Update_Delete_PrimaryKey('@TableName', '@ColumnName', '@OldValue', NULL, 1);/*Delete*/
EXEC (@Result)
GO

DROP FUNCTION Update_Delete_PrimaryKey;
于 2016-08-07T18:32:03.690 に答える