質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.78%

php pdo アクセスするテーブルごとに、$sql文を書き換えて実行させたいのですが、ご教示お願いします。

解決済

回答 4

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 779

bellevue

score 14

前提・実現したいこと

PHP,MySQLで、在庫管理のソフトウェアを作っています。
MySQLで、1つのデータベースsampledbを作成し、
複数のテーブルdb01,db02を作りました。
最初のレコードを作成するときに、同時に二つのテーブルに新規のレコードを作成しようと思います。
PHPで、pdo?を使ってinsert,update,deleteなどを実現しています。
最初は1つのテーブルdb01でうまくいきました。
仕様を変えてテーブルを2つに分けて、それぞれに共通のsysidというカラムを使い、他のカラムをそれぞれのテーブルに割り当てました。
最初のテーブルが
sysid,item1,item2,item3,item4
だったのを、

1つめのテーブルは、
sysid,item1,item2

2つめのテーブルは、
sysid,item3,item4

としたというイメージです。
最初に新規のレコードを作成しようとして次のようなことをしたいのですが問題が起きました。

$sql = "insert into DB01 (item1,item2) VALUES (:item1,:item2)";
$stmh = $pdo->prepare($sql);
$stmh->execute();
$pdo->commit();

$sql = "insert into DB02 (item3,item4) VALUES (:item3,:item4)";
$stmh = $pdo->prepare($sql);
$stmh->execute();
$pdo->commit();

複数のテーブルにアクセスするため、アクセスするテーブルごとに、$sql文を書き換えて実行させたいのですが、エラーが出ます。
「$sqlへの代入は1個のみにしてください」
という表示も出ています。

テーブルにアクセスするごとに書き換えて使用するということはできないのでしょうか?
**追記1**
最後に$sqlに複数のsql文を追加したのは、以下のようなものでした。

$sql = "insert into DB01 (00_C01_DB01__TohoRef, 00_C01_DB01__UketukeDate, 00_C01_DB01__GijutuTantoCode, 00_C01_DB01__GijutuTanto, 00_C01_DB01__JimuTantoCode, 00_C01_DB01__JimuTanto, 00_C01_DB01__Bikoid01) VALUES (:00_C01_DB01__TohoRef, :00_C01_DB01__UketukeDate, :00_C01_DB01__GijutuTantoCode, :00_C01_DB01__GijutuTanto, :00_C01_DB01__JimuTantoCode, :00_C01_DB01__JimuTanto, :00_C01_DB01__Bikoid01); insert into DB02A (00_C03_RP01_DB02A__ApcntCode) VALUES (:00_C03_RP01_DB02A__ApcntCode)";

**追記2**
こちらの追記もさせていただきます。create tableの部分をマークダウンにしました。不手際多く、ご迷惑をおかけしました。m(__)m

