3

Oracle または Postgresql ではないデータベースを使用しています。つまり、遅延制約にアクセスできません。つまり、制約は (コミット時だけでなく) 常に有効でなければなりません。

次のように、リンクされたリスト型の構造をデータベースに保存しているとしましょう。

id     parentId
---------------
1      null
2      1
3      2
4      3
5      4
6      5

parentIdは への外部キー参照idであり、制約によって一意である必要があります。

5itemを item の直前に移動したい1場合、DB は次のようになります。

id     parentId
---------------
1      null
2      5 <-- different
3      2
4      3
5      1 <-- different
6      4 <-- different

3 つの更新ステートメントである 3 つの行を変更する必要があります。これらの update ステートメントのいずれかが制約違反を引き起こします。制約が再び有効になる前に、3 つのステートメントすべてが完了する必要があります。

私の質問は、一意性制約に違反しない最善の方法は何ですか?

現在、2 つの異なる解決策を思いつくことができますが、どちらも好きではありません。

  • 影響parentIdを受けるそれぞれをに設定し、3 つの更新をnull実行します。
  • データ モデルを完全に変更して、この種の問題が問題にならない「コピー オン ライト」スタイルのバージョン管理されたデータベースにします。
4

1 に答える 1

0

これは、単一のクエリで実行できます。これにはたくさんのバリエーションがあると思いますが、これが私が使用するものです...

DECLARE
  @node_id         INT,
  @new_parent_id   INT
SELECT
  @node_id         = 5,
  @new_parent      = 1

UPDATE
  yourTable
SET
  parent_id = CASE WHEN yourTable.id = target_node.id    THEN new_antiscendant.id
                   WHEN yourTable.id = descendant.id     THEN target_node.parent_id
                   WHEN yourTable.id = new_descendant.id THEN target_node.id
              END
FROM
  yourTable          AS target_node
LEFT JOIN
  yourTable          AS descendant
    ON descendant.parent_id = target_node.id
LEFT JOIN
  yourTable          AS new_antiscendant
    ON new_antiscendant.id = @new_parent_id
LEFT JOIN
  yourTable          AS new_descendant
    ON COALESCE(new_descendant.parent_id, -1) = COALESCE(new_antiscendant.id, -1)
INNER JOIN
  yourTable
    ON yourTable.id IN (target_node.id, descendant.id, new_descendant.id)
WHERE
  target_node.id = @node_id

これは、@ new_parent_idがNULLまたはリストの最後のレコードであっても、機能します。

MySQLは更新での自己結合を好まないため、アプローチはおそらく、新しいマッピングを取得するために一時テーブルにLEFTJOINを実行することです。次に、そのテーブルに参加して、1つのクエリで3つのレコードすべてを更新します。

INSERT INTO
  yourTempTable
SELECT
  yourTable.id    AS node_id,
  CASE WHEN yourTable.id = target_node.id    THEN new_antiscendant.id
       WHEN yourTable.id = descendant.id     THEN target_node.parent_id
       WHEN yourTable.id = new_descendant.id THEN target_node.id
  END             AS new_parent_id
FROM
  yourTable          AS target_node
LEFT JOIN
  yourTable          AS descendant
    ON descendant.parent_id = target_node.id
LEFT JOIN
  yourTable          AS new_antiscendant
    ON new_antiscendant.id = @new_parent_id
LEFT JOIN
  yourTable          AS new_descendant
    ON COALESCE(new_descendant.parent_id, -1) = COALESCE(new_antiscendant.id, -1)
INNER JOIN
  yourTable
    ON yourTable.id IN (target_node.id, descendant.id, new_descendant.id)
WHERE
  target_node.id = @node_id

UPDATE
  yourTable
SET
  parent_id = yourTempTable.newParentID
FROM
  yourTable
INNER JOIN
  yourTempTable
    ON yourTempTamp.node_id = yourTable.id

(正確な構文はRDBMSによって異なります。)

于 2012-06-19T12:34:15.843 に答える