3

我正在用 Perl 和 DBI 编写小片段(SQLite 耶!)

我想将一些特定的查询记录到与运行查询的表名具有相同文件名的文本文件中。

这是我用来将结果转储到文本文件的代码:

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 命令中,不如保留它。那么你的 logger 函数就不必猜测日志数据应该去哪里了;它知道

也许这用一些代码更清楚。您的代码如下所示:

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::Abstract进行动态 SQL 生成。我上面的代码只是一个例子。

编辑:好的,所以你说你正在使用 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 回答