2017-08-23 3 views
1

多対多の関係を処理するために3つのテーブル構造を使用しています。私は人のリストを持つテーブルと項目のリストを持つテーブルを持っています。時々、複数の人が同じアイテムを持っていると私は、次の表の構造を設定して、時には倍数項目が同じ人物にリンクされています:アイテムがすでに存在するときに中間テーブルを更新する際に問題が発生する

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になります。新しい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テーブルにはアイテムが既に含まれているため、更新する必要はありません。

ここでは、中間テーブルを更新する2つの異なる方法を試しました。

最初に、私は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

申し訳ありませんが、私は早くこれに着手しませんでしたが、私はそれを理解し、その間に仕事で忙しかったです。私は、誰かが将来同じ問題を抱えている場合に備えて回答を提供すると考えています。とにかく、問題は私のSQLクエリでした。

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(); 
} 
:私はこれを持っている必要があります

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

INSERT IGNOREを使用しているため、アイテムが既に存在する場合は何も挿入されず、最後の成功した挿入のIDまたはIDがID(per the docs)になります。だからあなたが確認する必要があるのは影響を受けた行数です。 0の場合は無視され、SELECT DBから取得できます。 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 
    } 
} 

追記:

あなたがループ内でアイテムを挿入する方法は、(スーパーマーケットに行くと思うDBは、サーバと同じホストにない場合は特に、パフォーマンスのためにひどいです:DOあなたはN回回り、各アイテムを単独で入手するか、一度行ってNアイテムを手に入れますか?)。何をすべきかは、すべての要素を1つのクエリに挿入することです。明らかに、IDとそれ以外の場合は、あなたのケースでは明確なカットではありませんが、これについてはhereについて読むことができます。

関連する問題