我正在使用三表结构来处理多对多关系。我有一张表,其中包含人员列表,另一张表包含项目列表。有时多个人拥有相同的项目,有时多个项目链接到同一个人,因此我设置了以下表结构:
CREATE TABLE people (
id int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
fname varchar(128) NOT NULL,
lname varchar(128) NOT NULL,
);
CREATE TABLE items (
id int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
name varchar(128) NOT NULL UNIQUE,
);
UNIQUE 防止项目名称重复。
CREATE TABLE people_items (
pid int(11) NOT NULL,
iid int(11) NOT NULL,
FOREIGN KEY (pid) REFERENCES people(id)
ON UPDATE CASCADE
ON DELETE CASCADE
FOREIGN KEY (iid) REFERENCES items(id)
ON UPDATE CASCADE
ON DELETE CASCADE
);
这允许我将多个项目链接到多个人,反之亦然。它还允许我从中间表中删除不需要的记录。
只要输入一个新项目,一切正常,但如果输入一个现有项目,中间表不会更新,即使 people 表更新了。我也没有收到任何错误。
Items 是一个逗号分隔的文本条目,它被展开并小写成 $items
。
首先,我插入任何新项目并检索 id:
for ($i=0;$i<count($items);$i++){
$sql="INSERT IGNORE INTO items (name) VALUES (?);";
$stmt=$conn->prepare($sql);
$stmt->bind_param('s',$items[$i]);
$stmt->execute();
$itemid=$stmt->insert_id;
如果返回一个新的 id,则执行以下操作:
if ($itemid){
$sql="INSERT INTO people_items (pid,iid) VALUES (?,?);";
$stmt=$conn->prepare($sql);
$stmt->bind_param('ii',$peopleid,$itemid);//the $peopleid is acquired the same way that the $itemid is acquired above
$stmt->execute();
}
到目前为止,一切正常。此时,如果现有项目已经在项目表中,则我的中间表不会更新。然而,people 表更新得很好,items 表不需要更新,因为它已经包含了项目。
在这里我尝试了两种不同的方法来更新中间表。
首先,我将选择和插入查询分开。
elseif(!$itemid){
$sql="SELECT id,name FROM items WHERE name=?;";
$stmt=$conn->prepare($sql);
$stmt->bind_param('s',$items[$i]);
$stmt->execute();
$stmt->store_result();
$stmt->bind_result($iid,$name);
$stmt->fetch();
$sql="INSERT INTO people_items (pid,iid) VALUES (?,?);";
$stmt=$conn->prepare($sql);
$stmt->bind_param('ii',$pid,$iid);
$stmt->execute();
}
这是我的替代方法,它也不更新中间表:
elseif(!$itemid){
$sql="INSERT INTO people_items (pid, iid) SELECT id,name FROM items WHERE name IN (?);";
$stmt=$conn->prepare($sql);
$stmt->bind_param('s',$items[$i]);
$stmt->execute();
}
我做错了什么?
最佳答案
因为你使用的是INSERT IGNORE
,所以如果该项目已经存在,它不会插入任何东西,你会得到 0 或最后一次成功插入的 ID 作为 ID ( per the docs)。所以你需要检查的是受影响的行数。如果它是 0,则它被忽略,您可以从数据库中SELECT
它。如果它不是 0,那么您可以安全地插入最后一个 ID。
for ($i=0;$i<count($items);$i++){
$sql="INSERT IGNORE INTO items (name) VALUES (?);";
$stmt=$conn->prepare($sql);
$stmt->bind_param('s',$items[$i]);
$stmt->execute();
$itemid=$stmt->insert_id;
$affected_rows = $stmt->affected_rows;
if ($affected_rows !== 0) {
// insert
}
else {
// select and insert
}
}
旁注:
你在循环中插入元素的方式对性能来说很糟糕,特别是如果数据库与服务器不在同一个主机上(想想去超市:你去 N 次并自己拿到每件元素, 还是你只去一次就得到你的 N 件元素?)。您应该做的是在一个查询中插入所有元素。很明显,你的情况并不是那么明确,因为你有 ID 等等,但你可以阅读这个 here开始。
关于php - 当项目已经存在时更新中间表时出现问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45846304/