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 表中

数据库表中不建索引,在插入数据时,通过sql语句防止重复添加

添加联合key,防止重复插入

如何防止sql存储过程中的重复更新

mysql防止数据库重复

从 excel 导入数据时防止 SQL Server 2008 中的行重复