【问题标题】:Building query from module - Best Practices从模块构建查询 - 最佳实践
【发布时间】:2018-11-12 19:35:18
【问题描述】:

我有多个查询需要执行,最多可达 100 行甚至更多行,我正在寻找最佳实践/简单的方法来将查询从模块导入我的主文件、绑定值并执行它们。示例:

Sql.pm

package Sql;

use strict;
use warnings;

use Exporter;

our @ISA = qw(Exporter);
our @EXPORT_OK = qw($query);

our $query = "SELECT * 
FROM ?
";

main.pl

use strict;
use warnings;

use Sql;
#use DBI & connect ;

my $select = $Sql::query;
my $tbl= 'my_tbl';

my $sth = $dbh->prepare($select );
$sth->execute($tbl);

这会给我这个错误:

ct_result(ct_dynamic(CS_PREPARE)) 在 /usr/lib/x86_64-linux-gnu/perl5/5.22/DBD/Sybase.pm 第 138 行返回 -205。
DBD::Sybase::db 准备失败:服务器消息号=1087 严重性=16 状态=1 行=1 服务器=本地文本=必须声明表变量“@P1”。服务器消息编号=8180 严重性=16 状态=1 行=1 服务器=本地文本=无法准备语句。在 main.pl 第 16 行。 无法在 main.pl 第 17 行对未定义值调用方法“执行”。

如果我在我的 main.pl 中这样做

my $sth = $dbh->prepare($select);
$sth->bind_param(1, $tbl);
$sth->execute(1);

我收到同样的错误,但告诉我“无法在未定义的值上调用方法“bind_param””

我做错了什么? 有没有更好/简单/最佳实践的方法来实现我想要做的事情?

谢谢

【问题讨论】:

  • 定义“有麻烦”。请发帖minimal reproducible example。
  • 我已经编辑了主帖!感谢您的快速回复!
  • 您的“麻烦”来自于它不知道如何准备您的语句,因此 $sth 是未定义的,因此,$sth->... 方法将无法说出 Can't call method "..." on an undefined value。现在从metacpan.org/pod/distribution/DBI/… 的文档中可以看出,占位符在WHERE 子句中的搜索值以及为列设置值时可以正常工作。但不适用于表名或列名,这就是您尝试做的
  • 我明白了,我已将代码修改为: sql.pm our $query = "Select * from my_tbl where col = ?"; main.pl $tbl = '123' $sth->dbh->prepare($select) ; $sth->执行($tbl);这行得通,但这是一个好习惯吗?最佳做法是什么?
  • 你可能想将你的 $tbl 重命名为 $val

标签: perl


【解决方案1】:

我希望以下内容足以自我解释:

package My_App::Table_Name;

use feature 'state';
# save performance hit on `prepare` statements
# you said SQL statements of 100+ lines

sub sql_name {
    my $class = shift;
    my $dbh   = shift;
    my %params = @_;

    # do some checking on the params if needed, or use Type::Params::compile

    state $sth = $dbh->prepare( <<    END_OF_SQL ); # 4 spaces for neatness

        SELECT * FROM table_name WHERE col_foo = ? AND col_bar = ?

    END_OF_SQL

    $sth->execute(
        $params{foo},
        $params{bar},
    );
    return $sth->fetchall_hashref('id'); # or what ever
}

1;

然后在您的应用中:

use strict;
use warnings;

use DBI;
use My_App::Table_Name;

my $dbh = ... ; # RaiseError is your friend

my $results = My_App::Table_Name->sql_name($dbh,
    foo => 'abc',
    bar => '123',
);

# $results now contains a HashRef where the keys are the id's from the returned
# rows, each row being a HashRef itself, so you can do things like:

say $results->{42}->{col_foo};

这样,您可以按表名组织您的 SQL 语句,大多数数据库 修改作用于一张表,或者由于一张表内的变化,导致其他地方的更新链。

在每个模块中,您现在可以创建具有合理名称的子例程,例如

sub create { ... };
sub delete { ... };
sub search_joined_with_table_other { ... };

因此在您的应用中:

My_App::Table_Name->create($dbh,
    foo => 'xyz',
    bar => '345',
);

My_App::Table_Other->delete($dbh, id => 42 );

因此,基本上,您并不关心主应用程序中的所有 SQL,而是很好地将它们移到自己的包中。在您的应用程序中,您甚至不需要知道底层有 SQL,您只需调用类方法即可。

下一级,将为每个 Table_Name 包返回行对象,并在其上拥有自己的实例方法。

快乐编码并记住 TIMTOWTDI(有不止一种方法可以做到)

【讨论】:

  • 非常感谢您的快速回复和帮助!这很好用,但是如果我想使用 $dbh->selectall_arrayref($select, { Slice => {} });如何绑定参数来查询 $select ?
  • 从文档中,您可以看到 $dbh-&gt;selectall_arrayref(...) 与 $sth = $dbh-&gt;prepare(SQL_HERE); $sth-&gt;execute( params_here ); $sth-&gt;fetchall_arrayref( slice_here ); 没什么区别,但这会击败顶部的 state,您需要在内部执行 prepare 和重来一遍……这对于庞大的 SQL 语句来说并不有趣。所以,我的建议是把它分成三个语句,而不是一个紧凑的一次性完成
  • 我会让 DBI 通过 $dbh-&gt;prepare_cached 处理语句缓存,而不是在您自己的代码中进行。
  • 我的朋友@simbabque 是完全正确的,滚动你自己不是一般人应该做的。而不是state $sth = $dbh-&gt;prepare( ... ); 可以写my $sth = $dbh-&gt;prepare_cached( ... ); ...但是龙在你的道路上(见metacpan.org/pod/distribution/DBI/DBI.pm#prepare_cached 和它可能遇到的麻烦)。使用state 确实可以避免所有这些
  • 不能这样使用商店。除了占用资源之外,下次调用时 dbh 可能会有所不同。
猜你喜欢
  • 2013-01-15
  • 2015-10-18
  • 1970-01-01
  • 1970-01-01
  • 2018-05-10
  • 2023-03-25
  • 2013-05-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多