2009-05-13 6 views
5

私は、複数のメソッドのそれぞれ(クエリ)に複数のパラメータを含むことができるクエリを持っています。私はそれをより保守可能にするためにファイルサイズと行数を減らそうとしています。以下、このような発生である:Perlで複数のデータベースクエリを効率化するにはどうすればよいですか?

$sql_update = qq { UPDATE database.table 
        SET column = 'UPDATE!' 
        WHERE id = ? 
       }; 

$sth_update = $dbh->prepare($sql_update); 

if ($dbh->err) { 
    my $error = "Could not prepare statement. Error: ". $dbh->errstr ." Exiting at line " . __LINE__; 
    print "$error\n"; 
    die; 
} 

$sth_rnupdate->execute($parameter); 

if ($dbh->err) { 
    my $error = "Could not execute statement. Error: ". $dbh->errstr ." Exiting at line " . __LINE__; 
    print "$error\n"; 
    die; 
} 

これは単なる一例であり、但し、で渡されるだけつのパラメータを含む様々な他の選択例がある、しかし、二つ以上のパラメータを使用して、いくつかのもあります。私は、関数/メソッドにすべてをカプセル化して、パラメータの配列を渡し、パラメータをexecute()関数にどのように入れるのだろうと思っていますか?

可能であれば、SQLクエリとパラメータを渡すだけで、フェッチされたレコードへの参照を取得するという方法を書くことができました。これはまったく安全ですか?

答えて

6

行数と保守可能なコードが唯一の目標である場合は、いくつかの素晴らしいORMフレームワーク/ライブラリのいずれかを使用することをお勧めします。 Class::DBIDBIx::Classは、2つの良い出発点です。場合によっては、あなたはこれらのモジュールを学ぶために余分な時間を費やすことに心配しています - そうではありません。スタートアップと生産性を得るためには午後1時しかかかりませんでした。

Table->retrieve(id => $parameter)->column('UPDATE!')->update; 

ザ・ダウンのみ側(つまりあれば)これらのフレームワークの非常に複雑なSQL文はあなたにいくつかの追加がかかる場合があります書き込みカスタムメソッド学習を必要とすることである:例えば、クラス:: DBIを使用して、あなたの例では、ただ1行です時間を過ごすのはあまりありません。

+0

これは非常に有益です。 Class :: DBIモジュールにはコアperlが付属していますか? (unix用) –

+0

いいえ、あなたはそれをインストールする必要があります。 "install Class :: DBI"と入力し、CPANシェル内のすべての依存関係に「y」を付けて応答すると、それがインストールされます。 –

2

"実行"機能は、すべてのパラメータを含む配列を受け入れます。

あなたはちょうどあなたが行われている実行したいとハンドルいるの文を示すための方法を見つけなければならない...

あなたが新しいものを作成した場合ので、あなたの文はどこかにハンドルを維持するために非常に良いだろう毎回 "準備する"というメリットを取り除くたびにそれを準備してください。

すべての行を返すことについては、( "fetchrow_hashref push"のような)大きな結果セットに注意してください。あなたのすべての記憶を食べてください!

+0

私はあなたが言っていることを見ます - 私はこれを少し調べなければなりません。すべての私のステートメントは、私が上で指定したコードを模倣しますが、唯一異なるのはパラメターとステートメント自体です。 –

2

ここRaiseErrorの使用を含むように編集閉鎖/キーワード名(コンパイルが、それ以外はテストされていない)により、ハッシュに保存されている匿名のサブルーチンを使用して、簡単なアプローチです:

# define cached SQL in hash, to access by keyword 
# 
sub genCachedSQL { 
    my $dbh = shift; 
    my $sqls = shift; # hashref for keyword => sql query 
    my %SQL_CACHE; 
    while (my($name,$sql) = each %$sqls) { 
    my $sth = $dbh->prepare($sql); 
    $SQL_CACHE{$name}->{sth} = $sth; 

    $SQL_CACHE{$name}->{exec} = sub { # closure for execute(s) 
     my @parameters = @_; 
     $SQL_CACHE{$name}->{sth}->execute(@parameters); 

     return sub { # closure for resultset iterator - check for undef 
      my $row; eval { $row = $SQL_CACHE{$name}->{sth}->fetchrow_arrayref(); }; 
      return $row; 
     } # end resultset closure 

    } # end exec closure 

    } # end while each %$sqls 

    return \%SQL_CACHE; 

} # end genCachedSQL 


my $dbh = DBI->connect('dbi:...', { RaiseError => 1 }); 

# initialize cached SQL statements 
# 
my $sqlrun = genCachedSQL($dbh, 
{'insert_table1' => qq{ INSERT INTO database.table1 (id, column) VALUES (?,?) }, 
    'update_table1' => qq{ UPDATE database.table1 SET column = 'UPDATE!' WHERE id = ? }, 
    'select_table1' => qq{ SELECT column FROM database.table1 WHERE id = ? }}); 

# use cached SQL 
# 
my $colid1 = 1; 
$sqlrun->{'insert_table1'}->{exec}->($colid1,"ORIGINAL"); 
$sqlrun->{'update_table1'}->{exec}->($colid1); 
my $result = $sqlrun->{'select_table1'}->{exec}->($colid1); 
print join("\t", @$_),"\n" while(&$result()); 

my $colid2 = 2; 
$sqlrun->{'insert_table1'}->{exec}->($colid2,"ORIGINAL"); 

# ... 
+0

これは優れています。私はこのソリューションを少し実装してみようと思います。 –

+1

喜んで貢献する - 間違った実行テストケースのためにevalがどのように動作するかを確実にテストすること – bubaker

+0

クイック質問Bubaker、 このクエリはfetchall_arrayref()を実行し、結果としてすべてのレコードをフェッチします。平均して約9列の30万レコードを超過しています。 このクエリでfetchrow_arrayref()またはfetchrow_hashrefを使用して個々のレコードを繰り返し処理する方法はありますか? –

1

私はbubakerのに非常に感心このためにクロージャを使用する例

コードベースを小さくして保守性を向上させるというのが当初の目標だった場合、元のコードから削除するよう声を掛けていると思っています。 CDBIやDBICなどに提供しています(両方とも偉大な図書館にもかかわらず)。)

$dbhは、そのコードのほとんどが消え、属性でRaiseErrorセットでインスタンス化されていた場合:

$sql_update = qq { UPDATE database.table 
        SET column = 'UPDATE!' 
        WHERE id = ? 
       }; 
$sth_update = $dbh->prepare($sql_update); 
$sth_update->execute($parameter); 

私は元のコードでのエラー処理はあなたのことを多くを追加していることを見ることができませんRaiseErrorによって生成されたdieのバニラからは得られませんが、重要な場合は、DBIのマンページのHandleError属性を参照してください。

さらに、そのようなステートメントが再利用されていない場合(しばしばそれらを準備することの目的、最適化の方法をキャッシュすること、プレースホルダを使用してSQLインジェクションを軽減すること) doを使用しますか?

$dbh->do($sql_update, \%attrs, @parameters); 
4

1回のデータベース呼び出しごとにエラーをチェックする意味がありません。どのように退屈!

代わりに、データベースに接続するときにRaiseErrorオプションをtrueに設定します。その後、データベースエラーが発生すると、例外がスローされます。それをキャッチしないと(eval {}ブロック内で)、あなたのプログラムはあなたが上で手動で行っていたのと同様のメッセージで死にます。

関連する問題