SlideShare a Scribd company logo
1 of 39
Download to read offline
Perl と SQL のいろいろ
Takuya Tsuchida(tsucchi)
概要

Perl でアプリケーションを開発している人は、SQL も書いている人が
多いのではないか、と思います。
本トークでは、初心者から中級者を対象に、Perl と SQL にまつわる
tips を紹介します。

初心者が陥りがちな良くない書き方をどのように改善していく
か、SQL にまつわる困ったことが起きたときにどのように対応する
か、簡単な SQL を簡単に済ませるにはどうしたらよいか、といったこ
とをお話したいと思います。
ちなみに...

超豪華裏番組開催中です...




体が3つくらい欲しいですねw
自己紹介


●
   名前: 土田 拓也
  ●
    tsucchi(twitter, github, CPAN)とか
  ●
    tsucchi1022(はてなとか)
●
  某印刷会社で Perl とか SQL とか書いてます
●
  去年も発表させていただきました
  ●
    あんなテスト、こんなテスト
    ●
      http://yapcasia.org/2011/talk/47
目次


●
 DBI/DBD のインストール
●
 プレースホルダ
●
 トランザクション
●
 SQLを見たい!
●
 SQLの置き場所
●
 DBI の意外と便利な機能など
●
 テストとチューニング
DBI/DBD のインストール
DBI/DBD のインストール          libmysqlclient とか
                      Oracle (Instant) Client とか
●
    慣れれば簡単だが、ハマるポイントが結構多い

1.クライアントライブラリを入れる
2.DBI を入れる
3.DBD ::* を入れる

※細かい手順はググればきっと出てくるので調べ
            MySQL なら DBD::mysql
                  •


てね          PostgreSQL なら DBD::pg
                  •

            Oracle なら DBD::Oracle
                  •
DBI/DBD のインストール

               環境によって対応方法は異なるので、
    ありがちなハマりどころ
    ●
              ググったりして頑張って解決してください


    1.クライアントライブラリを入れる
    → クライアントライブラリが入っていない or
    入れようとした際にうまくいかない

•
    2.DBI を入れる
    DB に実際に接続して行うテストが入っている事がある
•
    3.DBD ::* を入れる
    ログやメッセージを見て、それが確認できた場合は強制インストールでよい(と思う)
•   cpan なら force install, cpanm なら -f オプションを使う
     →インストール時に流れるテストコードが失敗
    する
DBI/DBD のインストール
ありがちなハマりどころ(2)
●




ありがちではないかも、ですが。。。

クライアントライブラリを入れるところでハマると、Pure
                  ま、メンテナは僕なのですがね!
               (一緒にメンテしてくれる方絶賛募集中!)
Perl のDBD が使いたくなるかもしれないが・・・

●
 必ず XS のもの(先ほど挙げたもの)を使うこと!
●
 逆に言うと、Pure Perl のドライバは絶対に使わないこと!
  ●
    DBD::mysqlPP(使っちゃダメ!)
  ●
    DBD::pgPP(使っちゃダメ!)
少なくとも、MySQL のドライバに関しては、過去セキュリティホールがありま
した。また、複雑なクエリを投げるとハングすることがあることが確認されて
います。
プレースホルダ
プレースホルダ

よくない例
●


     my $dbh = DBI->connect(...);
     my $sql = "SELECT id, name FROM employee WHERE id = $id";
     my $sth = $dbh->prepare($sql);
     $sth->execute();




                        変数をそのまま SQL 中に
                         埋め込むのはダメ!
プレースホルダ

なぜ良くないか
●

●
  セキュリティ
●
  $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();
プレースホルダ

文字列化?(サニタイズ?)
●

    my $sql = "SELECT id, name FROM employee WHERE id = '$id'";


●
    SQL の変数部分を文字列化して、この問題を防ごうとする
      のは止めましょう
    ●
      数値型に文字列を渡すと、インデックスが効かなくなるこ
       とがある(遅くなる。逆の場合も同じ)
    ●
      たとえば「' OR 1=1 --」という入力を渡すとこの場合も
       全件出力されてしまう(無意味)
プレースホルダ                                               ? がプレースホルダです

解決策:プレースホルダ
●


