3

私はPerlとDBIで小さなスニペットを書いています(SQLite yay!)

クエリが実行されるテーブル名と同じファイル名を持つテキストファイルにいくつかの特定のクエリを記録したいと思います。

結果をテキストファイルにダンプするために使用するコードは次のとおりです。

sub dumpResultsToFile {
    my ( $query ) = @_;

    # Prepare and execute the query
    my $sth = $dbh->prepare( $query );
    $sth->execute();

    # Open the output file
    open FILE, ">results.txt" or die "Can't open results output file: $!";

    # Dump the formatted results to the file
    $sth->dump_results( 80, "\n", ", ", \*FILE );

    # Close the output file
    close FILE or die "Error closing result file: $!\n";
}

これが私がこれを呼ぶ方法です:

dumpResultsToFile ( <<"    END_SQL" );
    SELECT TADA.fileName, TADA.labelName
    FROM   TADA
    END_SQL

私が効果的に望んでいるのは、「results.txt」(上記でハードコードされている)に移動する代わりに、「TADA.txt」に移動する必要があることです。

これがテーブル「HAI」と「LOL」の間の結合である場合、結果セットは「HAI.LOL.txt」に書き込まれる必要があります。

私が言っていることは、DBIで魔法を使っても可能ですか?

テーブルのSQLクエリを解析せずに実行したいのですが、 SQLクエリでソーステーブル名を取得するために広く使用され、デバッグされた関数がある場合は、それも機能します。

私が欲しいのは、それがどのクエリ出力を保持しているかについてのヒントを与えるファイル名を持っていることです。今のところ、テーブル名に基づいて分類するのは良い方法のようです。

4

3 に答える 3

4

おそらくそうではありません。SQL 生成コードのアプローチが間違っています。プログラムからあまりにも多くの情報を隠しています。ある時点で、プログラムはどのテーブルから選択するかを認識します。その情報を捨てて不透明な SQL コマンド内に埋め込むのではなく、保持する必要があります。そうすれば、ロガー関数はログ データがどこに行くべきかを推測する必要がなくなります。それは知っています。

たぶん、これはいくつかのコードでより明確になります。コードは次のようになります。

sub make_query {
    my ($table, $columns, $conditions) = @_;
    return "SELECT $columns FROM $table WHERE $conditions";
}

sub run_query {
    my ($query) = @_;
    $dbh->prepare($query);
    ...
}

run_query( make_query( 'foo', '*', '1=1' ) );

これでは、やりたいことができません。したがって、次のようなことを行うようにプログラムを構成する必要があります。

sub make_query {
    my ($table, $columns, $conditions) = @_;
    return +{
        query => "SELECT $columns FROM $table WHERE $conditions",
        table => $table,
    } # an object might not be a bad idea
}

sub run_query {
    my ($query) = @_;

    $dbh->prepare($query->{query});
    log_to_file( $query->{table}.'.log', ... );

    ...
}

run_query( make_query( 'foo', '*', '1=1' ) );

API は同じですが、必要な方法でログを記録するために必要な情報が得られました。

また、動的 SQL 生成のためにSQL::Abstractを検討してください。上記の私のコードは単なる例です。

編集:OK、あなたはSQLiteを使用していると言っています。次の出力を解析できる EXPLAIN コマンドがあります。

sqlite> explain select * from test;
0|Trace|0|0|0|explain select * from test;|00|
1|Goto|0|11|0||00|
2|SetNumColumns|0|2|0||00|
3|OpenRead|0|2|0||00|
4|Rewind|0|9|0||00|
5|Column|0|0|1||00|
6|Column|0|1|2||00|
7|ResultRow|1|2|0||00|
8|Next|0|5|0||00|
9|Close|0|0|0||00|
10|Halt|0|0|0||00|
11|Transaction|0|0|0||00|
12|VerifyCookie|0|1|0||00|
13|TableLock|0|2|0|test|00|
14|Goto|0|2|0||00|

TableLock が探したいもののようです。YMMV、これは悪い考えです。

于 2009-10-18T13:45:10.913 に答える
4

一般に、SQL では、理論的な理由 (結果セットは計算された列のみで構成される可能性がある) と実用的な理由 (結果セットのデータにはテーブル名が含まれず、列名のみが含まれる) の両方で、結果セットからテーブル名を確実に推測することはできません。 .

したがって、使用されているテーブルを把握する唯一の方法は、元のクエリと共に格納する (または元のクエリから推測する) ことです。

于 2009-10-18T13:46:08.070 に答える
3

SQL::Statementの構文解析機能については良いことを聞いたことがありますが、これまで使用したことはありませんでした。

use SQL::Statement;
use strict;
use warnings;

my $sql = <<"    END_SQL";
    SELECT TADA.fileName, TADA.labelName
    FROM   TADA
    END_SQL
my $parser = SQL::Parser->new();
$parser->{RaiseError} = 1;
$parser->{PrintError} = 0;
my $stmt = eval { SQL::Statement->new($sql, $parser) }
    or die "parse error: $@";
print join',',map{$_->name}$stmt->tables;
于 2009-10-18T18:05:53.907 に答える