【问题标题】:How can I use placeholders for variadic SQL functions with Perl's DBI?如何在 Perl 的 DBI 中为可变 SQL 函数使用占位符?
【发布时间】:2009-10-29 23:49:45
【问题描述】:

我不知道“variadic”是否真的是正确的词,但我说的是可以采用值列表的东西,例如IN()。如果您长期使用 DBI,您可能尝试过这样做:

(注意:为简洁起见,所有示例都非常简化)

my $vals = join ', ', @numbers;
my $sth = $dbh->prepare( "SELECT * FROM mytbl WHERE foo IN( ? )" );
$sth->execute( $vals );     # doesn't work

DBI 占位符根本不支持这些恶作剧,据我所知,它是每个 ? 的单个值或什么都没有。

这导致我最终做了类似的事情:

my $sth = $dbh->prepare( "SELECT * FROM mytbl WHERE foo IN ( $vals )" );

这不是那么可怕,但考虑一个函数,就像我今天写的那样,它必须接受一些带有IN 子句和值列表的任意 SQL

sub example { 
    my $self = shift;
    my ( $sql, @args ) = @_;

    my $vals = join ', ', @args;
    $sql =~ s/XXX/$vals/;    <---- # AARRRGHGH
    my $sth = $self->dbh->prepare( $sql );
    ...
}

这最终会被看起来像的东西调用

my $sql = "SELECT * FROM mytbl WHERE foo IN( XXX ) AND bar = 42 ORDER BY baz";
my $result = $self->example( $sql, @quux );

这真的冒犯了我的审美。以编程方式构建自定义 SQL 已经够痛苦了。如果不需要的话,我不想走上正则表达式我的 SQL 字符串的道路。

有没有更好的办法?

【问题讨论】:

    标签: sql perl dbi


    【解决方案1】:

    值得深思。

    DBIx::Simple 使用双问号占位符为此类事物提供语法:

    $db->query( 'SELECT * FROM mytbl WHERE foo IN ( ?? )', @args );
    

    另外,SQL::Abstract 功能强大,但我发现有时抽象不会产生最佳 SQL。

    【讨论】:

      【解决方案2】:

      为什么不:

        my $sql = "SELECT * FROM mytbl WHERE foo IN(" . join(',', ('?')x@quux) . ") AND bar = 42 ORDER BY baz";
        my $sth = $dbh->prepare($sql);
        $sth->execute(@quux);
      

      【讨论】:

      • 我昨天就写了这样一个方法(其实它是my $sql = 'INSERT INTO ' . $databaseName . '.' . $tableName . ' (' . join(', ', $this-&gt;_fields) . ')' . ' VALUES (' . join(', ', ('?') x @{$this-&gt;_fields}) . ')';,但足够接近了:)
      • 有了这样的声明,注意@quux 不为空...WHERE foo IN() 无效。
      【解决方案3】:

      如果您不介意脱离纯 DBI 并使用一些模块,我会以 SQL::Abstract 为例。 SQL::Abstract 可以获取 Perl 哈希并将其转换为 where clause

      my $sql  = SQL::Abstract->new;
      my @numbers = (1 .. 10);
      my ($stmt, @bind) = $sql->where({foo => {'in', \@numbers}});
      # $stmt is " WHERE ( foo IN ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ) )"
      # @bind contains the values 1 through 10.
      

      【讨论】:

        【解决方案4】:

        sprintf 在这种情况下很方便:

        my $sth = $dbh->prepare( 
            sprintf(
                'SELECT * FROM mytbl WHERE foo IN( %s )',
                join(',', ('?') x @numbers) )
        );
        

        【讨论】:

        • 我喜欢sprintf 的想法;我很惊讶我在做这个时没有想到这一点
        • 我一直认为sprintf 不是很糟糕,但是在复杂的替换中肯定有优势(例如,我在下面的 cmets 中的 concat 语句是只写的)。 :)
        • Perlish = 轻松完成工作。 sprintf 通常是完成工作的一种简单方法,ergo ... :)
        【解决方案5】:

        如果使用占位符和绑定值变得笨拙,总是有DBI::quote()

        my $sql = sprintf 'SELECT * FROM mytabl WHERE foo IN ( %s )',
             join( ',', map { $dbh->quote( $_ ) } @args );
        

        【讨论】:

          猜你喜欢
          • 2012-12-22
          • 2015-08-07
          • 2010-12-24
          • 1970-01-01
          • 1970-01-01
          • 2017-05-20
          • 2010-12-13
          • 1970-01-01
          • 2012-01-27
          相关资源
          最近更新 更多