web-dev-qa-db-ja.com

MySQLでは、1つの行をコピーして同じテーブルに挿入できますか?

insert into table select * from table where primarykey=1

1つの行をコピーして同じテーブルに挿入したいだけです(つまり、テーブル内の既存の行を複製したい)が、このテーブルには列が多すぎます。

しかし、これを行うと、エラーが発生します:

キー1の重複エントリ「xxx」

これを処理するには、コピーするレコードの一時コンテナーと同じ列を持つ別のテーブルを作成します。

create table oldtable_temp like oldtable;
insert into oldtable_temp select * from oldtable where key=1;
update oldtable_tem set key=2;
insert into oldtable select * from oldtable where key=2;

これを解決する簡単な方法はありますか?

141
lina

Leonard Challisの手法をいくつかの変更を加えて使用しました。

CREATE TEMPORARY TABLE tmptable_1 SELECT * FROM table WHERE primarykey = 1;
UPDATE tmptable_1 SET primarykey = NULL;
INSERT INTO table SELECT * FROM tmptable_1;
DROP TEMPORARY TABLE IF EXISTS tmptable_1;

一時テーブルとして、複数のレコードが存在することはないため、主キーについて心配する必要はありません。これをnullに設定すると、MySQLが値自体を選択できるため、複製を作成するリスクはありません。

挿入する行が1つだけになるようにしたい場合は、INSERT INTO行の末尾にLIMIT 1を追加できます。

また、一時テーブル名に主キー値(この場合は1)を追加したことに注意してください。

173
Grim...

更新07/07/2014-私の回答に基づくGrim ...による回答は、改善されているため、より良いソリューションです以下の私の解決策なので、それを使用することをお勧めします。

次の構文を使用して、すべての列をリストせずにこれを実行できます。

CREATE TEMPORARY TABLE tmptable SELECT * FROM table WHERE primarykey = 1;
UPDATE tmptable SET primarykey = 2 WHERE primarykey = 1;
INSERT INTO table SELECT * FROM tmptable WHERE primarykey = 2;

別の方法で主キーを変更することもできます。

56
LeonardChallis

新しいレコードに新しいprimarykeyが必要だと思いますか? primarykeyAUTO_INCREMENTの場合、次のようにします。

INSERT INTO table (col1, col2, col3, ...)
SELECT col1, col2, col3, ... FROM table
  WHERE primarykey = 1

...ここでcol1, col2, col3, ...はテーブル内のすべての列です除くprimarykey

AUTO_INCREMENT列ではなく、primarykeyの新しい値を選択できるようにしたい場合は、次のようになります。

INSERT INTO table (primarykey, col2, col3, ...)
SELECT 567, col2, col3, ... FROM table
  WHERE primarykey = 1

...ここで567primarykeyの新しい値です。

36
Jordan Running

列を指定する必要がある最初のクエリでほとんどそれがありました。その方法では、挿入でプライマリキーを除外し、テーブルにある可能性のある自動インクリメントを実行して、エントリ。

たとえば、これを変更します。

insert into table select * from table where primarykey=1

これに:

INSERT INTO table (col1, col2, col3) 
SELECT col1, col2, col3 
FROM table 
WHERE primarykey = 1

クエリのINSERTまたはSELECT部分​​の列リストにprimarykey列を含めないでください。

12
Braeden Black

テーブルをダンプして、挿入コマンドを見つけて編集することもできます。

mysqldump -umyuser -p mydatabase --skip-extended-insert mytable > outfile.sql

--skip-extended-insertは、行ごとに1つの挿入コマンドを提供します。次に、お気に入りのテキストエディタで行を見つけ、コマンドを抽出して、プライマリキーを「デフォルト」に変更します。

この手順では、次のことを前提としています。

  • _duplicate_temp_tableがありません
  • 主キーはintです
  • テーブルを作成するアクセス権があります

もちろん、これは完全ではありませんが、特定の(おそらくほとんどの)場合に機能します。