drop table DB01;
create table DB01(
sysid mediumint unsigned not null auto_increment,Lsysid mediumint,00_C01_DB01__TohoRef varchar(128),
00_C01_DB01__UketukeDate char(10),
00_C01_DB01__GijutuTantoCode char(20),
00_C01_DB01__GijutuTanto varchar(128),
...
primary key(sysid)) DEFAULT CHARSET=utf8;
drop table DB02A;
create table DB02A(
sysid mediumint unsigned not null auto_increment,Lsysid mediumint,00_C03_RP01_DB02A__ApcntCode char(20),
00_C03_RP01_DB02A__Apcnt varchar(128),
...
primary key(sysid)) DEFAULT CHARSET=utf8;
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • te2ji

    2019/04/25 10:02

    必要な情報まで削ってしまっています。
    可能であれば、再現性のある最小限のコードで提示してください。最小限に出来ないのであれば、全体を提示してください。
    今の状態だと回答がブレます。
    beginTransactionは?エラーは何が出している?バインドは?等々

    キャンセル

  • m6u

    2019/04/25 10:05

    関係するテーブルの構造をCREATE TABLE文で第三者も再現できるように示してほしい。また、sysidはなにかの連番なのか固定値なのか、それなのにINSERT INTO文に組み込まれていないのが不思議。default値やautoincrement値なら省略できますが、そうでないなら必ず値を与える必要があります。

    キャンセル

  • bellevue

    2019/04/25 14:21

    「$sqlへの代入は1個のみにしてください」というのは、NetBeansIDEで[の端に表示されるワーニングです。
    マークダウン、勉強します。すいません。
    全体は長かったものですから、確認したいポイントに絞りました。再現性を犠牲にしてしまい、申し訳ありません。
    sysidはautoincrementとしています。
    皆さん、関心を持っていただいて、ありがとうございます。

    キャンセル

回答 4

checkベストアンサー

+1

テーブルにアクセスするごとに書き換えて使用するということはできないのでしょうか?

できます。

「可変になるところ」を引数にした関数を作ると良いです。
そこは考えてやってみてください。(「可能か?」という質問なので)

あと既に指摘があるようにパラメータだけ指定してbindValueによる値のbindが行われていないためのエラーが起きてるのではないでしょうか。

サンプル(※動作未検証)

関数を用意

function connection(){
    return new PDO(xxxxx); //ここは自身の設定を反映すること
}
function createInsertSQL(string $table,array $item):string
{
    $clms = [];
    $values = [];
    foreach($item as $clm=>$value){
        $clms[] = $clm;
        $values[] = ':'.$clm;
    }
    return "insert into {$table} (".implode(',',$clms).") VALUES (".implode(',',$values).")";
}
function stmtBind(PDOStatement $stmt,array $item)
{
    foreach($item as $clm=>$value){
        $stmt->bindValue(':'.$clm, $value);
    }
}

1個ずつ実行

try{
    $pdo = connection();
    $pdo->beginTransaction();

    $table = 'DB01';
    $set = ['item1'=>1,'item2'=>2];
    $stmt = $pdo->prepare(createInsertSQL($table,$set));
    stmtBind($stmt,$set);

    if(!$stmt->execute()){
        throw new PDOException();
    }
    $table = 'DB02';
    $set = ['item3'=>3,'item4'=>4];
    $stmt = $pdo->prepare(createInsertSQL($table,$set));
    stmtBind($stmt,$set);
    if(!$stmt->execute()){
        throw new PDOException();
    }
    $pdo->commit();
}catch(PDOException $e){
    $pdo->rollBack();
    die(json_encode($e));
}

まとめて実行

try{
    $pdo = connection();
    $pdo->beginTransaction();

    $sqls = [];

    $set1 = ['item1'=>1,'item2'=>2];
    $sqls[] = createInsertSQL('DB01',$set1);

    $set2 = ['item3'=>3,'item4'=>4];
    $sqls[] = createInsertSQL('DB02',$set2);

    $stmt = $pdo->prepare(implode(';',$sqls),
        array(PDO::MYSQL_ATTR_MULTI_STATEMENTS=>true));
    stmtBind($stmt,array_merge($set,$set2));

    if(!$stmt->execute()){
        throw new PDOException();
    }
    $pdo->commit();
}catch(PDOException $e){
    $pdo->rollBack();
    die(json_encode($e));
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/04/26 05:35

    それはSQLの構文エラーですね。
    試したコードを質問本文にマークダウンで追記してください

    キャンセル

  • 2019/04/26 10:37

    以下のものをやってみました。非常に変則的な変数名の付け方をしており、見にくいと思います。申し訳ありません。複数文の代入も成功させたいですが、とりあえずは解決しております。お忙しいでしょうから、無視していただいて結構です。もし、お時間許すようでしたら、どこが間違っているのかご指摘いただけるとありがたいです。
    ```
    $sql = "insert into DB01 (00_C01_DB01__TohoRef, 00_C01_DB01__UketukeDate, 00_C01_DB01__GijutuTantoCode, 00_C01_DB01__GijutuTanto, 00_C01_DB01__JimuTantoCode, 00_C01_DB01__JimuTanto, 00_C01_DB01__Bikoid01) VALUES (:00_C01_DB01__TohoRef, :00_C01_DB01__UketukeDate, :00_C01_DB01__GijutuTantoCode, :00_C01_DB01__GijutuTanto, :00_C01_DB01__JimuTantoCode, :00_C01_DB01__JimuTanto, :00_C01_DB01__Bikoid01); insert into DB02A (00_C03_RP01_DB02A__ApcntCode) VALUES (:00_C03_RP01_DB02A__ApcntCode)";
    ```

    キャンセル

  • 2019/04/26 10:41

    コメントではなく、質問本文にマークダウンで追記してください

    キャンセル

+1

bindParamやbindValue が必要では?
テーブル定義はCREATE TABLE に修正した方が適切なコメントが付きやすいです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/04/25 14:21

    まず、エラーの意味をはっきりさせるために、シンプルに空白に近いレコードを一つ作ってみることにしました。うまくいってからbindなどをさせるつもりでした。ただ、1つめのテーブルではそういった処理を行ってレコードもインサートできました。2つめのテーブルで$sqlにsql文を代入するステートメントのところに来てfatalerrorが発生してしまいました。
    ありがとうございます。ご指摘の点は今後生かせるように努力してみます。

    キャンセル

+1

「$sqlへの代入は1個のみにしてください」のメッセージは
NetBeans IDEのおせっかい機能で、
$sqlに繰り返し値を代入しているけどいいのかい?と。
なにかの間違いで別の変数名を使いたいところで間違って$sqlに代入していませんか、というおせっかい。
問題なければ無視して良いけど、
変数名の書き間違いの可能性もあるのでしっかり見定めてから無視するように。

最初のテーブルが
sysid,item1,item2,item3,item4
だったのを、

1つめのテーブルは、
sysid,item1,item2

2つめのテーブルは、
sysid,item3,item4 

sysidはautoincrement

すごくヤバみを感じる。
「どうせ一連の動き」と甘く考えてそれぞれで1ずつ増えれば同じ番号が両方につくとでも思っちゃいないだろうか?

一つのテーブルだったときと違って、
別テーブルに別れた上でそれぞれのテーブルでsysidがautoincrementしているとね、
一方のみINSERT INTO文が成功してもう一方が失敗する場合や、
あるいは同時アクセス的にAさんのリクエストとBさんのリクエストがかち合って
順番が保証されなかったりする場合もないとはいえないので、
同じsysidを他方で使えるようにしないといけないのではないかと。

その場合、
PHP: PDO::lastInsertId - Manual
lastInsertId()を使って取得した値を他方のテーブルへのINSERT INTO文に使わないといけないはず。

それと、
prepare()
bindValue()
execute()
で一連の流れ。
PHP: PDOStatement::bindValue - Manual
プレースホルダーに変数を当てるのに必要。

1つ目のINSERT INTO文を実行してすぐcommit()してるけど、
2つ目の方で失敗したらどうなるんだろうな。
1つ目を実行する前に
PHP: PDO::beginTransaction - Manual
beginTransaction() して一貫性の確保が必要。
1つ目実行、2つ目実行して例外やエラーが発生しなかったことを確認した後、
やっとcommit()する。
途中で例外やエラーがあったらrollback()して一貫性を保証。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/04/26 04:26

    実際のもののcreatetableを貼り付けます。質問のものとだいぶ違うものですから。
    `drop table DB01;
    create table DB01(
    sysid mediumint unsigned not null auto_increment,Lsysid mediumint,00_C01_DB01__TohoRef varchar(128),
    00_C01_DB01__UketukeDate char(10),
    00_C01_DB01__GijutuTantoCode char(20),
    00_C01_DB01__GijutuTanto varchar(128),
    ...
    primary key(sysid)) DEFAULT CHARSET=utf8;
    drop table DB02A;
    create table DB02A(
    sysid mediumint unsigned not null auto_increment,Lsysid mediumint,00_C03_RP01_DB02A__ApcntCode char(20),
    00_C03_RP01_DB02A__Apcnt varchar(128),
    ...
    primary key(sysid)) DEFAULT CHARSET=utf8;`

    Lsysidというのは、sysidで同期をとるのはご指摘があったように無理があるかと思って、自分で同期を確保するために利用しようと考えていたカラムです。

    キャンセル

  • 2019/04/26 04:39

    ここではなく、質問を修正してください。

    キャンセル

  • 2019/04/26 15:27

    コメントにコードを書かれるとみづらいだけじゃなく他の回答者に失礼なので。
    そして、現状なにが問題なのかがぼやけているので質問文中に補足をお願いしたい。

    キャンセル

0

解決できました。以下のように4つを順番に実行させるということが必須だということなのかなと理解しました。途中をはしょったりするとだめなのではないでしょうか。皆様、お忙しい中で、いろいろ教えていただき、ありがとうございました。

$sql = "insert into DB02A (Lsysid) VALUES (:Lsysid)";
$stmh = $pdo->prepare($sql);
$stmh->bindValue(':Lsysid', $row['sysid'], PDO::PARAM_INT);
$stmh->execute();


sysidというのは、最初にレコードをインサートできた後で、lastInsertId()で得たものです。二つ目以降のテーブルでもsysidは使いますが、同期を確保するためにLsysidというものを用意し、そこに同期させる一つ目のテーブルのsysidを入れるようにしています。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.78%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • トップ
  • PHPに関する質問
  • php pdo アクセスするテーブルごとに、$sql文を書き換えて実行させたいのですが、ご教示お願いします。