my $dbh = DBI->connect(...);
my $sql = "SELECT id, name FROM employee WHERE id = ?";
my $sth = $dbh->prepare($sql);
$sth->execute($id);


                     SQL で「?」で指定した値をここで渡す

●
 先ほどの怪しい入力を渡すとエラーになる
●
 型のミスマッチで遅くなることが無い
●
 使い方もそんなに難しくない
トランザクション
トランザクション

トランザクションって?
●


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件しか入らない
●
  成功時はすべてのデータが入ってほしい
●
  失敗時は(中途半端なデータができるくらいなら)データが
    無いほうがマシ
●
  これを実現するのがトランザクション
トランザクション
                       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 で囲んだ区間がトランザクションになる
●

 ●
   何らかの原因で処理が失敗すると、処理が無かったことに
    される(ロールバック)
 ●
   ただ、この書き方だとちょっと怪しいかな。
トランザクション

処理内容に応じて、明示的にロールバックさせる方が良い
●


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 したり
トランザクション

●
 ロールバックすると、begin_work() から rollback() まで
の処理が「なかったこと」になる
●
 ただ、この書き方もちょっと怪しい
●
 通常は Try::Tiny 等をつかって、エラー処理と組み合わせ
る
トランザクション
                              ●   エラー処理対象としたい部分をを 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 $_; #例外を上位に投げ直す
};
SQLを見たい!
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 変数がずれてるとか、そういった問題は見つけにく
     い
SQLを見たい!

$DBI::trace 変数
●


