views:

1283

answers:

9

Is there a standard way to bind arrays (of scalars) in a SQL query? I want to bind into an IN clause, like so:

SELECT * FROM junk WHERE junk.id IN (?);

I happen to be using Perl::DBI which coerces parameters to scalars, so I end up with useless queries like:

SELECT * FROM junk WHERE junk.id IN ('ARRAY(0xdeadbeef)');

Clarification: I put the query in its own .sql file, so the string is already formed. Where the answers mention creating the query string dynamically I'd probably do a search and replace instead.

Edit: This question is kind of a duplicate of Parameterizing a SQL IN clause?. I originally thought that it should be closed as such, but it seems like it's accumulating some good Perl-specific info.

+1  A: 

No, there is not.

Dave Markle
+2  A: 

Have a look at this question. I don't know much about perl but if you can join the array together so it's in one string you could probably get it to work. Or just use the most up voted answer in that question.

Ray
+1  A: 

In python, I've always ended up doing something like:

query = 'select * from junk where junk.id in ('
for id in junkids:
  query = query + '?,'
query = query + ')'

cursor.execute(query, junkids)

...which essentially builds a query with one '?' for each element of the list.

(and if there's other parameters in there too, you need to make sure you line things up correctly when you execute the query)

[edit to make the code easier to understand for non-python people. There is a bug, where the query will have an extra comma after the last ?, which I will leave in because fixing it would just cloud the general idea]

John Fouhy
Sure, if you're not worried about SQL Injection then you don't need parameters at all.
Ray
@Ray: I don't understand your comment -- he *is* doing parameter binding, just adding an appropriate number of parameters to bind dynamically.
cdleary
hm.. so he is. Sorry my mistake.
Ray
No problem. Just wanted to make sure we were on the same page. :-)
cdleary
Here's a Perl version: $params = '?, ' x scalar(@junk_ids); $query = 'SELECT * FROM junk WHERE junk.id IN (' . substr($params, 0, -2) . ');'; # Strip off the trailing comma-space.
cdleary
+5  A: 

I do something like:

my $dbh = DBI->connect( ... );
my @vals= ( 1,2,3,4,5 );
my $sql = 'SELECT * FROM table WHERE id IN (' . join( ',', map { '?' } @vals ) . ')';
my $sth = $dbh->prepare( $sql );
$sth->execute( @vals );
JDrago
But remember to protect yourself from SQL Injection attacks...
Dems
@Dems, JDrago's example is already using bind variables, so he doesn't need to do anthing else to prevent SQL injection.
mpeters
`map { '?' } @vals` is more simply `('?') x @vals`
ysth
+7  A: 

You specify "this is the SQL for a query with one parameter" -- that won't work when you want many parameters. It's a pain to deal with, of course. Two other variations to what was suggested already:

1) Use DBI->quote instead of place holders.

my $sql = "select foo from bar where baz in ("
           . join(",", map { DBI->quote($_) } @bazs)
           . ")";
my $data = $dbh->selectall_arrayref($sql);

2) Use an ORM to do this sort of low level stuff for you. DBIx::Class or Rose::DB::Object, for example.

Ask Bjørn Hansen
+2  A: 

With plain DBI you'd have to build the SQL yourself, as suggested above. DBIx::Simple (a wrapper for DBI) does this for you automatically using the '??' notation:

$db->query("select * from foo where bar in (??)", @values);
8jean
But then can you bind parameters elsewhere in the SQL? I don't see how you could bind parameters after the ?? unless it optionally takes an array reference instead.
cdleary
Good question. If I understand the docs correctly, you can have either multiple single placeholders or a single '??', but not both.
8jean
+1  A: 

If you don't like the map there, you can use the 'x' operator:

my $params = join ', ' => ('?') x @foo;
my $sql    = "SELECT * FROM table WHERE id IN ($params)";
my $sth    = $dbh->prepare( $sql );
$sth->execute( @vals );

The parentheses are needed around the '?' because that forces 'x' to be in list context.

Read "perldoc perlop" and search for 'Binary "x"' for more information (it's in the "Multiplicative Operators" section).

Ovid
this should be the correct answer (at least in my opinion).
maletin
A: 

I use DBIx::DWIW. It contains a function called InList(). This will create the part of the SQL that is needed for the list. However this only works if you have all your SQL in the program instead of outside in a separate file.

Peter Stuifzand
+2  A: 

And yet another way to build SQL is to use something like SQL::Abstract....

use SQL::Abstract;
my $sql    = SQL::Abstract->new;
my $values = [ 1..3 ];
my $query  = $sql->select( 'table', '*', { id => { -in => $values } } );

say $query;   # => SELECT * FROM table WHERE ( id IN ( ?, ?, ? ) )

/I3az/

draegtun
I was just about to post this, and I definitely think it's the "correct answer". If you can't use an ORM, at least use something like SQL::Abstract (or maybe Fey).
jrockway