简体   繁体   English

PHP MySQL 在同一个表中复制一行...使用主键和唯一键

[英]PHP MySQL Copy a row within the same table... with a Primary and Unique key

My table has two keys, one is an auto incrementing id (PRIMARY), the other is the name of the item (UNIQUE).我的表有两个键,一个是自动递增的 id (PRIMARY),另一个是项目的名称 (UNIQUE)。

Is it possible to duplicate a row within this same table?是否可以在同一个表中复制一行? I have tried:我努力了:

INSERT INTO items
SELECT * FROM items WHERE id = '9198'

This gives the error Duplicate entry '9198' for key 'PRIMARY'这给出了错误Duplicate entry '9198' for key 'PRIMARY'

I have also tried:我也试过:

INSERT INTO items
SELECT * FROM items WHERE id = '9198'
ON DUPLICATE KEY UPDATE id=id+1

Which gives the error Column 'id' in field list is ambiguous这给出了错误Column 'id' in field list is ambiguous

And as far as the item name (UNIQUE) field goes, is there a way to append (Copy) to the item name, since this field must also be unique?至于项目名称(UNIQUE)字段,有没有办法将 append (Copy)到项目名称,因为这个字段也必须是唯一的?

Select all columns explicitly, except the id column: 明确选择所有列,但id列除外:

INSERT INTO items
(col1, col2, ..., coln)
SELECT col1, col2, ..., coln
FROM items
WHERE id = '9198'

Your next question will probably be: 你的下一个问题可能是:

Is there a way to do this without listing all the columns explicitly? 有没有办法在不明确列出所有列的情况下执行此操作?

Answer: No, I don't think so. 答:不,我不这么认为。

If you really don't want to list all the table columns like in Mark's answer, you can try this: 如果您真的不想列出Mark的答案中的所有表格列,您可以尝试这样做:

CREATE TEMPORARY TABLE temp_tbl SELECT * FROM items WHERE id = '9198';
SELECT @maxId := MAX(id) + 1 FROM items;
UPDATE temp_tbl SET id = @maxId;
INSERT INTO items SELECT * FROM temp_tbl;
DROP TABLE temp_tbl;

Not beautiful, not fast. 不漂亮,不快。 But works. 但是有效。

Alternatively, if you don't want to write all the columns explicitly (and don't want to start creating/dropping tables), you can just get the columns of the table and build the query automagically: 或者,如果您不想显式写入所有列(并且不想开始创建/删除表),则只需获取表的列并自动构建查询:

//get the columns
$cols=array();
$result = mysql_query("SHOW COLUMNS FROM [table]"); 
 while ($r=mysql_fetch_assoc($result)) {
  if (!in_array($r["Field"],array("[unique key]"))) {//add other columns here to want to exclude from the insert
   $cols[]= $r["Field"];
  } //if
}//while

//build and do the insert       
$result = mysql_query("SELECT * FROM [table] WHERE [queries against want to duplicate]");
  while($r=mysql_fetch_array($result)) {
    $insertSQL = "INSERT INTO [table] (".implode(", ",$cols).") VALUES (";
    $count=count($cols);
    foreach($cols as $counter=>$col) {
      $insertSQL .= "'".$r[$col]."'";
  if ($counter<$count-1) {$insertSQL .= ", ";}//dont want a , on the last one
    }//foreach
  $insertSQL .= ")";

  mysql_query($insertSQL);//execute the query
  }//while

Note that this uses the depreciated code of MySQL and it should be MySQLi. 请注意,这使用了MySQL的折旧代码,它应该是MySQLi。 I'm sure it could also be improved, but it's what I'm using and it works very well. 我相信它也可以改进,但它正是我正在使用它并且它工作得非常好。

Thanks to hobailey for providing a great maintenance-free solution. 感谢hobailey提供了一个免维护的解决方案。

Here is the code I ended up using, which is updated for MySQLi: 这是我最终使用的代码,它是为MySQLi更新的:

// Get the columns
$cols = array();
$result = $mysqli->query("SHOW COLUMNS FROM [TABLE]"); // Change table name

while ($r = $result->fetch_array(MYSQLI_ASSOC)) {
    if (!in_array($r["Field"], array("COLA", "COL4", "COL8"))) { // Edit array with any column names you want to exclude
        $cols[] = $r["Field"];
    }
}

