21

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.

5
  • This is a dupe of a well known question. Let me just find it.... Commented Feb 4, 2009 at 23:16
  • It is also Database specific... Commented Feb 4, 2009 at 23:17
  • Cool - searched, but couldn't find it myself. Commented Feb 4, 2009 at 23:17
  • 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. Commented Feb 4, 2009 at 23:17
  • Found it, but it's not with Arrays, but you might be able to adapt. Commented Feb 4, 2009 at 23:18

8 Answers 8

14

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( @foo );

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).

Sign up to request clarification or add additional context in comments.

Comments

12

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 { $dbh->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.

1 Comment

however, in my side, using DBI->quote is not working; i used $dbh->quote; here $dbh is from $dbh = DBI->connect("DBI:mysql:$db:$host", $user, $pass);
9

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 );

2 Comments

@Dems, JDrago's example is already using bind variables, so he doesn't need to do anthing else to prevent SQL injection.
map { '?' } @vals is more simply ('?') x @vals
6

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 ( ?, ?, ? ) )

1 Comment

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).
4

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);

2 Comments

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.
Good question. If I understand the docs correctly, you can have either multiple single placeholders or a single '??', but not both.
2

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]

5 Comments

Sure, if you're not worried about SQL Injection then you don't need parameters at all.
@Ray: I don't understand your comment -- he is doing parameter binding, just adding an appropriate number of parameters to bind dynamically.
hm.. so he is. Sorry my mistake.
No problem. Just wanted to make sure we were on the same page. :-)
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.
0

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.

Comments

0

Use

SELECT * FROM junk WHERE junk.id = ANY (?);

instead

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.