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


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

我的表有两个键,一个是自动递增的id(PRIMARY(,另一个是项目的名称(UNIQUE(。

是否可以在同一表中复制一行?我试过:

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

这会导致错误Duplicate entry '9198' for key 'PRIMARY'

我也尝试过:

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

这给出了错误Column 'id' in field list is ambiguous

项目名称(UNIQUE(字段而言,有没有办法将(Copy)附加到项目名称中,因为此字段也必须是唯一的?

显式选择除 id 列之外的所有列:

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

您的下一个问题可能是:

有没有办法在不明确列出所有列的情况下做到这一点?

答:不,我不这么认为。

如果你真的不想像 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;

漂亮,不快。但有效。

感谢 hobailey 提供了一个出色的免维护解决方案。

这是我最终使用的代码,它针对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

或者,如果您不想显式编写所有列(并且不想开始创建/删除表(,则可以获取表的列并自动构建查询:

//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

请注意,这使用MySQL的折旧代码,它应该是MySQLi。我相信它也可以改进,但这是我正在使用的,它运行得很好。

问题标题确实说明你想从 PHP 执行此操作。

我遇到了同样的问题,如果您更改表结构(添加/删除列(,写出所有列名既乏味又难以维护......而且我不喜欢使用临时表的解决方案。

我选择用从 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;
}

您甚至可以在重新插入之前对内部数据执行更多操作(取消设置其他列以忽略、编辑值等(。

PHP 中的另一种解决方案是复制同一表中的行,没有特定的列/例如主键 - 并且没有"临时表"和"显示来自...的列"-方法:

$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();

INSERT 是一个 SELECT 语句,因此语句中的值不是直接的 --> "real_escape_string"或类似的东西没有问题。

对于具有许多列的表,我使用类似于 Phius 想法的(是的,慢(方法。
我把它放在这里只是为了完整。

假设表"tbl"有一个定义如下的"id">

id INT 不为空AUTO_INCREMENT主键

然后,您可以按照以下步骤克隆/复制行:

  1. 创建 TMP 表

创建临时表tbl_tmp,如 TBL;

  1. 插入一个或多个要克隆/复制的条目

插入tbl_tmp从 TBL 中选择 * ...;

  1. 从"id"中删除自动增量标记

更改表tbl_tmp修改 ID INT;

  1. 删除主索引

更改表tbl_tmp删除主键;

  1. 更新您的唯一索引并将"id"设置为 0(步骤 6. 需要 0 才能工作(

更新tbl_tmp设置unique_value=?,id=0;

  1. 将修改后的行复制到"TBL"中,并自动生成"ID"。

插入到 TBL 中选择 * 从tbl_tmp;

  1. 清理(或仅关闭数据库连接(

放置表tbl_tmp;

如果您还需要克隆/复制其他表中的一些依赖数据,请执行上述操作对于每一行。在步骤 6 之后,您可以获取上次插入的密钥并使用它来使用相同的过程克隆/复制其他表中的从属行。

我很

惊讶有人没有提到使用phpMyAdmin来创建查询。因为这可以快速添加所有列,然后您只需将 id 设置为 null 或 o,如上所述 wlf。

这是迄今为止最简单的方法

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

我最近不得不做类似的事情,所以我想我发布了任何尺寸表的解决方案,包括示例。它只需要一个配置数组,几乎可以调整到任何大小的表。

$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;
}

这将输出如下内容:

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

最简单的只是复制记录的值

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

使用使记录的值重复,并添加新/更改某些列值的值,如"是"或"否">

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

假设表是user(id,email,user)的,并且因为你有一个WHERE子句,所以你不能使用MAX(id)+1

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

但请记住,在使用 INSERT 时应始终指定列名。

我想

在我的事件表中复制一行,发现马克的解决方案非常有用。我把它缩短了一点。

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;
}

和复制代码:

$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).'")'; 

这是复制任何表的记录的常规函数:

/**
 * @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();
}

我用这个...它会删除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;

由于表通常可以具有默认值(create_date等(,因此使用字段名称很有用,像这样的函数可以在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();
}}

如果您在数组中给出字段名称,它将返回它复制的 id。

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