6

mysql データベース テーブルから次の結果セットがあるとします。

+----+------+-------+
| ID | type | value |
+----+------+-------+
|  8 |    A |  1435 |
|  9 |    B |  7348 | 
| 10 |    A |  1347 | 
| 11 |    A |  3478 | 
| 12 |    A |  4589 | 
| 13 |    B |  6789 |
+----+------+-------+

行 ID 8 を削除し、フィールド「値」の値を押し下げて、すべての行が前のエントリの値を持つようにしたいのですが、フィールド「タイプ」が行と同じものにのみ影響します削除されます (この場合は「A」)。

つまり、行 ID 8 を削除すると、最終的に次のようになります。

+----+------+-------+
| ID | type | value |
+----+------+-------+
|  - |    - |    -  | *
|  9 |    B |  7348 |   |
| 10 |    A |  1435 | * |
| 11 |    A |  1347 | * |
| 12 |    A |  3478 | * |
| 13 |    B |  6789 |   V
+----+------+-------+

ID 10 は ID 8 から値を継承し、次に ID 11 は ID 10 から値を継承する、というように続きます。ただし、タイプが「B」の行は影響を受けないことに注意してください。

質問: 各行を 1 つずつクエリして更新することなく、この値の「シフト」を実行する方法はありますか? 理想的な世界では、1 つのクエリを実行してシフトを実行し、次に別のクエリを実行して行を削除しますが、これが可能かどうかはよくわかりません。

(また、すべてのアプリケーション ロジックをアプリケーション自体にカプセル化するつもりなので、トリガーを使用したくありません)

4

3 に答える 3

2

このタスクは、ウィンドウ/分析関数を使用して非常に簡単に実行できます。MySQL にはそのような機能がないため、次の制限付きでシミュレートできます。

  • 並べ替えるための一意のフィールドが必要です。

私はidこの目的のために使用しました。

type次のクエリは、パーティション インジケーターとして使用して、テーブル内の各行を列挙します。

SELECT t.id, t.type, t.value,
  (SELECT count(*) FROM testbed WHERE type = t.type AND id <= t.id) AS rownum
  FROM testbed t
 ORDER BY t.type, t.id;

可視性のためだけに追加ORDER BYしましたが、最終的なクエリでは必要ありません。

次のクエリでは、2 つの結果を結合し、必要な方法で「値をシフト」することができます。

SELECT c.id AS c_id, c.type AS c_type, c.value AS c_value,
       p.id AS p_id, p.type AS p_type, p.value AS p_value
  FROM (SELECT t.id, t.type, t.value,
               (SELECT count(*) FROM testbed
                 WHERE type = t.type AND id <= t.id) AS rownum
          FROM testbed t) AS c
  LEFT JOIN (SELECT t.id, t.type, t.value,
                    (SELECT count(*) FROM testbed
                      WHERE type = t.type AND id <= t.id) AS rownum
               FROM testbed t) AS p
         ON c.type = p.type AND c.rownum = p.rownum + 1
 ORDER BY c.type, c.id;

最後に、タスクは次の 2 つのクエリで完了しUPDATEますDELETE

UPDATE testbed AS t
JOIN (
  SELECT c.id AS c_id, c.type AS c_type, c.value AS c_value,
         p.id AS p_id, p.type AS p_type, p.value AS p_value
    FROM (SELECT t.id, t.type, t.value,
                 (SELECT count(*) FROM testbed
                   WHERE type = t.type AND id <= t.id) AS rownum
            FROM testbed t) AS c
    LEFT JOIN (SELECT t.id, t.type, t.value,
                      (SELECT count(*) FROM testbed
                        WHERE type = t.type AND id <= t.id) AS rownum
                 FROM testbed t) AS p
           ON c.type = p.type AND c.rownum = p.rownum + 1
  ) AS s
  ON t.id = s.c_id
  SET t.value = s.p_value
 WHERE t.value = 'A'; -- you can use more complex predicate(s) here

DELETE FROM testbed WHERE id = 8; -- make sure both predicate(s) match

このクエリはSQL Fiddleで確認できます(更新ではありません)。

于 2012-05-31T13:49:35.553 に答える
2
SET @remove_id = 8;

SELECT ID, type, value FROM (
  SELECT   ID,
           type,
           CAST(IF(type <> @type OR ISNULL(@val), value, @val) AS UNSIGNED)
             AS value,
           @type := IF(ID   = @remove_id, type, @type),
           @val  := IF(type = @type, value, @val)
  FROM     my_table JOIN (SELECT @type := NULL, @val := NULL) AS z
  ORDER BY ID ASC
) AS t
WHERE ID <> @remove_id

sqlfiddleで参照してください。


アップデート

あなたが実際に基になるテーブルを更新したいと思っていたことに気づきませんでした。そのために、少しハッカーを使用して、UPDATEステートメントで同じことを効果的に行うことができます (ユーザー変数に直接割り当てることはできないため、代わりに、新しい値と最初の値を取得して形成された null 文字列の連結を列に割り当てます)。新しく割り当てられたユーザー変数の 0 文字):

SET @remove_id = 8, @type = NULL, @val = NULL;

UPDATE my_table SET
  value = IF(
    type <> @type OR ISNULL(@val),
    value,
    CONCAT(@val, LEFT(@val := value, 0))
  ),
  type = CONCAT(type, LEFT(
    @type := IF(
      ID <> @remove_id,
      @type,
      CONCAT(type, LEFT(@val := value, 0))
    )
  , 0))
ORDER BY ID ASC;

DELETE FROM my_table WHERE ID = @remove_id;

sqlfiddleで参照してください。

于 2012-05-31T13:37:25.740 に答える
0

1 つのトランザクションで 2 つのクエリを実行できるように、InnoDB テーブルを使用することをお勧めします。私はこれを行います:

Step 1: Start a transaction on the table
Step 2: Get the record that is to be deleted (e.g. ID #8)
Step 3: Issue a query DELETE FROM tablename WHERE `ID`=$record_id
Step 4: Issue a query UPDATE tablename SET `value`='former_value' WHERE `type`='former_type' LIMIT 1
Step 5: if all operations were successful, commit the transaction else rollback
Step 6: End the transaction

お役に立てれば

于 2012-05-31T13:06:01.563 に答える