SQL 防止重复插入
Posted
技术标签:
【中文标题】SQL 防止重复插入【英文标题】:SQL Prevent Duplicate INSERT 【发布时间】:2016-02-19 19:52:48 【问题描述】:我有一个脚本,我已经设置了一个 CRON,它通过 JSON (cURL) 从第 3 方服务器获取值
现在每次 cron 运行时都会插入一条全新的记录。导致重复,并导致我手动删除重复。
我将如何防止重复,只更新丢失的信息或与 $var 值不同的信息?
我想做什么
如果新值不是旧值,则使用旧值,否则使用新值;
$prep_stmt = "SELECT * FROM members WHERE record NOT LIKE record=? ";
$stmt = $mysqli->prepare($prep_stmt);
if ($stmt)
$stmt->bind_param('s');
$stmt->execute();
$stmt->store_result();
if ($stmt->num_rows !== 1)
if ($insert_stmt = $mysqli->prepare("
INSERT INTO members (
start_date
)
VALUES (?)"))
$insert_stmt->bind_param('s',$repStartDate);
if (! $insert_stmt->execute()) header('Location: ../error.php?err=Registration failure: INSERT');
【问题讨论】:
阅读INSERT ... ON DUPLICATE KEY UPDATE 更新了我的 OP 以获取关于 INSERT on Dup 的修订问题和我的尝试? @草莓 @Saty 我已经更新了我的 OP。介意看看我做的是否正确吗? @Strawberry UPDATE 查询是什么意思?你在说UPDATE table SET c=c+1 WHERE a=1;
@Strawberry 文档中没有明确说明UPDATE c=c+1;
怎么办@
【参考方案1】:
如果您在记录存在时不想更新,则可以使用Insert Ignore
,如下所示。
mysql> SELECT * FROM visit;
Empty set (0.00 sec)
mysql> INSERT IGNORE INTO visit (user_id, total_visit) VALUES (32, 1);
Query OK, 1 row affected (0.01 sec)
mysql> INSERT IGNORE INTO visit (user_id, total_visit) VALUES (32, 1);
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT * FROM visit;
+----+---------+-------------+
| id | user_id | total_visit |
+----+---------+-------------+
| 1 | 32 | 1 |
+----+---------+-------------+
1 row in set (0.00 sec)
如果您想在记录存在时更新,则可以使用On Duplicate Key Update
,如下所示。
mysql> INSERT IGNORE INTO visit (user_id, total_visit) VALUES (32, 1) ON DUPLICATE KEY UPDATE total_visit = total_visit + VALUES(total_visit);
Query OK, 1 row affected (0.00 sec)
mysql> SELECT * FROM visit;
+----+---------+-------------+
| id | user_id | total_visit |
+----+---------+-------------+
| 1 | 32 | 1 |
+----+---------+-------------+
1 row in set (0.00 sec)
mysql> INSERT IGNORE INTO visit (user_id, total_visit) VALUES (32, 1) ON DUPLICATE KEY UPDATE total_visit = total_visit + VALUES(total_visit);
Query OK, 2 rows affected (0.00 sec)
mysql> SELECT * FROM visit;
+----+---------+-------------+
| id | user_id | total_visit |
+----+---------+-------------+
| 1 | 32 | 2 |
+----+---------+-------------+
1 row in set (0.00 sec)
FULL EXAMPLE is here
【讨论】:
我已更新我的 OP 以询问首选方法和原因 你必须重构你的代码。此外,您应该避免对原始问题进行重大更新,因为您正在使答案和 cmets 对当前状态来说是多余的!【参考方案2】:使用替换而不是插入:
REPLACE INTO members (...) VALUES (...)
如果它是新数据,这将创建一个新行(如插入),如果该行是表现有条目的副本,则会更新现有数据。
通过查看主键字段和唯一键来找到重复项。因此,如果您的数据数据是重复的,例如用户名匹配然后使用户名成为唯一索引或您的主键。
更多文档可以在https://dev.mysql.com/doc/refman/5.7/en/replace.html找到
P.S.:INSERT ON DUPLICATE KEY UPDATE 也是一个有效的解决方案。
【讨论】:
我已经更新了我的 OP 以询问首选方法以及为什么@maxhb 使用你的方法会导致重复 正如我所写:如果您将所有字段组成的重复项定义为主键或唯一键,则不会产生重复项。你必须“告诉”mysql 是什么让两行重复。 我将使用我当前的代码更新我的 OP,而不会出现缺失值。如果您可以编辑答案以直观地向我展示,我不明白您在说什么... 要在名称列上创建索引,请使用以下 sql 语句:CREATE UNIQUE INDEX nameIndex ON members (name(255));【参考方案3】:我看不出这个最小示例未能解决问题的哪一部分:
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table (id INT AUTO_INCREMENT PRIMARY KEY,constant CHAR(1) NOT NULL);
INSERT INTO my_table VALUES (1,'a'),(2,'b'),(3,'a');
INSERT INTO my_table (id,constant) VALUES(1,'b') ON DUPLICATE KEY UPDATE constant = VALUES(constant);
INSERT INTO my_table (id,constant) VALUES(4,'a') ON DUPLICATE KEY UPDATE constant = VALUES(constant);
SELECT * FROM my_table;
+----+----------+
| id | constant |
+----+----------+
| 1 | b |
| 2 | b |
| 3 | a |
| 4 | a |
+----+----------+
【讨论】:
放弃了 NOT NULL(在 pk 旁边冗余)......即它不能为空。主要是想获得Copy Editor点。没有别的了:) @Levi 这似乎是一个不错的答案【参考方案4】:我最终编写了另一个 if 语句来检查传入的唯一值是否存在以及现有的 db 值是否存在并将其留空以防止它导入重复项。我还编写了一个单独的文件来更新我收到的(新)和数据库中的(旧)值之间的区别,这实际上对我的应用程序非常有用。
这是我对遇到此问题的其他人的回答:)
$prep_stmt = "SELECT * FROM table WHERE column_keys=?";
$stmt = $mysqli->prepare($prep_stmt);
if ($stmt)
$stmt->bind_param('s',$varvalues);
$stmt->execute();
$stmt->store_result();
if ($stmt->num_rows == 1)
if ($insert_stmt = $mysqli->prepare(""))
$insert_stmt->bind_param('');
if (! $insert_stmt->execute())
echo 'shits broke';
else if ($insert_stmt = $mysqli->prepare("
INSERT INTO table (column_keys)
VALUES (?)")) // you will need a ? per column seperate by a , (?,?,?...?)
$insert_stmt->bind_param('s',
$varvalues
); // you will also need to bind a 's' (string) 'i' for num, etc per $var value.
if (! $insert_stmt->execute()) echo 'shits broke'; //lol
我偶然发现了一个简单的错误报告技巧,它帮助我清理了一些我忽略的东西。只需将它放在文件的顶部,或者您要调试的上方;)
error_reporting(E_ALL);
【讨论】:
以上是关于SQL 防止重复插入的主要内容,如果未能解决你的问题,请参考以下文章
如何防止将重复数据插入到值为多个的 SQL Server 表中