$ env DBI_TRACE=1 perl a.pl
DBI 1.620-nothread default trace level set to 0x0/1 (pid
28328 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
...
SQLを見たい!

●
 $DBI::trace 変数
●
 環境変数以外にも、$dbh にセットする方法もある
●
 割と手軽
●
 情報が豊富
  ●
    (DBI_TRACE=2 にするとさらに豊富になる)
  ●
    豊富過ぎて逆に見づらいことも。。。
●
  bind されている値は見えるのだが、分かりにくい
SQLを見たい!

DBIx::QueryLog
●


$ perl -MDBIx::QueryLog a.pl
[2012-08-24T12:27:47] [main] [0.000045] SELECT id, name FROM
detective WHERE id = '1234' at a.pl line 12

●
 上記の方法以外にも、スクリプトのどこかで use する方法
もある
●
 これも手軽
●
 実行時間も分かる
●
 bind されている値はもっとも見やすい
●
 SQL の中身を見る目的ならこれがベスト
SQLの置き場所
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 ...(検索条件いっぱい)
";

          あれ?この関数なにするんだっけ??
SQLの置き場所

●
 ヒアドキュメント?
●
 関数 or メソッドにする?
  ●
    何らかの事情で、モジュールを入れられない場
     合は有効かもしれない
●
  SQL::Library
●
  Data::Section::Simple
SQLの置き場所
             この ini ファイルのセクション風の
                     ものが SQL名
●
 SQL::Library
●
 SQL を別のファイルに置いて、簡単に取り出せる

[detective_detail]
/* 先ほどから挙げてる長い SQL。
このファイル名を detective.sql とします */
SELECT detective.first_name
     , detective.family_name
     , toys.name
...
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 の命名に気をつけないと、別ファイルになる分読みづら
くなることもあるので注意
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__ に書いて...
SQLの置き場所

Data::Section::Simple
●

 ●
   長いSQL が1行で呼び出せるので、本処理が見やすくなる
 ●
   同一ファイルに置くならベストと思われる
 ●
   mod_perl 環境では、__DATA__ が読めないため使えません


                       使いてーorz
               (私は mod_perl 環境を使っています)
DBI の意外と便利な機能など
なんじゃこれ?
    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 とか意味わかんないですよね?
DBI の意外と便利な機能など

SQL よりもビジネスロジックに集中したい
●

 ●
   ORM とか使うと良いかも
   ●
     Teng
     ●
       very simple ORMapper Teng(nihen さん)
     ●
       http://yapcasia.org/2012/talk/show/3570fad2-d484-
        11e1-964b-37a36aeab6a4
   ●
     DBIx::Class
   ●
     DBIx::Simple
   ●
     いろいろあるので、調べてみると良いと思います
テストとチューニング
テストとチューニング

●
 テスト
  ●
    DB をあまり意識したくない場合は DBD::Mock
  ●
    DB を意識したテストが必要な場合はデータを入れて頑張
      るしかない
●
  チューニング
  ●
    まずは NYTProf 使う
    ●
      原因は SQL では無いかもしれない
  ●
    原因が SQL の場合
    ●
      DBMS についてくるツールを使う
      ●
        MySQL なら EXPLAIN
    ●
      原因が分かったらあとはインデックス張ったりロジック
        を見直す
まとめ

●
 DBI/DBD インストールがんばろう
  ●
    ちゃんとしたドライバを使おう
●
  プレースホルダを使おう
  ●
    自前エスケープ絶対駄目!
●
  上手にトランザクションをかけよう
●
  DBIx::QueryLog 便利だよ!(xaicron++)
●
  SQL が長いときは上手く外に追い出してみよう
●
  ORM とかも検討してみよう
●
  テストは大変です
  ●
    でもそれはいつものことです!
●
  チューニング頑張ろう
  ●
    原因が SQL とは限らないから、切り分けからきっちりと
おしまい

ご清聴ありがとうございました!

More Related Content

What's hot

Flumeを活用したAmebaにおける大規模ログ収集システム
Flumeを活用したAmebaにおける大規模ログ収集システムFlumeを活用したAmebaにおける大規模ログ収集システム
Flumeを活用したAmebaにおける大規模ログ収集システムSatoshi Iijima
 
BinDataで バイナリデータを 楽に扱う
 BinDataで バイナリデータを 楽に扱う BinDataで バイナリデータを 楽に扱う
BinDataで バイナリデータを 楽に扱うRyouta Shirono
 
オントロジーとは?
オントロジーとは?オントロジーとは?
オントロジーとは?Kouji Kozaki
 
データ活用を俊敏に進めるためのDataOps実践方法とその高度化のためのナレッジグラフ活用の取り組み(NTTデータ テクノロジーカンファレンス 2020 ...
データ活用を俊敏に進めるためのDataOps実践方法とその高度化のためのナレッジグラフ活用の取り組み(NTTデータ テクノロジーカンファレンス 2020 ...データ活用を俊敏に進めるためのDataOps実践方法とその高度化のためのナレッジグラフ活用の取り組み(NTTデータ テクノロジーカンファレンス 2020 ...
データ活用を俊敏に進めるためのDataOps実践方法とその高度化のためのナレッジグラフ活用の取り組み(NTTデータ テクノロジーカンファレンス 2020 ...NTT DATA Technology & Innovation
 
PGCon 2023 参加報告(第42回PostgreSQLアンカンファレンス@オンライン 発表資料)
PGCon 2023 参加報告(第42回PostgreSQLアンカンファレンス@オンライン 発表資料)PGCon 2023 参加報告(第42回PostgreSQLアンカンファレンス@オンライン 発表資料)
PGCon 2023 参加報告(第42回PostgreSQLアンカンファレンス@オンライン 発表資料)NTT DATA Technology & Innovation
 
Vaasa marraskuu 2014
Vaasa marraskuu 2014Vaasa marraskuu 2014
Vaasa marraskuu 2014Outi Lammi
 
MonotaRO のデータ活用と基盤の過去、現在、未来
MonotaRO のデータ活用と基盤の過去、現在、未来 MonotaRO のデータ活用と基盤の過去、現在、未来
MonotaRO のデータ活用と基盤の過去、現在、未来 株式会社MonotaRO Tech Team
 
第3回ナレッジグラフ推論チャレンジ2020の紹介
第3回ナレッジグラフ推論チャレンジ2020の紹介第3回ナレッジグラフ推論チャレンジ2020の紹介
第3回ナレッジグラフ推論チャレンジ2020の紹介KnowledgeGraph
 
Elasticsearch as a Distributed System
Elasticsearch as a Distributed SystemElasticsearch as a Distributed System
Elasticsearch as a Distributed SystemSatoyuki Tsukano
 
Mongo DBを半年運用してみた
Mongo DBを半年運用してみたMongo DBを半年運用してみた
Mongo DBを半年運用してみたMasakazu Matsushita
 
カジュアルにMongo dbのbackup機能説明
カジュアルにMongo dbのbackup機能説明カジュアルにMongo dbのbackup機能説明
カジュアルにMongo dbのbackup機能説明Masakazu Matsushita
 
Kafka vs Pulsar @KafkaMeetup_20180316
Kafka vs Pulsar @KafkaMeetup_20180316Kafka vs Pulsar @KafkaMeetup_20180316
Kafka vs Pulsar @KafkaMeetup_20180316Nozomi Kurihara
 
StudyCo_DocumentAI による OCR と LLM で紙文書をデータ化する(試み)
StudyCo_DocumentAI による OCR と LLM で紙文書をデータ化する(試み)StudyCo_DocumentAI による OCR と LLM で紙文書をデータ化する(試み)
StudyCo_DocumentAI による OCR と LLM で紙文書をデータ化する(試み)Taku Yoshida
 
ARC-009_RDB 技術者のための NoSQL ガイド
ARC-009_RDB 技術者のための NoSQL ガイドARC-009_RDB 技術者のための NoSQL ガイド
ARC-009_RDB 技術者のための NoSQL ガイドdecode2016
 
恐怖!シェルショッカーの POSIX原理主義シェルスクリプト
恐怖!シェルショッカーの POSIX原理主義シェルスクリプト恐怖!シェルショッカーの POSIX原理主義シェルスクリプト
恐怖!シェルショッカーの POSIX原理主義シェルスクリプトRichie Shellshoccar
 
データ基盤グループを支えるチームビルディング
データ基盤グループを支えるチームビルディングデータ基盤グループを支えるチームビルディング
データ基盤グループを支えるチームビルディング株式会社MonotaRO Tech Team
 
PostgreSQL10を導入!大規模データ分析事例からみるDWHとしてのPostgreSQL活用のポイント
PostgreSQL10を導入!大規模データ分析事例からみるDWHとしてのPostgreSQL活用のポイントPostgreSQL10を導入!大規模データ分析事例からみるDWHとしてのPostgreSQL活用のポイント
PostgreSQL10を導入!大規模データ分析事例からみるDWHとしてのPostgreSQL活用のポイントNTT DATA OSS Professional Services
 

What's hot (20)

Flumeを活用したAmebaにおける大規模ログ収集システム
Flumeを活用したAmebaにおける大規模ログ収集システムFlumeを活用したAmebaにおける大規模ログ収集システム
Flumeを活用したAmebaにおける大規模ログ収集システム
 
BinDataで バイナリデータを 楽に扱う
 BinDataで バイナリデータを 楽に扱う BinDataで バイナリデータを 楽に扱う
BinDataで バイナリデータを 楽に扱う
 
オントロジーとは?
オントロジーとは?オントロジーとは?
オントロジーとは?
 
データ活用を俊敏に進めるためのDataOps実践方法とその高度化のためのナレッジグラフ活用の取り組み(NTTデータ テクノロジーカンファレンス 2020 ...
データ活用を俊敏に進めるためのDataOps実践方法とその高度化のためのナレッジグラフ活用の取り組み(NTTデータ テクノロジーカンファレンス 2020 ...データ活用を俊敏に進めるためのDataOps実践方法とその高度化のためのナレッジグラフ活用の取り組み(NTTデータ テクノロジーカンファレンス 2020 ...
データ活用を俊敏に進めるためのDataOps実践方法とその高度化のためのナレッジグラフ活用の取り組み(NTTデータ テクノロジーカンファレンス 2020 ...
 
PGCon 2023 参加報告(第42回PostgreSQLアンカンファレンス@オンライン 発表資料)
PGCon 2023 参加報告(第42回PostgreSQLアンカンファレンス@オンライン 発表資料)PGCon 2023 参加報告(第42回PostgreSQLアンカンファレンス@オンライン 発表資料)
PGCon 2023 参加報告(第42回PostgreSQLアンカンファレンス@オンライン 発表資料)
 
Vaasa marraskuu 2014
Vaasa marraskuu 2014Vaasa marraskuu 2014
Vaasa marraskuu 2014
 
MonotaRO のデータ活用と基盤の過去、現在、未来
MonotaRO のデータ活用と基盤の過去、現在、未来 MonotaRO のデータ活用と基盤の過去、現在、未来
MonotaRO のデータ活用と基盤の過去、現在、未来
 
第3回ナレッジグラフ推論チャレンジ2020の紹介
第3回ナレッジグラフ推論チャレンジ2020の紹介第3回ナレッジグラフ推論チャレンジ2020の紹介
第3回ナレッジグラフ推論チャレンジ2020の紹介
 
Elasticsearch as a Distributed System
Elasticsearch as a Distributed SystemElasticsearch as a Distributed System
Elasticsearch as a Distributed System
 
WiredTigerを詳しく説明
WiredTigerを詳しく説明WiredTigerを詳しく説明
WiredTigerを詳しく説明
 
Mongo DBを半年運用してみた
Mongo DBを半年運用してみたMongo DBを半年運用してみた
Mongo DBを半年運用してみた
 
カジュアルにMongo dbのbackup機能説明
カジュアルにMongo dbのbackup機能説明カジュアルにMongo dbのbackup機能説明
カジュアルにMongo dbのbackup機能説明
 
Linked Open Dataとは
Linked Open DataとはLinked Open Dataとは
Linked Open Dataとは
 
これがCassandra
これがCassandraこれがCassandra
これがCassandra
 
Kafka vs Pulsar @KafkaMeetup_20180316
Kafka vs Pulsar @KafkaMeetup_20180316Kafka vs Pulsar @KafkaMeetup_20180316
Kafka vs Pulsar @KafkaMeetup_20180316
 
StudyCo_DocumentAI による OCR と LLM で紙文書をデータ化する(試み)
StudyCo_DocumentAI による OCR と LLM で紙文書をデータ化する(試み)StudyCo_DocumentAI による OCR と LLM で紙文書をデータ化する(試み)
StudyCo_DocumentAI による OCR と LLM で紙文書をデータ化する(試み)
 
ARC-009_RDB 技術者のための NoSQL ガイド
ARC-009_RDB 技術者のための NoSQL ガイドARC-009_RDB 技術者のための NoSQL ガイド
ARC-009_RDB 技術者のための NoSQL ガイド
 
恐怖!シェルショッカーの POSIX原理主義シェルスクリプト
恐怖!シェルショッカーの POSIX原理主義シェルスクリプト恐怖!シェルショッカーの POSIX原理主義シェルスクリプト
恐怖!シェルショッカーの POSIX原理主義シェルスクリプト
 
データ基盤グループを支えるチームビルディング
データ基盤グループを支えるチームビルディングデータ基盤グループを支えるチームビルディング
データ基盤グループを支えるチームビルディング
 
PostgreSQL10を導入!大規模データ分析事例からみるDWHとしてのPostgreSQL活用のポイント
PostgreSQL10を導入!大規模データ分析事例からみるDWHとしてのPostgreSQL活用のポイントPostgreSQL10を導入!大規模データ分析事例からみるDWHとしてのPostgreSQL活用のポイント
PostgreSQL10を導入!大規模データ分析事例からみるDWHとしてのPostgreSQL活用のポイント
 

Similar to PerlとSQLのいろいろ

Perl暦およそ10年(?)の僕がデータベースを使えるようになるまでの昔話
Perl暦およそ10年(?)の僕がデータベースを使えるようになるまでの昔話Perl暦およそ10年(?)の僕がデータベースを使えるようになるまでの昔話
Perl暦およそ10年(?)の僕がデータベースを使えるようになるまでの昔話azuma satoshi
 
Let s database_testing
Let s database_testingLet s database_testing
Let s database_testingYuji Shimada
 
初心者向け SQLite の始め方
初心者向け SQLite の始め方初心者向け SQLite の始め方
初心者向け SQLite の始め方suno88
 
ビギナーだから使いたいO/Rマッパー ~Tengを使った開発~
ビギナーだから使いたいO/Rマッパー ~Tengを使った開発~ビギナーだから使いたいO/Rマッパー ~Tengを使った開発~
ビギナーだから使いたいO/Rマッパー ~Tengを使った開発~Akabane Hiroyuki
 
Web技術勉強会 第25回
Web技術勉強会 第25回Web技術勉強会 第25回
Web技術勉強会 第25回龍一 田中
 
Deep Learningと他の分類器をRで比べてみよう in Japan.R 2014
Deep Learningと他の分類器をRで比べてみよう in Japan.R 2014Deep Learningと他の分類器をRで比べてみよう in Japan.R 2014
Deep Learningと他の分類器をRで比べてみよう in Japan.R 2014Takashi J OZAKI
 
知って得する標準関数の使い方
知って得する標準関数の使い方知って得する標準関数の使い方
知って得する標準関数の使い方Soudai Sone
 
⑲jQueryをおぼえよう!その5
⑲jQueryをおぼえよう!その5⑲jQueryをおぼえよう!その5
⑲jQueryをおぼえよう!その5Nishida Kansuke
 
ウェブから情報をあつめる
ウェブから情報をあつめるウェブから情報をあつめる
ウェブから情報をあつめるShuhei Iitsuka
 
「Html sql」で図書館hpにアクセスしてみよう
「Html sql」で図書館hpにアクセスしてみよう「Html sql」で図書館hpにアクセスしてみよう
「Html sql」で図書館hpにアクセスしてみようKentaro Matsui
 
Sql world を支える技術
Sql world を支える技術Sql world を支える技術
Sql world を支える技術Oda Shinsuke
 
⑯jQueryをおぼえよう!その2
⑯jQueryをおぼえよう!その2⑯jQueryをおぼえよう!その2
⑯jQueryをおぼえよう!その2Nishida Kansuke
 
Lisp Tutorial for Pythonista : Day 4
Lisp Tutorial for Pythonista : Day 4Lisp Tutorial for Pythonista : Day 4
Lisp Tutorial for Pythonista : Day 4Ransui Iso
 
Sql database でも使えるほにゃらら
Sql database でも使えるほにゃららSql database でも使えるほにゃらら
Sql database でも使えるほにゃららOda Shinsuke
 
なかったらINSERTしたいし、あるならロック取りたいやん?
なかったらINSERTしたいし、あるならロック取りたいやん?なかったらINSERTしたいし、あるならロック取りたいやん?
なかったらINSERTしたいし、あるならロック取りたいやん?ichirin2501
 
第2回品川Redmine勉強会(日本語全文検索)
第2回品川Redmine勉強会(日本語全文検索)第2回品川Redmine勉強会(日本語全文検索)
第2回品川Redmine勉強会(日本語全文検索)Masanori Machii
 

Similar to PerlとSQLのいろいろ (20)

Perl暦およそ10年(?)の僕がデータベースを使えるようになるまでの昔話
Perl暦およそ10年(?)の僕がデータベースを使えるようになるまでの昔話Perl暦およそ10年(?)の僕がデータベースを使えるようになるまでの昔話
Perl暦およそ10年(?)の僕がデータベースを使えるようになるまでの昔話
 
Let s database_testing
Let s database_testingLet s database_testing
Let s database_testing
 
初心者向け SQLite の始め方
初心者向け SQLite の始め方初心者向け SQLite の始め方
初心者向け SQLite の始め方
 
ビギナーだから使いたいO/Rマッパー ~Tengを使った開発~
ビギナーだから使いたいO/Rマッパー ~Tengを使った開発~ビギナーだから使いたいO/Rマッパー ~Tengを使った開発~
ビギナーだから使いたいO/Rマッパー ~Tengを使った開発~
 
Web技術勉強会 第25回
Web技術勉強会 第25回Web技術勉強会 第25回
Web技術勉強会 第25回
 
Deep Learningと他の分類器をRで比べてみよう in Japan.R 2014
Deep Learningと他の分類器をRで比べてみよう in Japan.R 2014Deep Learningと他の分類器をRで比べてみよう in Japan.R 2014
Deep Learningと他の分類器をRで比べてみよう in Japan.R 2014
 
Gorinphp0729
Gorinphp0729Gorinphp0729
Gorinphp0729
 
Gorinphp0729
Gorinphp0729Gorinphp0729
Gorinphp0729
 
知って得する標準関数の使い方
知って得する標準関数の使い方知って得する標準関数の使い方
知って得する標準関数の使い方
 
swooleを試してみた
swooleを試してみたswooleを試してみた
swooleを試してみた
 
⑲jQueryをおぼえよう!その5
⑲jQueryをおぼえよう!その5⑲jQueryをおぼえよう!その5
⑲jQueryをおぼえよう!その5
 
Tdd
TddTdd
Tdd
 
ウェブから情報をあつめる
ウェブから情報をあつめるウェブから情報をあつめる
ウェブから情報をあつめる
 
「Html sql」で図書館hpにアクセスしてみよう
「Html sql」で図書館hpにアクセスしてみよう「Html sql」で図書館hpにアクセスしてみよう
「Html sql」で図書館hpにアクセスしてみよう
 
Sql world を支える技術
Sql world を支える技術Sql world を支える技術
Sql world を支える技術
 
⑯jQueryをおぼえよう!その2
⑯jQueryをおぼえよう!その2⑯jQueryをおぼえよう!その2
⑯jQueryをおぼえよう!その2
 
Lisp Tutorial for Pythonista : Day 4
Lisp Tutorial for Pythonista : Day 4Lisp Tutorial for Pythonista : Day 4
Lisp Tutorial for Pythonista : Day 4
 
Sql database でも使えるほにゃらら
Sql database でも使えるほにゃららSql database でも使えるほにゃらら
Sql database でも使えるほにゃらら
 
なかったらINSERTしたいし、あるならロック取りたいやん?
なかったらINSERTしたいし、あるならロック取りたいやん?なかったらINSERTしたいし、あるならロック取りたいやん?
なかったらINSERTしたいし、あるならロック取りたいやん?
 
第2回品川Redmine勉強会(日本語全文検索)
第2回品川Redmine勉強会(日本語全文検索)第2回品川Redmine勉強会(日本語全文検索)
第2回品川Redmine勉強会(日本語全文検索)
 

PerlとSQLのいろいろ

  • 1. Perl と SQL のいろいろ Takuya Tsuchida(tsucchi)
  • 2. 概要 Perl でアプリケーションを開発している人は、SQL も書いている人が 多いのではないか、と思います。 本トークでは、初心者から中級者を対象に、Perl と SQL にまつわる tips を紹介します。 初心者が陥りがちな良くない書き方をどのように改善していく か、SQL にまつわる困ったことが起きたときにどのように対応する か、簡単な SQL を簡単に済ませるにはどうしたらよいか、といったこ とをお話したいと思います。
  • 4. 自己紹介 ● 名前: 土田 拓也 ● tsucchi(twitter, github, CPAN)とか ● tsucchi1022(はてなとか) ● 某印刷会社で Perl とか SQL とか書いてます ● 去年も発表させていただきました ● あんなテスト、こんなテスト ● http://yapcasia.org/2011/talk/47
  • 5. 目次 ● DBI/DBD のインストール ● プレースホルダ ● トランザクション ● SQLを見たい! ● SQLの置き場所 ● DBI の意外と便利な機能など ● テストとチューニング
  • 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 のドライバに関しては、過去セキュリティホールがありま した。また、複雑なクエリを投げるとハングすることがあることが確認されて います。
  • 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 で「?」で指定した値をここで渡す ● 先ほどの怪しい入力を渡すとエラーになる ● 型のミスマッチで遅くなることが無い ● 使い方もそんなに難しくない
  • 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 $_; #例外を上位に投げ直す };
  • 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.pl DBI 1.620-nothread default trace level set to 0x0/1 (pid 28328 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 FROM detective WHERE id = '1234' at a.pl line 12 ● 上記の方法以外にも、スクリプトのどこかで use する方法 もある ● これも手軽 ● 実行時間も分かる ● bind されている値はもっとも見やすい ● 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 環境を使っています)
  • 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 ● いろいろあるので、調べてみると良いと思います
  • 37. テストとチューニング ● テスト ● DB をあまり意識したくない場合は DBD::Mock ● DB を意識したテストが必要な場合はデータを入れて頑張 るしかない ● チューニング ● まずは NYTProf 使う ● 原因は SQL では無いかもしれない ● 原因が SQL の場合 ● DBMS についてくるツールを使う ● MySQL なら EXPLAIN ● 原因が分かったらあとはインデックス張ったりロジック を見直す
  • 38. まとめ ● DBI/DBD インストールがんばろう ● ちゃんとしたドライバを使おう ● プレースホルダを使おう ● 自前エスケープ絶対駄目! ● 上手にトランザクションをかけよう ● DBIx::QueryLog 便利だよ!(xaicron++) ● SQL が長いときは上手く外に追い出してみよう ● ORM とかも検討してみよう ● テストは大変です ● でもそれはいつものことです! ● チューニング頑張ろう ● 原因が SQL とは限らないから、切り分けからきっちりと