web-dev-qa-db-ja.com

単一操作でのINSERTおよびDELETE(1つのテーブルから別のテーブルへの移動)

この質問 から派生して、その複雑なソートの結果を使用して、どの行のデータを1つのテーブルから別のテーブルに移動するかを決定します。まだ試していませんが、次の方法で「コピー」部分を実行しても問題はないと思います。

INSERT * FROM (...)

かっこ内にそのSELECTからの私の結果を使用します。

ただし、同じクエリを使用して同じ結果をSELECTにしようとすると、DELETE FROM、結果には同じ行が含まれなくなります。したがって、正しいテーブルの正しい行をDELETEするために、少なくとも主キー(およびOriginテーブル、つまり2つのOriginテーブルと1つの宛先テーブル)を格納する方法が必要です。

私が読んで理解したことから、単一のクエリで実行することは不可能であり、いずれにしてもトランザクションが必要になります(正しく実行する方法を検索する必要があります)。

したがって、私の最初のアプローチは、最初にINSERTコピーを実行してから、2つのテーブルから、宛先テーブルに表示されている主キーを含む行を削除することです。そのテーブルは2つのOriginテーブルよりもmuch大きくなるため、少し非効率的であり、派生テーブルから、サブクエリから取得したことがわかります。そこにある情報を使用する方がはるかに効率的です。それで、私がこれについてどうやって最善を尽くすことができるかについての手がかりはありますか?

2
insaner

好奇心の強い人のために、@ evanSteinbrennerのヒントのおかげで、これが私がそれを解決した方法です:

LOCK TABLES
    main WRITE,
    main as t WRITE,
    main as t_in WRITE,
    main as df WRITE,
    main_date WRITE,
    main_date as d WRITE,
    main_date as d2 WRITE,
    main_date as dx WRITE,
    main_weekday WRITE,
    main_weekday as w WRITE;
CREATE TEMPORARY TABLE IF NOT EXISTS stacks_tmp AS ( $sql_stacks_ready );
SELECT COUNT(*) FROM stacks_tmp ORDER BY date;
INSERT INTO main (id, title, added_on, date) SELECT  id, title, added_on, date FROM  stacks_tmp;
DELETE FROM main_date WHERE  (id) IN (SELECT id FROM   stacks_tmp WHERE from_table = 'date') ;
DELETE FROM main_weekday WHERE  (id) IN (SELECT id FROM   stacks_tmp WHERE from_table = 'weekday') ;
UNLOCK TABLES;

これは、元のPerl/dbiコードから単純化されています。このコードは、各ステートメントの失敗をチェックするロジックを実行します。私はまだそれを学ぶ必要があるので、私はまだトランザクションを使用してそれを持っていません。 $sql_stacks_ready変数には、順序付けされたスタックを作成する(したがって、LOCK TABLES内のすべてのエイリアスの必要性)を作成する(半)複雑なクエリが含まれます。列from_tableはスタック構築クエリによって設定され、元のテーブルが何であったかに設定されます。

1
insaner

table2から削除されたものをtable1に挿入するトリガーを使用できます。

CREATE TABLE table1(id int, name varchar(10), level int);
INSERT INTO table1(id, name, level) VALUES
  (0, 'a', 0)
  , (1, 'b', 1)
  , (2, 'c', 0)
  , (3, 'd', 2)
  , (4, 'e', 1)
  , (4, 'f', 0);

CREATE TABLE table2(id int, name varchar(10), level int);

DELIMITER //

CREATE TRIGGER table1_after_delete
    AFTER DELETE
    ON table1 FOR EACH ROW        
    trig: BEGIN
    IF (@TRIGGER_AFTER_DELETE_ENABLE = FALSE)
    THEN
        LEAVE trig;
    END IF;
    INSERT INTO table2
    ( 
        id
        , name
        , level
    )
    VALUES
    ( 
        OLD.id
        , OLD.name
        , OLD.level 
    );
END; //

DELIMITER ;

DELETE FROM table1 WHERE level = 1;
SET @TRIGGER_AFTER_DELETE_ENABLE = FALSE;
DELETE FROM table1 WHERE level = 2;

このサンプルは、table1内のすべてのlevel=1を削除し、次にlevel=2を削除します。 table2の場合にのみ、@TRIGGER_AFTER_DELETE_ENABLE <> FALSEに移動します。サンプル sqlfiddle を参照してください。

したがって:

  • 1の行がtable2に挿入されます(変数はまだ設定されていません)
  • 2の行はtable2に挿入されません(変数がFALSEに設定されます)

Table1からの出力:

d   name    level
0   a       0
2   c       0
4   f       0

Table2からの出力:

id  name    level
1   b       1
4   e       1

level=2のある行はもうありません(サンプル sqlfiddle を参照)。

あなたは基本的にこのように選択します:

  • @TRIGGER_AFTER_DELETE_ENABLE = FALSE deleteステートメントの前=>データを削除してコピーする
  • @TRIGGER_AFTER_DELETE_ENABLE <> FALSE =>データのみを削除

SQLサーバー

問題はこのRDBMSに関するものではありませんが、SQL ServerではOUTPUT句を使用します。

DELETE FROM t
OUTPUT deleted.id, deleted.name, deleted.level INTO table2
FROM table1 t
WHERE level > 0;

単に削除されたものの出力を別の場所に挿入するだけです。一時テーブルは不要であり、IOはtempdbで制限されます。

2

最初にすべてを一時テーブルに挿入します。次に、挿入と削除は、複雑なクエリではなく、一時テーブルのすべてです。これにより、両方を行う方法の問題が解決され、おそらく、複雑なクエリを複数回実行するよりもパフォーマンスが向上します。

My-SQLよりも多くのMS-SQLを実行するため、スタックオーバーフローの別の回答にリンクします。

https://stackoverflow.com/questions/5859391/create-a-temporary-table-in-a-select-statement-without-a-separate-create-table

MS-SQLでは単に

  SELECT .... into #MyTempTable FROM ... WHERE..
1