DELIMITER $$
CREATE PROCEDURE DUPLICATE_ROW(copytable VARCHAR(255), primarykey VARCHAR(255), copyid INT, out newid INT)
BEGIN
        DECLARE EXIT HANDLER FOR SQLEXCEPTION SET @error=1;
        SET @temptable = '_duplicate_temp_table';
        SET @sql_text = CONCAT('CREATE TABLE ', @temptable, ' LIKE ', copytable);
        PREPARE stmt FROM @sql_text;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;
        SET @sql_text = CONCAT('INSERT INTO ', @temptable, ' SELECT * FROM ', copytable, ' where ', primarykey,'=', copyid);
        PREPARE stmt FROM @sql_text;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;
        SET @sql_text = CONCAT('SELECT max(', primarykey, ')+1 FROM ', copytable, ' INTO @newid');
        PREPARE stmt FROM @sql_text;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;
        SET @sql_text = CONCAT('UPDATE ', @temptable, ' SET ', primarykey, '=@newid');
        PREPARE stmt FROM @sql_text;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;
        SET @sql_text = CONCAT('INSERT INTO ', copytable, ' SELECT * FROM ', @temptable, '');
        PREPARE stmt FROM @sql_text;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;
        SET @sql_text = CONCAT('DROP TABLE ', @temptable);
        PREPARE stmt FROM @sql_text;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;
        SELECT @newid INTO newid;
END $$
DELIMITER ;

CALL DUPLICATE_ROW('table', 'primarykey', 1, @duplicate_id);
SELECT @duplicate_id;
6
Piotr Kaczmarek

これは、ある程度の創造性で実現できます。

SET @sql = CONCAT('INSERT INTO <table> SELECT null, 
    ', (SELECT GROUP_CONCAT(COLUMN_NAME) 
    FROM information_schema.columns 
    WHERE table_schema = '<database>' 
    AND table_name = '<table>' 
    AND column_name NOT IN ('id')), ' 
from <table> WHERE id = <id>');  

PREPARE stmt1 FROM @sql;
EXECUTE stmt1;

これにより、新しい行は、選択された行のIDではなく、自動インクリメントされたIDを取得します。

4
curmil

テーブルの主キーフィールドが自動インクリメントフィールドである場合、クエリを列で使用できます。たとえば、test_tblという名前のテーブルには、id, name, ageという3つのフィールドがあります。 idは主キーフィールドであり、自動インクリメントなので、次のクエリを使用して行を複製できます。

INSERT INTO `test_tbl` (`name`,`age`) SELECT `name`,`age` FROM `test_tbl`;

このクエリにより、すべての行が複製されます。


テーブルの主キーフィールドが自動インクリメントフィールドでない場合は、次の方法を使用できます。

INSERT INTO `test_tbl` (`id`,`name`,`age`)
  SELECT 20,`name`,`age` FROM `test_tbl` WHERE id = 19;

このクエリの結果は、id=19の重複した行がid=20として挿入されます。

4
user2595171

以下のいくつかは、このサイトから収集されました。これは、任意の数のフィールドを持つテーブルのレコードを複製するためにしたことです:

これは、表の先頭にAIフィールドがあることも前提としています

function duplicateRow( $id = 1 ){
dbLink();//my db connection
$qColumnNames = mysql_query("SHOW COLUMNS FROM table") or die("mysql error");
$numColumns = mysql_num_rows($qColumnNames);

for ($x = 0;$x < $numColumns;$x++){
$colname[] = mysql_fetch_row($qColumnNames);
}

$sql = "SELECT * FROM table WHERE tableId = '$id'";
$row = mysql_fetch_row(mysql_query($sql));
$sql = "INSERT INTO table SET ";
for($i=1;$i<count($colname)-4;$i++){//i set to 1 to preclude the id field
//we set count($colname)-4 to avoid the last 4 fields (good for our implementation)
$sql .= "`".$colname[$i][0]."`  =  '".$row[$i]. "', ";
}
$sql .= " CreateTime = NOW()";// we need the new record to have a new timestamp
mysql_query($sql);
$sql = "SELECT MAX(tableId) FROM table";
$res = mysql_query($sql);
$row = mysql_fetch_row($res);
return $row[0];//gives the new ID from auto incrementing
}
3
richardwhitney