// Build and do the insert
$result = $mysqli->query("SELECT * FROM [TABLE] WHERE [SELECTION CRITERIA];"); // Change table name and add selection criteria

while ($r = $result->fetch_array(MYSQLI_ASSOC)) {

    $insertSQL = "INSERT INTO [TABLE] (" . implode(", ",$cols) . ") VALUES ("; // Change table name
    $count = count($cols);

    foreach($cols as $counter=>$col) {
// This is where you can add any code to change the value of existing columns
        $insertSQL .= "'" . $mysqli->real_escape_string($r[$col]) . "'";
        if ($counter < ($count - 1)) {
            $insertSQL .= ", ";
        }
    } // END foreach

    $insertSQL .= ");";

    $mysqli->query($insertSQL);
    if ($mysqli->affected_rows < 1) {
// Add code if the insert fails
    } else {
// Add code if the insert is successful
    }

} // END while

The question title does state you want to do this from PHP. 问题标题确实表明你想从PHP做到这一点。

I've encountered the same problem and writing out all the column names is tedious and hard to maintain if you change your table structure (add/remove columns)... and I don't like the solutions that use temp tables. 我遇到了同样的问题,如果你改变你的表结构(添加/删除列),写出所有列名称是繁琐且难以维护的......我不喜欢使用临时表的解决方案。

I've elected to solve this problem with two queries sent from PHP - works great and no maintenance required (disclaimer: I use the meekrodb library for database access) : 我选择用PHP发送的两个查询来解决这个问题 - 工作得很好,无需维护(免责声明:我使用meekrodb库进行数据库访问)

//get the data as an associative array
$row = DB::queryFirstRow("SELECT * FROM your_table WHERE id=%i",$id);
if ($row){
    unset($row["id"]); //unset the primary key
    DB::insert("your_table",$row);
    return DB::insertId();
} else {
    return false;
}

You can even perform more manipulations on the internal data (unset other columns to ignore, edit values, etc) before re-inserting. 在重新插入之前,您甚至可以对内部数据执行更多操作(取消设置其他列以忽略,编辑值等)。

Another solution in PHP for copy a row in the same table without a specific column(s) / eg primary key - and without "TEMPORARY TABLE" and "SHOW COLUMNS FROM..."-method: PHP中的另一个解决方案,用于在没有特定列/例如主键的同一表中复制行 - 并且没有“TEMPORARY TABLE”和“SHOW COLUMNS FROM ...” - 方法:

$stmt = $db->prepare("select * from table where id = :id;");
$stmt->bindValue(':id', $_GET['id'], PDO::PARAM_INT);
$stmt->execute();
$row = $stmt->fetch(PDO::FETCH_ASSOC);
unset($row['id']);      //remove primary key

$columns = array_keys($row);
$query = "insert into table (`".implode('`, `', $columns)."`) select `".implode('`, `', $columns)."` from  data_ticket_serie where id = ".$_GET['id'].";";
// echo $query;
$stmt = $db->prepare($query);
$stmt->execute();

The INSERT is a SELECT-statement, so the values are not direct in the statement --> no problems with "real_escape_string" or something like that. INSERT是一个SELECT语句,因此值在语句中不直接 - >“real_escape_string”没有问题或类似的问题。

For tables with many columns, I use a (yes, slow) method similar to Phius idea. 对于包含许多列的表,我使用类似于Phius想法的(是,慢)方法。
I put it here just for completeness. 我把它放在这里只是为了完整。

Let's assume, table 'tbl' has an 'id' defined like 我们假设,表'tbl'的'id'定义为

id INT NOT NULL AUTO_INCREMENT PRIMARY KEY id INT NOT NOT NULL AUTO_INCREMENT PRIMARY KEY

Then you can clone/copy a row by following these steps: 然后,您可以按照以下步骤克隆/复制行:

  1. create a tmp table 创建一个tmp表

CREATE TEMPORARY TABLE tbl_tmp LIKE tbl; CREATE TEMPORARY TABLE tbl_tmp LIKE tbl;

  1. Insert one or more entries you want to clone / copy 插入要克隆/复制的一个或多个条目

INSERT INTO tbl_tmp SELECT * FROM tbl WHERE ...; INSERT INTO tbl_tmp SELECT * FROM tbl WHERE ...;

  1. remove the AUTOINCREMENT tag from 'id' 从'id'中删除AUTOINCREMENT标记

