MySQLデータベースにINSERT文で行を挿入しようとしたときの一意キー制約によるエラーを無視する
一意キー制約
MySQLデータベースを使っていて、列が1つだけの表にどんどんレコードを追加していきたい、 ただし重複したデータは登録したくないとします。
テーブル作成時に該当の列に一意キー制約をかけておくと
mysql> create table tbl_sometest (url varchar(255), constraint unique ct_sometest(url) ); Query OK, 0 rows affected (0.11 sec)
重複したデータを登録しようとしても以下のようにエラーになって登録できません。
mysql> insert into tbl_sometest (url) values ('http://www.example.com'); Query OK, 1 row affected (0.00 sec) mysql> select * from tbl_sometest; +------------------------+ | url | +------------------------+ | http://www.example.com | +------------------------+ 1 row in set (0.00 sec) mysql> insert into tbl_sometest (url) values ('http://www.example.com'); ERROR 1062 (23000): Duplicate entry 'http://www.example.com' for key 'ct_sometest'
エラーコード1062は MySQLのドキュメント に記載されています。
Perl によるコード
言い方を変えればデータ重複の検査をMySQLにまかせてスクリプト側では とにもかくにもINSERT文を実行すればよい、とも言えそうです。 ただしその場合はINSERT時のデータ重複によるエラーとそれ以外の予期せぬエラーを分けて扱いたい。 データ重複によるエラーは言わば「予期されたエラー」なので単に無視したいわけです。
そこで次のようなコードを書きました。
use DBI; # 略 my $dbh = DBI->connect($dsn, $dbUser, $dbPass); my $query = qq{ INSERT INTO tbl_sometest(url) VALUES (?) }; my $sth = $dbh->prepare($query); my $param = 'http://www.example.com'; my $res = $sth->execute($param); if (not defined $res) { if ($sth->err == 1062 and $sth->errstr =~ /ct_sometest/) { ; # It is nothing } else { ERROR "string : ".$sth->errstr; die; } }
コード中の「ct_sometest」はテーブル作成時に決めた一意キー制約の名前です。
DBI が出すエラーメッセージを抑制する
ところがこれでも標準エラー出力に 1062 のエラーメッセージが出ます。 どうやらDBIが内部で出すもののようで、これを抑制するには、 perldoc DBI によれば次のようにします。
{ local $sth->{PrintError} = 0; # localize and turn off for this block $param = 'http://www.example.com'; $res = $sth->execute($param); if (not defined $res) { ERROR "string : ".$sth->errstr; die; } }