同じMySQLテーブル内のレコードの複製/コピー


87

しばらく探していましたが、問題の簡単な解決策が見つかりません。テーブルのレコードを複製したいのですが、一意の主キーを更新する必要があります。

私はこのクエリを持っています:

INSERT INTO invoices
    SELECT * FROM invoices AS iv WHERE iv.ID=XXXXX
    ON DUPLICATE KEY UPDATE ID = (SELECT MAX(ID)+1 FROM invoices)

問題は、これが ID、行をコピーするのではなく、行のをです。誰かがこれを修正する方法を知っていますか?

//編集:フィールド名は時間とともに変化する可能性があるため、すべてのフィールド名を入力せずにこれを実行したいと思います。

回答:


137

私が通常行う方法は、一時テーブルを使用することです。おそらく計算効率は良くありませんが、問題なく動作するようです!ここでは、レコード99全体を複製して、レコード100を作成しています。

CREATE TEMPORARY TABLE tmp SELECT * FROM invoices WHERE id = 99;

UPDATE tmp SET id=100 WHERE id = 99;

INSERT INTO invoices SELECT * FROM tmp WHERE id = 100;

うまくいきますように!


私はそれが好きです、物事から目を離さないための真ん中の一歩
SeanDowney

4
単一のレコードをコピーする場合は、更新と挿入の両方でwhereを削除できます。次に、mysqlコンソールで2回押すと、履歴の最後から2番目の操作が表示され、最後に便利な更新のIDを変更し、Enterキーを押し、上に押して、何も変更せずにもう一度Enterキーを押して、繰り返します複数コピーの手順。はるかに高速。
クリスティアンヴラビー

4
id 100がすでに存在する場合はどうでしょうか。次に、新しい行に新しいIDを選択し、操作中に誰かが新しい行を挿入すると、IDと新しく挿入されたIDは同じになります。これは、このコードのボトルネックです。
nbhatti2001 2013年

6
あなたは、NULLを使用することができますCREATE TEMPORARY TABLE tmp SELECT bla FROM invoices WHERE bla; UPDATE tmp SET id=NULL; INSERT INTO invoices SELECT * FROM tmp;
トビアスBeuving

@TobiasBeuvingColumn 'id' connot be null
Marcel

83

マルチクライアント環境では、Alexの答えにはいくつかの注意(ロックやトランザクションなど)が必要です。

AUTO IDフィールドがテーブルの最初のフィールドであると仮定すると(通常の場合)、暗黙のトランザクションを利用できます。

    一時テーブルを作成tmp SELECT *請求書からWHERE ...;
    ALTER TABLE tmpドロップID; #autoincrementフィールドを削除
    #tmpセットを更新...; #他の一意のキーを変更するためだけに必要
    INSERT INTO invoices SELECT 0、tmp。* FROM tmp;
    DROP TABLE tmp;

MySQLのドキュメントから:

AUTO_INCREMENTの使用:列にNULLまたは0を明示的に割り当てて、シーケンス番号を生成することもできます。


これにより、AUTO_INCREMENTフィールドがあるにもかかわらず、IDが0のレコードが配置されました。これをnullに変更しても機能するので、コードを変更して0ではなくnullを使用することをお勧めします。MySQL5.5.35(Ubuntu)を使用しています。
タイラーコリアー

@TylerCollier:NULLまたは0両方を使用できます。ここで問題を再現して共有することで、問題のスクリーンショットを投稿できますか?
Ravinder Reddy 2014

4行目のneatoサブクエリを理解しようとして頭が痛くなりました。この説明が役立つかもしれません。宛先テーブルinvoicesにAUTO_INCREMENT IDフィールドが含まれています。ソース表にtmpはありません。挿入中に必要な1対1の相関関係を取得0するには、宛先テーブルのAUTO_INCREMENTフィールドであるselectの最初のフィールドとしてaを指定します。tmp.*ソーステーブルからすべてのフィールドを選択しますtmp。パーフェクト!これで1対1の相関関係ができました。素晴らしい回答@Tim。
elbowlobstercowstand 2015年

@RavinderReddy NULLと0は同じではありません-0のレコードIDを持つことが可能です
Tobias Beuving

@TobiasBeuving:私はそれをよく知ってNULLおり0、同じではありません。現在のコンテキストはAUTO_INCREMENT機能です。そのようなフィールドに0またはNULL値を挿入することはできません。あなたは、このようなフィールドへの入力として使用する場合でも、次のシーケンス値は、そのフィールドに割り当てられますオート matically。したがって、私のコメント。SQL Fiddleの証明:sqlfiddle.com
#!

12

DUPLICATE KEYがトリガーすることを確実に知っているので、事前にMAX(ID)+1を選択できます。

INSERT INTO invoices SELECT MAX(ID)+1, ... other fields ... FROM invoices AS iv WHERE iv.ID=XXXXX 

2
はい、しかし私は他のすべてのフィールドを入力したくありません(多かれ少なかれ20あり、それらは時間とともに変化する可能性があります)。tablestructreが変更されるたびにコードを変更したくありません。
数字:

あなたがする必要があります。フィールドのリストからSQLステートメントを生成するスクリプトを作成することをお勧めします。ささいなことです。
Ingo

