2012-07-26 65 views
13

我的表有两个键,一个是自动递增ID(PRIMARY),另一个是项目名称(UNIQUE)。PHP MySQL使用主键和唯一键在同一个表中复制一行...

是否有可能在同一个表中复制一行?我曾尝试:

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)附加到项目名称,因为此字段也必须是唯一的?

+1

尝试选择除ID以外的所有行,假设您的ID是自动增量,它会自动更新。 – databyss 2012-07-26 01:04:35

+0

@databyss这会工作,但我正在寻找一种替代方法,因为此表有几百列。 – Norse 2012-07-26 01:05:29

+0

复制并粘贴列名称。 – databyss 2012-07-26 01:06:12

回答

36

选择所有列明确,除了ID列:

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

你的下一个问题可能会是:

有没有办法做到这一点没有明确列出所有的列?

答:不,我不这么认为。

+0

见@马克的回答的方式来做到这一点没有明确列出列查询的列名。 – 2016-01-21 14:24:25

19

如果你真的不想列出像马克的回答所有的表列,你可以试试这个:

CREATE TEMPORARY TABLE temp_tbl SELECT * FROM items WHERE id = '9198'; 
UPDATE temp_tbl SET id = id + 1; 
INSERT INTO items SELECT * FROM temp_tbl; 
DROP TABLE temp_tbl; 

不漂亮,也不快。但工作。

+3

+1嗯...有趣... :-) – 2012-07-26 01:51:13

+0

这个例子为我工作。我只是做了肯定,当我与多个用户使用它,我增加了一个额外部分的temp_tbl,所以这是该用户唯一的。 我还添加了以下行: UPDATE temp_tbl SET field_that_needs_to_use_a_title = CONCAT('DUPLICATE',field_that_needs_to_use_a_title)WHERE id ='9198'; – 2013-10-19 04:43:48

0

说表是user(id,email,user)因为你有一个WHERE子句不能使用MAX(id)+1:记住

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

熊虽然在使用INSERT时,你应该总是指定的列名。

1

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

假设,表 'TBL' 有像

ID INT定义的 '身份证' NOT NULL AUTO_INCREMENT PRIMARY KEY

然后你就可以克隆/按照以下步骤复制一行:

  1. 创建一个tmp目录表

CREATE TEMPORARY TABLE tbl_tmp LIKE tbl;

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

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

  1. 从 'ID'

ALTER TABLE除去AUTOINCREMENT标签tbl_tmp MODIFY ID INT;

  1. 降主索引

ALTER TABLE tbl_tmp DROP PRIMARY KEY;

  1. 更新您的唯一索引和设置的 'id' 为0(0所需的步骤6到工作)

UPDATE SET tbl_tmp unique_value = ?, ID = 0;

  1. 修改后的行复制到与被自动生成的 '身份证' TBL“。

INSERT INTO tbl SELECT * FROM tbl_tmp;

  1. 清理(或只是关闭DB连接)

DROP TABLE tbl_tmp;

如果您还需要克隆/复制其他表中的某些相关数据,请为每行执行上述 。在第6步之后,您可以获取最后插入的密钥,并使用该密钥克隆/使用相同的过程在其他表中复制依赖行。

3

另外,如果你不想明确写入所有列(而不想开始创建/删除表),你可以得到表的列,并自动生成查询:

//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。我相信它也可以得到改善,但这是我正在使用的,它工作得很好。

2

问题标题确实表明您想从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; 
} 

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

1

我很惊讶没有人提到使用phpMyAdmin创建查询。因为这会使添加所有列的速度变快,然后您只需将标识设置为null或o,如上所述,由wlf。

这是迄今为止最简单的方式做到这一点

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

感谢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 
+0

不漂亮,但做的工作!是行'$ mysqli-> real_escape_string($ R [$山口])'需要考虑的值已经在数据库中? – 2016-01-21 14:22:54

+1

是需要逃生 - 在文本引号试试 - 或使用PDO /界PARAMS – anoldermark 2017-02-28 11:30:08

0

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

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

这是复制的任何表的一个记录中的一般功能:

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

的内爆是南辕北辙! - - - - - - - - - - - - - - - - 应该是: $ sql = sprintf(“INSERT INTO $ table(%s)VALUES(%s)”,implode(',' ,$ cols),implode(',',$ values)); - - - - - - 否则,它是一个很好的解决方案 – anoldermark 2017-05-03 17:17:19

+0

无法删除此二次注释 - 我需要线上方突破 – anoldermark 2017-05-03 17:26:07

0

另一种解决方案在PHP用于复制一个行在没有特定列/例如的同一表格中主键 - 不会“临时表”和“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(); 

INSERT是SELECT语句,因此它们的值不会在声明中直接 - >没有问题与“real_escape_string”或类似的东西。