ALTER TABLE tbl_tmp MODIFY id INT; ALTER TABLE tbl_tmp MODIFY id INT;

  1. drop the primary index 删除主索引

ALTER TABLE tbl_tmp DROP PRIMARY KEY; ALTER TABLE tbl_tmp DROP PRIMARY KEY;

  1. update your unique indices and set 'id' to 0 (0 needed for step 6. to work) 更新您的唯一索引并将'id'设置为0(步骤6工作需要0)

UPDATE tbl_tmp SET unique_value=?,id=0; UPDATE tbl_tmp SET unique_value = ?, id = 0;

  1. copy your modified rows into 'tbl' with 'id' being autogenerated. 将修改后的行复制到'tbl',并自动生成'id'。

INSERT INTO tbl SELECT * FROM tbl_tmp; INSERT INTO tbl SELECT * FROM tbl_tmp;

  1. cleanup (or just close the DB connection) 清理(​​或只是关闭数据库连接)

DROP TABLE tbl_tmp; DROP TABLE tbl_tmp;

If you also need clone/copy some dependant data in other tables, do the above for each row. 如果还需要在其他表中克隆/复制某些相关数据,请对每行执行上述操作。 After step 6 you can get the last inserted key and use this to clone/copy the dependant rows within other tables using the same procedure. 在步骤6之后,您可以获取最后插入的密钥,并使用此密钥使用相同的过程克隆/复制其他表中的相关行。

I am surprised anyone didn't mention using phpMyAdmin to create the query. 我很惊讶有人没有提到使用phpMyAdmin来创建查询。 Because this would make it fast to add all the columns and then you just set the id to null or o as mentioned above by wlf. 因为这样可以快速添加所有列,然后只需将id设置为null或o,如上所述。

This is by far the simplest way to do it 这是迄今为止最简单的方法

INSERT INTO users SELECT 0,email,user FROM users WHERE id=10

I had to do something similar recently so I thought I post my solution for any size table, example included. 我最近不得不做类似的事情,所以我想我发布我的解决方案适用于任何大小的表,包括示例。 It just take a configuration array which can be adjusted to practically any size table. 它只需要一个配置数组,可以调整到几乎任何大小的表。

$copy_table_row = array(
    'table'=>'purchase_orders',     //table name
    'primary'=>'purchaseOrderID',   //primary key (or whatever column you're lookin up with index)
    'index'=>4084,                  //primary key index number
    'fields' => array(
        'siteID',             //copy colunm
        ['supplierID'=>21],   //overwrite this column to arbirary value by wrapping it in an array
        'status',             //copy colunm
        ['notes'=>'copied'],  //changes to "copied"
        'dateCreated',        //copy colunm
        'approved',           //copy colunm
    ),
);
echo copy_table_row($copy_table_row);



function copy_table_row($cfg){
    $d=[];
    foreach($cfg['fields'] as $i => $f){
        if(is_array($f)){
            $d['insert'][$i] = "`".current(array_keys($f))."`";
            $d['select'][$i] = "'".current($f)."'";
        }else{
            $d['insert'][$i] = "`".$f."`";
            $d['select'][$i] = "`".$f."`";
        }
    }
    $sql = "INSERT INTO `".$cfg['table']."` (".implode(', ',$d['insert']).")
        SELECT ".implode(',',$d['select'])."
        FROM `".$cfg['table']."`
        WHERE `".$cfg['primary']."` = '".$cfg['index']."';";
    return $sql;
}

This will output something like: 这将输出如下内容:

INSERT INTO `purchase_orders` (`siteID`, `supplierID`, `status`, `notes`, `dateCreated`, `approved`)
SELECT `siteID`,'21',`status`,'copied',`dateCreated`,`approved`
FROM `purchase_orders`
WHERE `purchaseOrderID` = '4084';

Simplest just make duplicate value of the record最简单的只是使记录的重复值

INSERT INTO items (name,unit) SELECT name, unit FROM items WHERE id = '9198' 

Or With make duplicate the value of the record with add new/change value of some columns value like 'yes' or 'no'或者使用复制记录的值,添加一些列值的新值/更改值,如“是”或“否”

INSERT INTO items (name,unit,is_variation) SELECT name, unit,'Yes' FROM items WHERE id = '9198' 

Say the table is user(id,email,user) and because you have a WHERE clause you can't use MAX(id)+1 : 假设该表是user(id,email,user)并且因为您有WHERE子句,所以不能使用MAX(id)+1