他の唯一の選択肢は、挿入トリガーを使用することです(mysqlがそれをサポートしている場合)。
Ingo

11

あなたのアプローチは良いですが、問題はフィールド名を登録する代わりに "*"を使用することです。すべての列名を主キーを超えるように配置すると、スクリプトは1つまたは複数のレコードでチャームのように機能します。

INSERT INTO invoices (iv.field_name, iv.field_name,iv.field_name ) SELECT iv.field_name, iv.field_name,iv.field_name FROM invoices AS iv WHERE iv.ID=XXXXX


ただし、これは、フィールドがテーブルに追加またはテーブルから削除されるとすぐにコードを変更する必要があることを意味します。(常にではないが、十分な場合が多い)
Roemer

9

遅い回答ですが、それでもよくある質問ですが、1行のinsert intoステートメントを使用するだけでうまくいった別の回答を追加したいと思います。新しいテーブルを作成せずに、それは簡単だと思います(CREATE TEMPORARY TABLE権限の問題である):

INSERT INTO invoices (col_1, col_2, col_3, ... etc)
  SELECT
    t.col_1,
    t.col_2,
    t.col_3,
    ...
    t.updated_date,
  FROM invoices t;

ソリューションはAUTO_INCREMENTid列に対して機能しています。それ以外の場合は、IDステートメントに列も追加できます。

INSERT INTO invoices (ID, col_1, col_2, col_3, ... etc)
  SELECT
    MAX(ID)+1,
    t.col_1,
    t.col_2,
    t.col_3,
    ... etc ,
  FROM invoices t;

それは本当に簡単で簡単です、後で2番目の更新ステートメントを使用せずに1行で他のものを更新できます(例:追加のテキストでタイトル列を更新するか、文字列を別の文字列に置き換える)。また、正確に何を指定するか複製したい場合は、それがすべての場合、一部の場合はそれを行うことができます。


2
これは、コピー中に一部のフィールドを変更するための優れた基礎でもあります。INSERT into wp_options SELECT NULL, 'theme_mods_twopress', option_value, autoload FROM wp_options where option_name = 'theme_mods_onepress';エントリ(AUTO_INCREMENT NULL上からのトリック)とoption_nameフィールドも増やしながらコピーを使用しただけです。
Marcel Waldvogel 2017

はい。私にとっては良い考えです。ありがとうございました!!
Ragu Natarajan

3

レコードのセット全体を複製したい場合に適切なように、Alexのすばらしい答えを拡張したかっただけです。

SET @x=7;
CREATE TEMPORARY TABLE tmp SELECT * FROM invoices;
UPDATE tmp SET id=id+@x;
INSERT INTO invoices SELECT * FROM tmp;

私はこれをしなければならなかっただけで、アレックスの答えが完璧なジャンプオフポイントだとわかりました!。もちろん、@ xをテーブル内の最大の行番号に設定する必要があります(クエリで取得できると確信しています)。これは非常に特殊な状況でのみ役立つため、すべての行を複製したくない場合は注意して使用してください。必要に応じて数学を調整します。


2

私にも同様の問題があり、これは私がやっていることです:

insert into Preguntas  (`EncuestaID`, `Tipo` , `Seccion` , `RespuestaID` , `Texto` )  select '23', `Tipo`, `Seccion`, `RespuestaID`, `Texto` from Preguntas where `EncuestaID`= 18

プリガンタに行った:

CREATE TABLE IF NOT EXISTS `Preguntas` (
  `ID` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `EncuestaID` int(11) DEFAULT NULL,
  `Tipo` char(5) COLLATE utf8_unicode_ci DEFAULT NULL,
  `Seccion` int(11) DEFAULT NULL,
  `RespuestaID` bigint(11) DEFAULT NULL,
  `Texto` text COLLATE utf8_unicode_ci ,
  PRIMARY KEY (`ID`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=522 ;

したがって、IDは自動的にインクリメントされます。また、には固定値( '23')を使用していEncuestaIDます。


申し訳ありませんが、列名を使用したくないことに気づきました。これは当てはまりません
Alex Angelico

1

これも必要でした。私の解決策は、SQLYOG(無料バージョン)を使用して、目的のレコードをSQLとしてエクスポートする(挿入を作成する)ことでした。

これを手動で編集してIDを削除します。これは自動生成する必要があるため、SQLYogに挿入をコピーして実行します。これは無痛でした。他の多くのMySQL GUIもこれを行うことができると思います。

これにより、ライブシステムでのテスト目的で使用できるレコードが提供されます。

テーブルは毎日書き直されているので、今はこのインサートも再利用できるようになっています。


0

わずかなバリエーション。主な違いは、主キーフィールド( "varname")をnullに設定することです。これにより、警告が生成されますが機能します。主キーをnullに設定すると、最後のステートメントにレコードを挿入するときに自動インクリメントが機能します。

このコードは、以前の試行をクリーンアップし、問題なく複数回実行できます。

DELETE FROM `tbl` WHERE varname="primary key value for new record";
DROP TABLE tmp;
CREATE TEMPORARY TABLE tmp SELECT * FROM `tbl` WHERE varname="primary key value for old record";
UPDATE tmp SET varname=NULL;
INSERT INTO `tbl` SELECT * FROM tmp;
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.