以下を使用します

insert into ORDER_ITEM select * from ORDER_ITEM where ITEM_NUMBER =123;
2
Ishan Liyanage

更新フィールドと自動インクリメント値を含む行のクローン

CREATE TEMPORARY TABLE `temp` SELECT * FROM `testing` WHERE id = 14;

UPDATE `temp` SET id = (SELECT id FROM testing ORDER by id DESC LIMIT 1
 )+1, user_id = 252 ,policy_no = "mysdddd12" where id = 14;

INSERT INTO `testing` SELECT * FROM `temp`;

DROP TEMPORARY TABLE IF EXISTS `temp`;

私はこれに遅れているかもしれませんが、私は私のために働いた同様の解決策を持っています。

 INSERT INTO `orders` SELECT MAX(`order_id`)+1,`container_id`, `order_date`, `receive_date`, `timestamp` FROM `orders` WHERE `order_id` = 1

このように、一時テーブルなどを作成する必要はありません。行が同じテーブルにコピーされるため、Max(PK)+1関数を簡単に使用できます。

私はこの質問の解決策を探しに来て(構文を忘れていました)、自分のクエリを作成することになりました。物事が時々うまくいくおかしい。

よろしく

2
echo_salik

主キーが自動増分の場合、主キー以外の各フィールドを指定します。

INSERT INTO table(field1,field2,field3) SELECT (field1,field2,field3) FROM table WHERE primarykey=1

2
Fedy Venom

@LeonardChallisのソリューションを更新しました。他のソリューションとしては機能しなかったためです。一時テーブルのWHERE句とSET primaryKey = 0を削除して、MySQLがprimaryKeyを自動インクリメントするようにしました

CREATE TEMPORARY TABLE tmptable SELECT * FROM myTable;
UPDATE tmptable SET primaryKey = 0;
INSERT INTO myTable SELECT * FROM tmptable;

もちろん、これはallテーブルの行を複製することです。

2
D4V1D

上記に示したこのソリューションは、選択した行にも最適です。たとえば、Nice2workプロジェクトのデモ行を作成していますが、これは完璧に機能します。

CREATE TEMPORARY TABLE tmptable SELECT * FROM myTable WHERE id=500;
UPDATE tmptable SET id = 0;
UPDATE some fields I need to change
INSERT INTO myTable SELECT * FROM tmptable;
DROP TABLE tmptable;

//  You can use this same also directly into your code like (PHP Style)
$sql = "CREATE TEMPORARY TABLE tmptable SELECT * FROM myTable WHERE id=500;
UPDATE tmptable SET id = 0;
UPDATE some fields I need to change
INSERT INTO myTable SELECT * FROM tmptable;DROP TABLE tmptable;";
1
Ronald Stevens

私はGrimの手法を少し変更して使用しました。このクエリを探している人が主キーの問題のために単純なクエリを実行できないためである場合:

INSERT INTO table SELECT * FROM table WHERE primakey=1;

MySql 5.6.26をインストールすると、キーはNULLに対応せず、エラーが発生します。

#1048 - Column 'primakey' cannot be null 

したがって、一時テーブルを作成した後、主キーをNULL可能に変更します。

CREATE TEMPORARY TABLE tmptable_1 SELECT * FROM table WHERE primarykey = 1;
ALTER TABLE tmptable_1 MODIFY primarykey int(12) null;
UPDATE tmptable_1 SET primarykey = NULL;
INSERT INTO table SELECT * FROM tmptable_1;
DROP TEMPORARY TABLE IF EXISTS tmptable_1;
1
Luca Camillo

