2009-10-29 4 views
4

Je ne sais pas si "variadic" est réellement le bon mot, mais je parle de choses qui peuvent prendre une liste de valeurs, comme IN(). Si vous avez travaillé avec DBI depuis longtemps, vous avez probablement essayé de le faire:Comment puis-je utiliser des espaces réservés pour des fonctions SQL variées avec DBI de Perl?

(Note: Tous les exemples extrêmement simplifiées par souci de concision)

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

espaces réservés DBI simplement ne prennent pas en charge ces types des manigances, c'est une valeur unique pour chaque ? ou rien, pour autant que je sache.

Cela me conduit à finir par faire quelque chose comme:

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

qui n'est pas si horrible, mais considérer une fonction, comme je l'ai écrit aujourd'hui un, qui doit accepter une SQL arbitraire avec IN clause et une liste de valeurs

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

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

Cela finit par obtenir appelé par la substance qui ressemble à

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

Cela choque vraiment mon sens de l'esthétique. Construire SQL personnalisé par programme est une assez grande douleur comme c'est; Je ne veux pas aller dans le sens de regexing mes chaînes SQL si je n'ai pas à le faire.

Y a-t-il un meilleur moyen?

Répondre

3

sprintf est à portée de main dans de telles situations:

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

J'aime l'idée de 'sprintf'; Je suis étonné de ne pas y penser car je travaillais dessus – friedo

+1

J'ai toujours pensé que 'sprintf' n'était pas très perlish, mais il y a certainement des avantages dans les substitutions compliquées (par exemple ma déclaration de concat dans les commentaires ci-dessous est en écriture seule). :) – Ether

+1

Perlish = faire le travail facilement. sprintf est souvent un moyen facile de faire le travail, ergo ... :) –

5

Matière à réflexion.

DBIx::Simple offre une syntaxe pour ce type de chose en utilisant une marque double question espace réservé:

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

En outre, SQL::Abstract est puissant, mais je trouve parfois les abstractions ne conduisent pas à SQL optimale.

5

Pourquoi ne pas:

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

j'ai écrit une telle méthode hier (en réalité il était 'mon $ sql = 'INSERT INTO. DatabaseName $. ''. TableName $. '('. Join ('', $ this- -> _ champs). ')'. 'VALEURS ('. join (',', ('?') x @ {$ this -> _ champs}). ')'; ', mais assez proche :) – Ether

+0

Avec tel une déclaration, faites attention que @quux ne soit pas vide ... 'OERE foo IN()' n'est pas valide. – ysth

2

Si vous utilisez des espaces réservés et les valeurs liées devient maladroit, il y a toujours DBI::quote().

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

Si vous ne me dérange pas de rupture DBI pure et en utilisant des modules, je prendrais un coup d'œil à SQL::Abstract pour votre exemple. SQL::Abstract peut prendre un hachage Perl et le transformer en 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. 
Questions connexes