レコードの連続挿入に便利なプレースホルダ
テキストデータなどから1行ずつ取り出してデータベースにレコード挿入する場合、同じSQLのINSERT文を値だけ変えて何度も実行することになります。
この時、プログラム上で都度SQL文を用意して値を埋め込んで(以下略)とやると、データベースはSQLを渡される度にその内容を「えーっとなになに…?」と解析せねばならないので、負担が大きく無駄です。
これを省力化できる仕組みが「プレースホルダ」です。
あらかじめ値部分だけ空けておいたSQLを用意してやり、あとは連続して値だけを放り込んで実行を繰り返させる。
ざっくり言うと、そういう仕組です。
(※以下、文例はPerl で書いておりますが、基本的な使い方は同じ。)
プレースホルダがないとき
# $file(ファイル) やら$sth(ステートメントハンドル)やらは既に用意してあります。 while ( my $line = readline $file ) { # 行が無くなるまで$file から行を1行ずつ読み出し my @values = split ';', $line; # セミコロン区切りの行を分解して配列に格納 my $sql = 'INSERT INTO my_table (column0, column1, column2 ) VALUES ('.$values[0].','.$values[1].','. $values[2].');'; # SQL に配列の値を埋め込み $sth->do($SQL) or die $!; # SQL実行 # 毎回新しいSQLを渡されるので、毎回構文解析等の処理が行われる。 } $sth->finish;
プレースホルダがあるとき
# $file(ファイル) やら$sth(ステートメントハンドル)やらは既に用意してあります。 my $SQL = 'INSERT INTO my_table (column0, column1, column2 ) VALUES (?,?,?);'; # SQLはあらかじめ用意。値を埋め込む場所には「?」を置いておく。 $sth->prepare($SQL); # SQLはループの外でprepare(準備)しておく。 while ( my $line = readline $file ) { # 行が無くなるまで$file から行を1行ずつ読み出し my @values = split ';', $line; # セミコロン区切りの行を分解して配列に格納 $sth->execute(@values) or die $!; # SQL実行 # SQLはprepare で用意されたものを使いまわすので、構文解析等の処理は1回だけ。 } $sth->finish;
キーが重複したら挿入(INSERT)じゃなくて更新(UPDATE)したい
さて、この便利なプレースホルダによるレコード挿入ですが、たまに「キーが重複したらINSERT(新規挿入)じゃなくてUPDATE(更新)させたいんだよね」という場合があります。
そんなときはINSERT ON DUPLICATE KEY UPDATE 構文を使います。
例えば、column0 がキーの場合、以下の様になります。
INSERTだけ
これだとキーになるcolumn0 の値が重複するとエラーが出てしまいます。
INSERT INTO my_table (column0, column1, column2) VALUES (?,?,?);
ON DUPLICATE KEY UPDATE 追加
こうすると、キーであるcolumn0 が重複した場合、column1 とcolumn2 の値は上書きされます。
INSERT INTO my_table (column0, column1, column2 ) VALUES (?,?,?) ON DUPLICATE KEY UPDATE column1 = VALUES(column1), column2 = VALUES(column2);
プレースホルダの値を更新値に指定する方法がピンと来なかったのですが、そこは気にせずにVALUESの中からカラム名で指定できるんですね。
参考
@kijtra さんの以下の記事を参考にさせて頂きました。
ありがとうございます。