【问题标题】:MySQL/PHP check for duplicate before INSERTMySQL/PHP 在 INSERT 之前检查重复项
【发布时间】:2015-02-21 22:21:23
【问题描述】:

我正在尝试在执行 INSERT 语句之前检查 MySQL 中的现有条目。如果用户输入数据库中已有的名称(字段设置为唯一),则应提示他们重新输入名称。

我遇到的问题是,如果新条目与任何形式的记录匹配,则会显示错误消息并且不会发生 INSERT。

例如,如果用户输入 DUMMY_NEW 并且有一条 DUMMY_OLD 记录,即使 DUMMY_NEW 在表中不存在,他们也无法添加该记录。

我已经搜索并尝试了其他答案,但似乎无法让它发挥作用。

为了清楚起见,删除了无关位的代码:

//Create connection to database using mysqli
$conn = new mysqli($dbhost, $dbuser, $dbpass, $db);

//Set variables according to user input on previous form
$Server_Name = $_POST['Server_Name'];

//Check for duplicate server name - if exists inform user else run INSERT ($stmt)
$checkdup = "SELECT * FROM dcr_table WHERE SERVER_NAME = '".$Server_Name."'";
$dupresult = $conn->query($checkdup);

if($dupresult = 1)
{
        print "<br>Error! <p></p>";
        echo "" . $Server_Name . " already exists in the DCR";
        print "<p></p>Please check the Server Name and try again";
}
else {


//Define the INSERT statement
    $stmt = "INSERT INTO dcr_master (Server_Name, Description,..., ... , ... )";

//Execute the INSERT statement
    $conn->query($stmt);

//Success and return new id
    echo "<br><p></p>Record Added!<p></p>";
    echo "New id: " . mysqli_insert_id($conn);

//Drop the connection
    $conn->close();
};

编辑: 我知道注入漏洞。 MySQL 帐户只有表的 SELECT、INSERT 和 UPDATE 权限。最终用户必须提供密码,否则提交将失败。这是目前用户访问受限的小型应用程序。 MySQL 转义字符串将在当前问题解决后实现。

编辑 2: 使用 Hobo Sapiens 方法确实可以报告现有条目,但仍会向表中添加新(空)行。记录 ID 仍然自动递增,所以我得到的是 id#300 - 记录,id#301 - 空白,id#302 - 记录。这是 INSERT 语句中 IGNORE 的结果吗?

【问题讨论】:

  • $dupresult = 1 是赋值,而不是比较。您可能还想检查$conn-&gt;num_results($dupresult) &gt; 0 或类似的东西。 $dupresult 永远不会等于 1,因为它是 MySQLi 资源。除此之外(以及巨大的 SQL 注入),您的逻辑似乎还不错。
  • 另外,$dupresult 必须是某种资源,它只是意味着查询成功了......哦,天哪,这段代码完全是垃圾,你必须彻底检查它。
  • @Halcyon 此处的代码存在缺陷,因为它引入了可能的竞争条件。这不是在 MySQl 环境中测试唯一性的方法。唯一列应该这样标记(OP 说是这样),INSERT 使用IGNORE 执行,结果在affected_rows 中检查

标签: php mysql


【解决方案1】:

此声明:

if($dupresult = 1)

将始终返回 1。您应该首先检索第一个查询结果(如果有),如下所示:

$row=$dupresult->fetch_array(MYSQLI_NUM);

然后将结果与 NULL 进行比较:

if(!$row)

【讨论】:

  • $dupresult 是 MySQLi 资源,当转换为布尔值时,它将始终为 true
  • @Halcyon 除非是NULL,一旦被施放将永远是false。请删除您的反对票。
  • @Halcyon 而且由于您甚至没有花时间承认自己的错误,所以下次如果您甚至不知道自己在说什么,就不要到处投反对票。
  • @Halcyon 对不起,我再次查看了这个问题,发现是我错了。当我第一次写答案时,以及当我在它被否决后再次检查它时,我都确信$dupresult 是一个由$result-&gt;fetch_assoc() 之类的东西返回的数组。而且我知道您明确指出它是一个 MySQLi 资源,但不幸的是没有打开任何灯。我的道歉
  • 对不起。不过,我不是给你投反对票的人。投赞成票:D
【解决方案2】:

如果两个人同时尝试创建相同的 ame 而您没有正确处理后果,您的代码会创建竞争条件。

如果您已将SERVER_NAME 列设置为UNIQUE,那么您无需在执行INSERT 之前检查服务器名称的存在,因为MySQL 会为您执行此操作。使用INSERT IGNORE ad 在查询执行后检查受影响的行数以了解它是否有效:

//Create connection to database using mysqli
$conn = new mysqli($dbhost, $dbuser, $dbpass, $db);

//Set variables according to user input on previous form
$Server_Name = $_POST['Server_Name'];

//Define the INSERT statement with IGNORE keyword
    $stmt = "INSERT IGNORE INTO dcr_master (Server_Name, Description,..., ... , ... )";
    if ($conn->query($stmt) === false) {
        die("Database error:".$conn->error);
    }

// Check for success
    if ($conn->affected_rows == 0) {
        print "<br>Error! <p></p>";
        echo "" . $Server_Name . " already exists in the DCR";
        print "<p></p>Please check the Server Name and try again";
    } else {

//Success and return new id
        echo "<br><p></p>Record Added!<p></p>";
        echo "New id: " . $conn->insert_id;
    }

这是一个原子操作,因此没有竞争条件,它只涉及对数据库的一次调用。

我建议您使用 OOP 样式mysqli_*() 的程序样式,但不要混合使用它们。适用于 SQL 注入的常规警告。

【讨论】:

    【解决方案3】:

    使用mysqli_num_rows

    $row_cnt = $dupresult->num_rows;
    
    if ($row_cnt > 0) {
    
       echo "There is a matching record";
    
    }else {
    
       //insert into table
    
    }
    

    【讨论】:

    • PHP 在我添加该代码时报告错误。注意:尝试在第 48 行获取非对象的属性(包含 $row_cnt = $dupresult->num_rows; 的行)
    • 如果选择查询失败,可能会发生这种情况。你得到false 而不是[MySQLi resource]。每当查询失败时,您可能想抛出 Exception
    猜你喜欢
    • 2021-07-10
    • 2011-02-03
    • 2014-11-13
    • 2021-01-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-03
    相关资源
    最近更新 更多