私はこれをしなければならなかった、これは私の手動の解決策でした:

  1. phpmyadminで、コピーする行をチェックします
  2. クエリ結果の操作の下にある'Export'をクリックします
  3. 次のページで'ファイルとして保存'をチェックしてから'Go'をクリックします
  4. エクスポートされたファイルをテキストエディターで開き、プライマリフィールドの値を見つけて、一意の値に変更します。
  5. phpmyadminに戻って'Import'タブをクリックし、browseの下にある.sqlファイルをインポートするファイルを見つけて、'Go '重複行を挿入する必要があります。

PRIMARYフィールドが何であるかわからない場合は、phpmyadminページを振り返り、'Structure'タブをクリックしてページの下の'Indexes'の下に、どの'Field''Keyname' value 'があるかが表示されますPRIMARY '

長い道のりのようなものですが、マークアップを処理したくない場合は、1行だけを複製する必要があります。

1
user27068

非常に簡単なソリューションとして、PHPMyAdminを使用して行をCSVファイルとしてエクスポートし、修正されたCSVファイルをインポートするだけです。 ID/primarykey列を編集して、インポートする前に値を表示しないようにします。

SELECT * FROM table where primarykey=1

次に、ページの下部で:

enter image description here

「エクスポート」とは、単にエクスポートし、csvファイルを編集してprimarykey値を削除するため、空になり、データベースにインポートするだけで、インポート時に新しいprimarykeyが割り当てられます。

0
Grant

このサイトでオンラインで見つけた回答を次に示します。上記の方法を説明します 1 ページの下部に回答があります。基本的には、メモリに保持されている一時テーブルにコピーする行をコピーします。次に、更新を使用して主キー番号を変更します。その後、ターゲットテーブルに再挿入します。次に、テーブルをドロップします。

これはそのためのコードです:

CREATE TEMPORARY TABLE rescueteam ENGINE = MEMORY SELECT * FROMfitnessreport4 WHERE rID = 1;#1行が影響を受けました。 UPDATE rescueteam SET rID = Null WHERE rID = 1;#1行が影響を受けました。INSERTINTO fitnessreport4 SELECT * FROM rescueteam;#1行が影響を受けました。 DROP TABLE rescueteam#MySQLは空の結果セットを返しました(つまり、ゼロ
列)。

一時テーブルrescueteamを作成しました。元のテーブルfitnessreport4から行をコピーしました。その後、一時テーブルの行の主キーをnullに設定して、キーの重複エラーを発生させることなく元のテーブルにコピーできるようにします。昨日の夜、このコードを試してみましたが、うまくいきました。

0
user2363682

ネクロポストで申し訳ありませんが、これは私がグーグルで見つけたものです。

まず、MySQLではなくSQL Serverを使用していますが、同様に動作するはずです。私はレナード・チャリスのソリューションを使用しましたが、それは最も単純でニーズを満たしていましたが、これには問題があります-PKを取得して1だけ増やすと、問題の行が追加されてから他のレコードを追加するとどうなりますか。システムにPKの自動インクリメントを処理させるのが最善であると判断したため、次のことを行いました。

SELECT * INTO #tmpTable FROM Table WHERE primarykey = 1
--Optionally you can modify one or more fields here like this: 
--UPDATE #tmpTable SET somefield = newData
ALTER TABLE #tmpTable DROP COLUMN TicketUpdateID
INSERT INTO Tickets SELECT * FROM #tmpTable
DROP TABLE #tmpTable

これはMySQLでも同様に機能すると思いますが、これをテストすることはできません。

0
maxx233

Kohaデータベースで使用して、バーコード列に 'C'プレフィックスを持つ重複アイテムを挿入しました:

INSERT INTO items (`biblionumber`, `biblioitemnumber`, `barcode`, `dateaccessioned` ) SELECT `biblionumber`, `biblioitemnumber`,  CONCAT('C',`barcode`), `dateaccessioned` FROM `items` WHERE barcode='14832';
0
Rajiv Mahmud

