如何使用 PDO 防止重复记录插入

How to prevent duplicate record inserts with PDO

我想尝试设置一些东西,如果用户已经提交了部门密钥和当前日期,它将停止插入记录。它会给出一个错误,说输入了重复的记录再试一次。

这是我插入页面的一部分,但它不起作用

$stmt= $db->prepare("INSERT INTO tbl_lighting(Department, 
    areaCode, offtime, gytime, ftime, ini, 
    sat_ob_department, sat_ib_department,
    sat_ob_onTime,sat_ib_onTime,
    sat_ob_offTime, sat_ib_offTime,
    ob_signature,ib_signature, 
    deptkey, comments,ib_comments,ob_comments,Requestdate

 ) Values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)WHERE NOT EXISTS 
(SELECT * FROM tbl_lighting where deptkey = $deptkey AND Requestdate= CAST(GETDATE() AS DATE)");

$stmt->bindParam(1, $_POST["Department"]);
$stmt->bindParam(2, $_POST["areaCode"]);
$stmt->bindParam(3, $_POST["offtime"]);
$stmt->bindParam(4, $_POST["gytime"]);
$stmt->bindParam(5, $_POST["ftime"]);
$stmt->bindParam(6, $_POST["ini"]);
$stmt->bindParam(7, $_POST["sat_ob_department"]);
$stmt->bindParam(8, $_POST["sat_ib_department"]);
$stmt->bindParam(9, $_POST["sat_ob_onTime"]);
$stmt->bindParam(10, $_POST["sat_ib_onTime"]);
$stmt->bindParam(11, $_POST["sat_ob_offTime"]);
$stmt->bindParam(12, $_POST["sat_ib_offTime"]);
$stmt->bindParam(13, $_POST["ob_signature"]);
$stmt->bindParam(14, $_POST["ib_signature"]);
$stmt->bindParam(15, $_POST["deptkey"]);
$stmt->bindParam(16, $_POST["comments"]);
$stmt->bindParam(17, $_POST["ib_comments"]);
$stmt->bindParam(18, $_POST["ob_comments"]);
$stmt->bindParam(19, $_POST["Requestdate"]);


$stmt->execute();

这是我的提交表单

<form action = "insert_process.php" onsubmit="return validateForm()" name="form" id="form" method ="post" class="style1">

<table align="center" id="tfhover" class="tftable" border="1">
<br><br>
<tr>
<td colspan="7"><h1 align="center">Lighting Schedule Form</h1></td>
</tr>

<tr>
<th>Department</th><th style="width: 75px">Area Code</th><th style="width: 144px">Off Time</th><th>Grave Yard On Time</th><th>First Shift OnTime</th><th width="125px">Comments Or Date By</th><th>Initials</th></tr>

<tr>

    <!--First row accross on the table-->
<td><select name="Department" id="Department" >
            <option value ="">Please select ...</option>
            <option value ="Upstairs Hang East">Upstairs Hang East</option>
            <option value ="Upstairs Hang West">Upstairs Hang West</option>
            <option value ="RDR">RDR</option>
            </select></td>
<td style="width: 75px"><input id="areaCode" name="areaCode" onkeydown="return false;" type="text"/></td>
<td style="width: 144px"><input class="offtime" id="offtime" name="offtime"  type="text" /></td>
<td><input id="gytime" name="gytime" type="text"></td>
<td><input id="ftime" name="ftime" type="text" ></td>
<td><input id="comments" name="comments" type="text"></td>
<td><input id="ini" name="ini" type="text" style="width: 68px" /></td>
</tr>


<input id="deptkey" name="deptkey" onkeydown="return false;" type="hidden"/>

    <!--end-->

也许你必须像这样逃避你是 php 变量 $deptkey,并且像这样大写 WHERE 条件:


$stmt= $db->prepare("INSERT INTO tbl_lighting(Department, 
    areaCode, offtime, gytime, ftime, ini, 
    sat_ob_department, sat_ib_department,
    sat_ob_onTime,sat_ib_onTime,
    sat_ob_offTime, sat_ib_offTime,
    ob_signature,ib_signature, 
    deptkey, comments,ib_comments,ob_comments,Requestdate</p>

<p>) Values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)WHERE NOT EXISTS 
(SELECT * FROM tbl_lighting WHERE deptkey = '".$deptkey."' AND Requestdate= CAST(GETDATE() AS DATE)");
</pre>

问题的本质是停止重复记录,我将解释为什么使用 PDO 的唯一键和异常是正确的方法。

数据库维护关系并存储数据。数据库的其中一项工作是处理并发和存储数据。通过创建唯一键来防止数据库中的重复记录,因此根本不允许此类记录出现在数据模型中。数据库处理同时连接的多个用户,这些用户依次插入一些数据。

我看到人们所做的是 select 插入一条记录,然后检查它是否存在以及是否返回 0 行 - 他们继续插入。这是一种糟糕的方法,不仅会损害性能,而且常常无法防止重复记录。这是因为 PHP(或任何语言)和 MySQL 服务器(或任何其他数据库服务器)之间存在轻微延迟。当您 select 编辑数据并完成检查时,已经过了一小段时间 - 比方说 1 毫秒。在那段时间里,另一个用户可能已经连接并经历了相同的过程(select,检查是否有 0 行,如果有 0 插入)。由于这两个用户同时在做同样的事情,他们都会得到关于结果的信息——因为 PHP 进程和 MySQL 之间的微小延迟。反过来,您最终可能会得到重复的记录。您还花了时间 select 处理一些东西并通过套接字连接发送它。

让数据库处理所有工作要快得多。由于由于唯一约束而无法进行插入,因此仅插入并让插入失败要容易得多("expensive" 比 select 插入并签入 PHP 少得多)。数据库还负责并发性或 "multiple users at the same time"。这意味着您的数据库知道其数据的状态,最好让它继续这样做。您最终的代码也会更少。

你的算法应该是这样的:

  • 获取输入
  • 准备声明
  • 执行插入
  • 如果出现异常,则存在重复记录(您可以检查数据库返回的代码以确保它是因为重复键或其他原因)

Fred 在他的评论中所说的是正确的,也是真正防止重复插入的唯一正确方法。此外,由于您使用的是准备好的语句,因此无需在语句中指定任何变量。只需绑定所有内容,让 PDO 负责清理用户输入。