【问题标题】:Check if row exists in the database before inserting插入前检查数据库中是否存在行
【发布时间】:2015-09-22 09:56:49
【问题描述】:
$DBH = new PDO($dsn, $username, $password, $opt);

$DBH->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$DBH->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

$STH = $DBH->prepare("INSERT INTO requests (id,imdbid,msg) VALUES ('',:imdbid,:msg)");
$STH->bindParam(':imdbid', $_POST['imdbid']);
$STH->bindParam(':msg', $_POST['msg']);

$STH->execute();
echo "<p>Successfully Requested ".$_POST['imdbid']."! Thanks!</p>";

是否有一些 SQL 查询将检查和插入或什么? 我需要它来检查用户输入的内容是否已经在数据库中,所以如果用户输入的 imdbid 已经存在,那么它将不会继续插入任何内容。我该怎么做?我知道我可以做一个 fetch_all 并为它做一个 foreach 但这不是只有在你执行之后才有效吗?

【问题讨论】:

    标签: php mysql pdo


    【解决方案1】:

    最好对列设置约束以防止重复数据,而不是检查和插入。

    只需在 imdbid 上设置一个 UNIQUE 约束:

    ALTER TABLE `requests` ADD UNIQUE `imdbid_unique`(`imdbid`);
    

    这样做的原因是为了避免遇到race condition

    在完成检查和实际插入数据之间有一个小窗口,在这个小窗口中,可能会插入与要插入的数据发生冲突的数据。

    解决方案?使用约束并检查$DBH-&gt;error() 是否存在插入错误。如果有任何错误,您知道有重复,然后您可以通知您的用户。

    我注意到你正在使用它,$DBH-&gt;setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);。在这种情况下,您不需要检查-&gt;error(),因为 PDO 会抛出异常。只需像这样用 try 和 catch 包装你的执行:

    $duplicate = false;
    
    try {
        $STH->execute();
    } catch (Exception $e) {
        echo "<p>Failed to Request ".$_POST['imdbid']."!</p>";
        $duplicate = true;
    }
    
    if (!$duplicate)
        echo "<p>Successfully Requested ".$_POST['imdbid']."! Thanks!</p>";
    

    【讨论】:

    • 嘿,再次戴夫:P 无论如何,你为什么要同时添加 imdbid 和 msg 然后 _unique?如果 imdbid 已经在数据库中,我只希望它停止,但如果消息相同,我不介意。所以让我们说在我的数据库中我有:imdbid = 123 msg = 嘿他们输入 imdbid = 1234 msg = 嘿它仍然应该允许它被插入
    • 另外,当我添加唯一性时,究竟会发生什么?然后我是否使用 INSERT INTO 请求 (id,imdbid_unique,msg_unique) ?
    • 没问题,我已经编辑了上面的代码,只在imdbid 上设置了唯一约束。接下来发生的事情是您只需像往常一样插入,除非您检查数据库中的错误。如果$DBH-&gt;error() 出现错误,则说明您插入了重复数据。
    • 当我尝试执行 Alter Table SQL 时,我得到了这个:MySQL 说:#1170 - BLOB/TEXT 列 'imdbid' 在没有密钥长度的密钥规范中使用
    • 您可以将数据类型更改为 INT 或 VARCHAR 吗?您不能在 BLOB 或 TEXT 上放置 UNIQUE 约束。同样通常对于像 IMDB id 这样的 ID,您不需要 TEXT 或 BLOB 来保存它。 TEXT 通常用于帖子或 cmets。
    【解决方案2】:

    试试这个

    $DBH = new PDO($dsn, $username, $password, $opt);
    
    $DBH->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $DBH->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
    
    $STH = $DBH->prepare("INSERT INTO requests (id,imdbid,msg) VALUES ('',:imdbid,:msg) WHERE NOT EXISTS(SELECT imdbid FROM requests WHERE imdbid =:imdbid)");
    $STH->bindParam(':imdbid', $_POST['imdbid']);
    $STH->bindParam(':msg', $_POST['msg']);
    
    $STH->execute();
    echo "<p>Successfully Requested ".$_POST['imdbid']."! Thanks!</p>";
    

    Source

    【讨论】:

    • 不会只是添加它然后检查它是否不存在吗?
    • 我无法检查它的工作情况。但它不会添加关于 SELECT,因为这可能会返回 false。能不能实时查看。如果没有,请告诉我。
    【解决方案3】:

    只需在插入之前运行查询。

    如果找到脚本:

    $DBH = new PDO($dsn, $username, $password, $opt);
    
    $DBH->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $DBH->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
    
    $sql = 'SELECT COUNT(*) from requests WHERE imdbid = :imdbid';
    $stmt = $DBH->prepare($sql);
    $stmt->execute(array(':imdbid' => $_POST['imdbid']));
    
    if($stmt->fetchColumn()){ die('Already exist');}
    
    $STH = $DBH->prepare("INSERT INTO requests (id,imdbid,msg) VALUES ('',:imdbid,:msg)");
    $STH->bindParam(':imdbid', $_POST['imdbid']);
    $STH->bindParam(':msg', $_POST['msg']);
    
    $STH->execute();
    echo "<p>Successfully Requested ".$_POST['imdbid']."! Thanks!</p>";
    

    或者使 msg 字段唯一。

    使用存储过程:

    DELIMITER //
     CREATE PROCEDURE insert_request_msg(IN `p_imbd`, IN `p_msg`)
        IF NOT EXISTS (SELECT COUNT(*) from requests WHERE imdbid = p_imbd)
        BEGIN
            INSERT INTO requests (id,imdbid,msg) VALUES ('',p_imbd,p_msg)
        END
        END IF; //
     DELIMITER ;
    

    您可以像这样在一个查询中调用它:

    $STH = $DBH->prepare('
    call insert_request_msg(:imdbid,:msg)
    ');
    $STH->bindParam(':imdbid', $_POST['imdbid']);
    $STH->bindParam(':msg', $_POST['msg']);
    

    【讨论】:

    • 哪个更快,您的脚本还是 Daves?我只关心页面加载速度:P
    • 这不会是一个很大的速度差异你可以注意到但与 daves 的问题它会创建一个 execption 因为插入重复是错误的,我添加了更多示例以使用存储过程进行检查行的存在或不全部在一个查询中
    • 好的,我不明白,第二个代码是否将整个查询阻塞在一个中?如果我将调用 insert_request_msg(:imdbid,:msg) 放在 prepare() 中,我会将它放在哪里?另请注意,我试图让它检查 imdbid 是否已经在数据库中,如果在同一个 imdb 或不同的 imdb 上使用相同的消息,我可能不太在意。
    • 哦,好吧,我让它变得更简单了,现在检查我的编辑,你可以在 mysql 中运行整个块,以便创建存储过程,然后你就可以在一个查询中运行它
    • 好的,但请记住,正如我所说,我不在乎味精是否唯一,我只关心 imdbid 是否已经在数据库中 请您更新您的代码吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多