PerlとSQLのいろいろ
Upcoming SlideShare
Loading in...5
×

Like this? Share it with your network

Share
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
4,948
On Slideshare
3,921
From Embeds
1,027
Number of Embeds
7

Actions

Shares
Downloads
16
Comments
0
Likes
7

Embeds 1,027

http://tsucchi.github.com 638
http://yapcasia.org 182
http://localhost 137
http://tsucchi.github.io 66
https://twitter.com 2
http://s.deeeki.com 1
http://webcache.googleusercontent.com 1

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Perl と SQL のいろいろTakuya Tsuchida(tsucchi)
  • 2. 概要Perl でアプリケーションを開発している人は、SQL も書いている人が多いのではないか、と思います。本トークでは、初心者から中級者を対象に、Perl と SQL にまつわるtips を紹介します。初心者が陥りがちな良くない書き方をどのように改善していくか、SQL にまつわる困ったことが起きたときにどのように対応するか、簡単な SQL を簡単に済ませるにはどうしたらよいか、といったことをお話したいと思います。
  • 3. ちなみに...超豪華裏番組開催中です...体が3つくらい欲しいですねw
  • 4. 自己紹介● 名前: 土田 拓也 ● tsucchi(twitter, github, CPAN)とか ● tsucchi1022(はてなとか)● 某印刷会社で Perl とか SQL とか書いてます● 去年も発表させていただきました ● あんなテスト、こんなテスト ● http://yapcasia.org/2011/talk/47
  • 5. 目次● DBI/DBD のインストール● プレースホルダ● トランザクション● SQLを見たい!● SQLの置き場所● DBI の意外と便利な機能など● テストとチューニング
  • 6. DBI/DBD のインストール
  • 7. DBI/DBD のインストール libmysqlclient とか Oracle (Instant) Client とか● 慣れれば簡単だが、ハマるポイントが結構多い1.クライアントライブラリを入れる2.DBI を入れる3.DBD ::* を入れる※細かい手順はググればきっと出てくるので調べ MySQL なら DBD::mysql •てね PostgreSQL なら DBD::pg • Oracle なら DBD::Oracle •
  • 8. DBI/DBD のインストール 環境によって対応方法は異なるので、 ありがちなハマりどころ ● ググったりして頑張って解決してください 1.クライアントライブラリを入れる → クライアントライブラリが入っていない or 入れようとした際にうまくいかない• 2.DBI を入れる DB に実際に接続して行うテストが入っている事がある• 3.DBD ::* を入れる ログやメッセージを見て、それが確認できた場合は強制インストールでよい(と思う)• cpan なら force install, cpanm なら -f オプションを使う →インストール時に流れるテストコードが失敗 する
  • 9. DBI/DBD のインストールありがちなハマりどころ(2)●ありがちではないかも、ですが。。。クライアントライブラリを入れるところでハマると、Pure ま、メンテナは僕なのですがね! (一緒にメンテしてくれる方絶賛募集中!)Perl のDBD が使いたくなるかもしれないが・・・● 必ず XS のもの(先ほど挙げたもの)を使うこと!● 逆に言うと、Pure Perl のドライバは絶対に使わないこと! ● DBD::mysqlPP(使っちゃダメ!) ● DBD::pgPP(使っちゃダメ!)少なくとも、MySQL のドライバに関しては、過去セキュリティホールがありました。また、複雑なクエリを投げるとハングすることがあることが確認されています。
  • 10. プレースホルダ
  • 11. プレースホルダよくない例● my $dbh = DBI->connect(...); my $sql = "SELECT id, name FROM employee WHERE id = $id"; my $sth = $dbh->prepare($sql); $sth->execute(); 変数をそのまま SQL 中に 埋め込むのはダメ!
  • 12. プレースホルダなぜ良くないか●● セキュリティ● $id が外部からくる値(たとえばフォームの入力)の場合、 どんな値が渡されるか分からない ● たとえば「0 OR 1=1」のような値が渡されると、テーブ ルの情報をすべて抜かれてしまう ● SQLインジェクション ● 他のテーブルに何かされたり、データを盗まれたり、 破壊される場合も my $dbh = DBI->connect(...); my $sql = "SELECT id, name FROM employee WHERE id = $id"; my $sth = $dbh->prepare($sql); $sth->execute();
  • 13. プレースホルダ文字列化?(サニタイズ?)● my $sql = "SELECT id, name FROM employee WHERE id = $id";● SQL の変数部分を文字列化して、この問題を防ごうとする のは止めましょう ● 数値型に文字列を渡すと、インデックスが効かなくなるこ とがある(遅くなる。逆の場合も同じ) ● たとえば「 OR 1=1 --」という入力を渡すとこの場合も 全件出力されてしまう(無意味)
  • 14. プレースホルダ ? がプレースホルダです解決策:プレースホルダ●my $dbh = DBI->connect(...);my $sql = "SELECT id, name FROM employee WHERE id = ?";my $sth = $dbh->prepare($sql);$sth->execute($id); SQL で「?」で指定した値をここで渡す● 先ほどの怪しい入力を渡すとエラーになる● 型のミスマッチで遅くなることが無い● 使い方もそんなに難しくない
  • 15. トランザクション
  • 16. トランザクショントランザクションって?●my $dbh = DBI->connect($dsn, $user, $pass, { AutoCommit => 1, ... } );my $sth = $dbh->prepare("INSERT INTO detective (id, name) VALUES (?, ?)");$sth->execute(1, シャーロック);$sth->execute(2, ネロ);$sth->execute(3, エリー);$sth->execute(4, コーデリア);● 例えば、3番目の INSERT の途中でプロセスがクラッシュすると、中途半端なデータになってしまう ● 4件入るはずが2件しか入らない● 成功時はすべてのデータが入ってほしい● 失敗時は(中途半端なデータができるくらいなら)データが 無いほうがマシ● これを実現するのがトランザクション
  • 17. トランザクション begin_work()で開始begin_work/commit●my $dbh = DBI->connect($dsn, $user, $pass, { AutoCommit => 1, ... } );$dbh->begin_work();my $sth = $dbh->prepare("INSERT INTO detective (id, name) VALUES (?, ?)");$sth->execute(1, シャーロック);$sth->execute(2, ネロ);$sth->execute(3, エリー);$sth->execute(4, コーデリア); commit で確定$dbh->commit();begin_work/commit で囲んだ区間がトランザクションになる● ● 何らかの原因で処理が失敗すると、処理が無かったことに される(ロールバック) ● ただ、この書き方だとちょっと怪しいかな。
  • 18. トランザクション処理内容に応じて、明示的にロールバックさせる方が良い●my $dbh = DBI->connect($dsn, $user, $pass, { AutoCommit => 1, ... } );my $sth = $dbh->prepare("INSERT INTO detective (id, name) VALUES (?, ?)");$dbh->begin_work(); # トランザクション開始 何らかの条件に応じて..,$sth->execute(1, シャーロック);$sth->execute(2, ネロ);$sth->execute(3, エリー);$sth->execute(4, コーデリア);if( has_toys() ) { #探偵さんがトイズを持って入れば OK $dbh->commit(); # トランザクションOK} commit したり...else { #トイズが無いので探偵失格 $dbh->rollback(); # ロールバック} rollback したり
  • 19. トランザクション● ロールバックすると、begin_work() から rollback() までの処理が「なかったこと」になる● ただ、この書き方もちょっと怪しい● 通常は Try::Tiny 等をつかって、エラー処理と組み合わせる
  • 20. トランザクション ● エラー処理対象としたい部分をを try {} で囲む ● エラー処理を catch に書くTry::Tiny との組み合わせ●use Try::Tiny;my $dbh = DBI->connect($dsn, $user, $pass, { AutoCommit => 1, ... } );my $sth = $dbh->prepare("INSERT INTO detective (id, name) VALUES (?, ?)");try { $dbh->begin_work(); # トランザクション開始 # 何か処理(1) $sth->execute(1, シャーロック); $sth->execute(2, ネロ); $sth->execute(3, エリー); ● こんな感じで、エラー時にロールバックする $sth->execute(4, コーデリア); ● この場合、処理(1)や(2)でエラーがあった場合もロールバックされる # 何か処理(2) $dbh->commit(); # トランザクションOK} catch { $dbh->rollback; #エラーが起きたので rollback # 必要ならその他エラー処理 die $_; #例外を上位に投げ直す};
  • 21. SQLを見たい!
  • 22. SQLを見たい!みんな大好きコンビ print デバッグ & Data::Dumper●my $dbh = DBI->connect(...);my $sql = "SELECT id, name FROM detective WHERE id = ?";use Data::Dumper; warn Dumper($sql, @binds);my $sth = $dbh->prepare($sql);$sth->execute(@binds);● 手軽● 問題となってる SQL の場所が分かっている場合はある程度有効● bind されている値は見づらい ● bind 変数がずれてるとか、そういった問題は見つけにく い
  • 23. SQLを見たい!$DBI::trace 変数●$ env DBI_TRACE=1 perl a.plDBI 1.620-nothread default trace level set to 0x0/1 (pid28328 pi 0) at DBI.pm line 276 via a.pl line 5... <- prepare(SELECT id, name FROM detective WHERE id= ?)= ( DBI::st=HASH(0x15a13d8) ) [1 items] at a.pl line 11 <- execute(1234)= ( 0E0 ) [1 items] at a.pl line 12...
  • 24. SQLを見たい!● $DBI::trace 変数● 環境変数以外にも、$dbh にセットする方法もある● 割と手軽● 情報が豊富 ● (DBI_TRACE=2 にするとさらに豊富になる) ● 豊富過ぎて逆に見づらいことも。。。● bind されている値は見えるのだが、分かりにくい
  • 25. SQLを見たい!DBIx::QueryLog●$ perl -MDBIx::QueryLog a.pl[2012-08-24T12:27:47] [main] [0.000045] SELECT id, name FROMdetective WHERE id = 1234 at a.pl line 12● 上記の方法以外にも、スクリプトのどこかで use する方法もある● これも手軽● 実行時間も分かる● bind されている値はもっとも見やすい● SQL の中身を見る目的ならこれがベスト
  • 26. SQLの置き場所
  • 27. SQLの置き場所SQL が長いと、コードが読みにくくなることがある●my $sql = " こんな感じの長いSQLがあると...SELECT detective.first_name , detective.family_name , toys.name , ...(カラムの情報いっぱい) FROM detective LEFT JOIN toys ON toys.id = detective.toys_id ...(JOIN がいっぱい) WHERE ...(検索条件いっぱい)"; あれ?この関数なにするんだっけ??
  • 28. SQLの置き場所● ヒアドキュメント?● 関数 or メソッドにする? ● 何らかの事情で、モジュールを入れられない場 合は有効かもしれない● SQL::Library● Data::Section::Simple
  • 29. SQLの置き場所 この ini ファイルのセクション風の ものが SQL名● SQL::Library● SQL を別のファイルに置いて、簡単に取り出せる[detective_detail]/* 先ほどから挙げてる長い SQL。このファイル名を detective.sql とします */SELECT detective.first_name , detective.family_name , toys.name...
  • 30. SQLの置き場所 さっきの SQL を呼び出すPerl コードSQL::Library● SQL ファイル名use SQL::Library;my $sql_lib = SQL::Library->new({ lib => detective.sql });my $sql = $sql_lib->retr( detective_detail ); SQL 名● 長いSQL を考えずに、数行のコードで呼び出せるので、本処理が読みやすくなる● SQL の命名に気をつけないと、別ファイルになる分読みづらくなることもあるので注意
  • 31. SQLの置き場所Data::Section::Simple●use Data::Section::Simple qw(get_data_section);my $sql = get_data_section(employee_detail);warn $sql; セクション名を指定して SQL を呼び出す__DATA__@@ employee_detail セクションの名前を指定SELECT employee.first_name , employee.family_name , employee.age , ...(カラムの情報いっぱい) FROM employee LEFT JOIN xx ON ... ...(JOIN がいっぱい) WHERE ...(検索条件いっぱい) SQL を __DATA__ に書いて...
  • 32. SQLの置き場所Data::Section::Simple● ● 長いSQL が1行で呼び出せるので、本処理が見やすくなる ● 同一ファイルに置くならベストと思われる ● mod_perl 環境では、__DATA__ が読めないため使えません 使いてーorz (私は mod_perl 環境を使っています)
  • 33. DBI の意外と便利な機能など
  • 34. なんじゃこれ? DBI の意外と便利な機能など my $row_href = $dbh->selectrow_hashref($sql, undef, @binds); my @rows = @{ $dbh->selectall_arrayref($sql, { Slice => {} }, @binds) }; 1行だけとってきたり... $dbh->do($sql); ● いい加減に insert 投げたり 全行hashref で取ったり● 結構便利 ● DBMS のコマンドを直接なげたりただちょっと癖がある●● 第二引数の undef とか意味わかんないですよね?
  • 35. DBI の意外と便利な機能などSQL よりもビジネスロジックに集中したい● ● ORM とか使うと良いかも ● Teng ● very simple ORMapper Teng(nihen さん) ● http://yapcasia.org/2012/talk/show/3570fad2-d484- 11e1-964b-37a36aeab6a4 ● DBIx::Class ● DBIx::Simple ● いろいろあるので、調べてみると良いと思います
  • 36. テストとチューニング
  • 37. テストとチューニング● テスト ● DB をあまり意識したくない場合は DBD::Mock ● DB を意識したテストが必要な場合はデータを入れて頑張 るしかない● チューニング ● まずは NYTProf 使う ● 原因は SQL では無いかもしれない ● 原因が SQL の場合 ● DBMS についてくるツールを使う ● MySQL なら EXPLAIN ● 原因が分かったらあとはインデックス張ったりロジック を見直す
  • 38. まとめ● DBI/DBD インストールがんばろう ● ちゃんとしたドライバを使おう● プレースホルダを使おう ● 自前エスケープ絶対駄目!● 上手にトランザクションをかけよう● DBIx::QueryLog 便利だよ!(xaicron++)● SQL が長いときは上手く外に追い出してみよう● ORM とかも検討してみよう● テストは大変です ● でもそれはいつものことです!● チューニング頑張ろう ● 原因が SQL とは限らないから、切り分けからきっちりと
  • 39. おしまいご清聴ありがとうございました!