INSERT INTO users SELECT 0,email,user FROM users WHERE id=10

Bear in mind though that you should always specify the column names when using INSERT. 请记住,在使用INSERT时应始终指定列名。

I wanted to copy a row in my events table and found Mark's solution very helpful. 我想在我的事件表中复制一行,发现Mark的解决方案非常有用。 I made it a bit shorter. 我做得有点短。

public static function getColumnsOfTable($table,  $arr_exclude_cols=array()) {
    global $obj_db;

    $cols = array();
    $result = $obj_db->query("SHOW COLUMNS FROM `".$table."`");

    while ($r = $result->fetch_array(MYSQLI_ASSOC)) {
        if (!in_array($r["Field"], $arr_exclude_cols)) { 
            $cols[] = $r["Field"];
        }
    }

    return $cols;
}

and the code for copying: 以及复制代码:

$cols = Utils::getColumnsOfTable('events', array('event_id'));

    $result1 = $obj_db->query('SELECT * FROM `events` WHERE `event_id` = '.$event_id);
    $arr_event = mysqli_fetch_array($result1, MYSQLI_NUM);
    unset($arr_event[0]);

    $insertSQL =  'INSERT INTO `events` (`' . implode('`, `',$cols) . '`) VALUES ("'. implode('","', $arr_event).'")'; 

This is a general function to copy an record of any table: 这是复制任何表记录的一般功能:

/**
 * @param string $table         Name of table
 * @param array $primaryKey     Which record should be copied? array('nameOfColumnWithUniqueId' => "value")
 * @param array $excludeFields  Which columns should not be copied (e.q. Unique Cols)
 * @param string $database      Name of database
 * @return int                  ID of new inserted record
 */
function copyMysqlRow($table, $primaryKey, $excludeFields = array(), $database = "usr_web3_2")
{
    $field = key($primaryKey);
    $value = current($primaryKey);
    $sql = "
        SELECT
            *
        FROM
            $database.$table
        WHERE
          $field = '$value'
    ";

    $result = mysql_query($sql);
    $row = mysql_fetch_assoc($result);

    $cols = array();
    $values = array();
    foreach ($row AS $col=>$value) {
        if (!in_array($col, $excludeFields)) {
            $cols[] = "`" . $col . "`";
            $values[] = $value === null ? 'null' : "'" . $value . "'";
        }
    }

    $sql = sprintf(" INSERT INTO $database.$table (%s) VALUES  (%s) ", implode($cols, ','), implode($values, ','));

    mysql_query($sql);

    return mysql_insert_id();
}

I use this one... it drops the primary key column on temp_tbl so there is no problem with duplicate IDs我用这个……它把主键列放在 temp_tbl 上,所以重复 ID 没有问题

CREATE TEMPORARY TABLE temp_tbl SELECT * FROM table_to_clone;
ALTER TABLE temp_tbl DROP COLUMN id;
INSERT INTO table_to_clone SELECT NULL, temp_tbl.* FROM temp_tbl;
DROP TABLE temp_tbl;

Since tables can often have DEFAULT values (create_date etc), it is useful to go with field names, a function like this will work in mysql, postge and mssql.由于表通常可以有默认值(create_date 等),它对带有字段名称的 go 很有用,像这样的 function 可以在 mysql、postge 和 mssql 中使用。

function duplicate_row($table, $id, $fields) {
global $conn;
try {
    $query = "SELECT * FROM $table WHERE id = :id";
    $stmt = $conn->prepare($query);
    $stmt->bindValue(":id", $id);
    $stmt->execute();
    $row = $stmt->fetch(PDO::FETCH_ASSOC);
    $query = "INSERT INTO $table (" . implode(", ", $fields) . ") VALUES (:" . implode(", :", $fields) . ")";
    $stmt = $conn->prepare($query);
    foreach ($fields as $key) {
        $stmt->bindValue(":" . $key, $row[$key]);
    }
    $stmt->execute();
    return $conn->lastInsertId();
} catch (PDOException $e) {
    echo "Error: " . $e->getMessage();
}}

If you give the field names in an array, it will return the id it copied.如果您在数组中给出字段名称,它将返回它复制的 id。

$dup_id = duplicate_row("table1", 9198, Array("field1","field2"));
echo $dup_id;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM