我使用三表结构来处理多对多关系。我有一个表有人员列表,另一个表有项目列表。有时多个人拥有相同的项目,有时多个项目链接到同一个人,所以我设置了下表结构:
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
);
这允许我将多个项目链接到多个人,反之亦然。它还允许我从中间表中删除不必要的记录。
只要输入了新项目,所有工作都可以正常工作,但如果输入现有项目,则不会更新中间表,即使人员表格是。我也没有任何错误。
项目是以逗号分隔的文本条目,它们已展开并缩小为$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();
}
到此为止,一切正常。此时如果现有项已经在items表中,那么我的中间表不会更新。然而,people表更新得很好,而items表不需要更新,因为它已经包含了该项。
这是我尝试两种不同的方法来更新中间表的地方。
首先,我将select和insert查询分开。
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();
}
我做错了什么?
答案 0 :(得分:0)
这是因为您正在使用INSERT IGNORE
,因此如果该项目已经存在,则不会插入任何内容,您将获得0或上次成功插入的ID作为ID({{3} })。所以你需要检查的是受影响的行数。如果它为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不是那么明确,因为你没有,但你可以阅读这个per the docs来开始。
答案 1 :(得分:0)
对不起,我没有早点到达这里,但我已经弄清楚了,并且在此期间忙于工作。我想我会提供答案,以防将来有人遇到同样的问题。无论如何,问题在于我的SQL查询。
我有这个:
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();
}
我应该有这个:
elseif(!$itemid){
$sql="INSERT INTO people_items (pid,iid) VALUES (?,(SELECT id FROM items WHERE name IN (?)));";
$stmt=$conn->prepare($sql);
$stmt->bind_param('ss',$peopleid,$items[$i]);
$stmt->execute();
}