Как я могу использовать заполнители для переменных функций SQL с DBI Perl? - PullRequest
4 голосов
/ 30 октября 2009

Я не знаю, является ли слово "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 )" );

что не , поэтому ужасно, но рассмотрим функцию, подобную той, что я написал сегодня, которая должна принимать произвольный SQL с предложением IN и списком значений

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, если мне это не нужно.

Есть ли лучший способ?

Ответы [ 5 ]

5 голосов
/ 30 октября 2009

Если вы не против отказаться от чистого DBI и использовать некоторые модули, я бы взглянул на SQL :: Abstract для вашего примера. SQL :: Abstract может взять хеш Perl и превратить его в where предложение .

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.
5 голосов
/ 30 октября 2009

Почему бы и нет:

  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);
5 голосов
/ 30 октября 2009

Пища для размышлений.

DBIx :: Simple предлагает синтаксис для этого типа вещей, используя заполнитель с двойным знаком вопроса:

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

Также, SQL :: Abstract является мощным, но я считаю, что иногда абстракции не приводят к оптимальному SQL.

3 голосов
/ 30 октября 2009

sprintf удобно в таких ситуациях:

my $sth = $dbh->prepare( 
    sprintf(
        'SELECT * FROM mytbl WHERE foo IN( %s )',
        join(',', ('?') x @numbers) )
);
2 голосов
/ 30 октября 2009

Если использование заполнителей и значений связывания становится неуклюжим, всегда есть DBI::quote().

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