テーブルを作成する

    CREATE TABLE `sample_table` (
       `sample_id` INT(10) unsigned NOT NULL AUTO_INCREMENT,
       `sample_name` VARCHAR(255) NOT NULL,
       `sample_col_1` TINYINT(1) NOT NULL,
       `sample_col_2` TINYINT(2) NOT NULL,

      PRIMARY KEY (`sample_id`),
      UNIQUE KEY `sample_id` (`sample_id`)

    ) ENGINE='InnoDB' DEFAULT CHARACTER SET 'utf8' COLLATE 'utf8_general_ci';

行を挿入する

INSERT INTO `sample_table`
   VALUES(NULL, 'sample name', 1, 2);

上記の行挿入を複製

INSERT INTO `sample_table`
   SELECT 
    NULL AS `sample_id`, -- new AUTO_INCREMENT PRIMARY KEY from MySQL
    'new dummy entry' AS `sample_name`,  -- new UNIQUE KEY from you
    `sample_col_1`, -- col from old row
    `sample_col_2` -- col from old row
   FROM `sample_table`
   WHERE `sample_id` = 1;

テスト

SELECT * FROM `sample_table`;
0
Abdullah Aydın

私はそれが古い質問であることを知っていますが、別の解決策があります:

これは、主キーが自動インクリメントであると仮定して、メインテーブルの行を複製し、リンクテーブルデータのコピーを新しいメインテーブルIDで作成します。

列名を取得するための他のオプション:
-tablenameから列を表示; (列名:フィールド)
-DESCRIBE tablename(列名:フィールド)
-SELECT column_name FROM information_schema.columns WHERE table_name = 'tablename'(列名:column_name)

//First, copy main_table row
$ColumnHdr='';
$Query="SHOW COLUMNS FROM `main_table`;";
$Result=Wrappedmysql_query($Query,$link,__FILE__,__LINE__);
while($Row=mysql_fetch_array($Result))
{
    if($Row['Field']=='MainTableID')     //skip main table id in column list
        continue;
    $ColumnHdr.=",`" . $Row['Field'] . "`";
}
$Query="INSERT INTO `main_table` (" . substr($ColumnHdr,1) . ")
        (SELECT " . substr($ColumnHdr,1) . " FROM `main_table`
            WHERE `MainTableID`=" . $OldMainTableID . ");";
$Result=Wrappedmysql_query($Query,$link,__FILE__,__LINE__);
$NewMainTableID=mysql_insert_id($link);

//Change the name (assumes a 30 char field)
$Query="UPDATE `main_table` SET `Title`=CONCAT(SUBSTRING(`Title`,1,25),' Copy') WHERE `MainTableID`=" . $NewMainTableID . ";";
$Result=Wrappedmysql_query($Query,$link,__FILE__,__LINE__);

//now copy in the linked tables
$TableArr=array("main_table_link1","main_table_link2","main_table_link3");
foreach($TableArr as $TableArrK=>$TableArrV)
{
    $ColumnHdr='';
    $Query="SHOW COLUMNS FROM `" . $TableArrV . "`;";
    $Result=Wrappedmysql_query($Query,$link,__FILE__,__LINE__);
    while($Row=mysql_fetch_array($Result))
    {
        if($Row['Field']=='MainTableID')     //skip main table id in column list, re-added in query
            continue;
        if($Row['Field']=='dbID')    //skip auto-increment,primary key in linked table
            continue;
        $ColumnHdr.=",`" . $Row['Field'] . "`";
    }

    $Query="INSERT INTO `" . $TableArrV . "` (`MainTableID`," . substr($ColumnHdr,1) . ")
            (SELECT " . $NewMainTableID . "," . substr($ColumnHdr,1) . " FROM `" . $TableArrV . "`
             WHERE `MainTableID`=" . $OldMainTableID . ");";
    $Result=Wrappedmysql_query($Query,$link,__FILE__,__LINE__);
}
0
Paul

少なくとも自動インクリメントの場合、max233は確かに正しい軌道に乗っていました。ただし、ALTER TABLEは実行しないでください。一時テーブルの自動インクリメントフィールドをNULLに設定するだけです。これによりエラーが発生しますが、一時テーブル内のすべてのフィールドの次のINSERTが発生し、NULL自動フィールドは一意の値を取得します。

0
user3067083