Using Perl bind_param with SQL IN statement [duplicate] - sql

This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
Is there SQL parameter binding for arrays?
I was wondering if there is anyway to use bind_param with SQL IN statements. According to perl documentation bind_param_array cannot be used as well. Has anyone come across the same situation?
http://search.cpan.org/perldoc?DBI#bind_param_array

No you can't do this easily. One option is to use $dbh->quote, eg.
my #values = (1,2,3,4);
my $sql = "SELECT * from my_table WHERE ID IN (";
$sql .= join(',', map { $dbh->quote($_) } #values)
$sql .= ')';
Or you can create the necessary placeholders and pass the array in as bind parameters, eg.
my #values = (1,2,3,4);
my $sql = "SELECT * from my_table WHERE ID IN (";
$sql .= join(',', map { '?' } #values);
$sql .= ')';
my $sth = $dbh->prepare($sql);
$sth->execute(#values);
Neither is exceptionally pretty.

Not if you're wanting an arbitrary number of placeholders, no. You can use it with an IN, as in
where foo in ( ?, ?, ? )
but then you must have exactly three binds.

Related

I have created a PDO prepared statement using LIKE and wildcards. It purpose is to find a member of staff [duplicate]

This question already has answers here:
How do I create a PDO parameterized query with a LIKE statement?
(9 answers)
Closed 2 years ago.
I have a sql query like this:
SELECT * FROM tbl_name WHERE title Like "%:needle%"
When I query the MySQL db manually with this statement it works. But when I use it with PDO and with the same values for :needle as I queried manually It just returns an empty result set.
Does utf8 encoding affects the behavior of it?
With PDO, this can be done like:
$stmt = $db->prepare("SELECT * FROM tbl_name WHERE title LIKE :needle");
$needle = '%somestring%';
$stmt->bindValue(':needle', $needle, PDO::PARAM_STR);
$stmt->execute();
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
try like
$sql = 'SELECT * FROM tbl_name WHERE title Like ":needle"';
$prep = $dbh->prepare($sql);
$ret = $prep->execute(array(':needle' => '%'.$somestring.'%'));
The '%' needs to be in the variable, not the statement.
Put the '%' in the variable, and you should be fine.
'SELECT * FROM tbl_name WHERE title Like ":needle"'
$needle = "%$needle%";
This worked for me:
$sql = $conn->prepare("select * from itens where nome LIKE :nome");
$nome = "%".$item->getName()."%";
$sql->bindParam("nome",$nome);

What does it mean "?" in sql query?

I just got a query cod :
SELECT o.id,o.sort_order,od.object FROM i_objects o, i_objects_description od
WHERE o.id=od.objects_id AND o.object_status = ? AND od.languages_id = ?
ORDER BY o.sort_order ASC
I want figure it out what does "?" mean in this query ?
If I run this query , it gives me this error :
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '?
Im using PEAR and this is my function :
function getArrayObjects( $language_id )
{
$q = 'SELECT o.id,o.sort_order,od.object FROM ' . TABLE_OBJECTS . ' o, ' . TABLE_OBJECTS_DESCRIPTION . ' od ';
$q.= 'WHERE o.id=od.objects_id AND o.object_status = ? AND od.languages_id = ? ';
$q.= 'ORDER BY o.sort_order ASC';
$sth = $this->_db->prepare( $q );
$res = $sth->execute( array( 'active', $language_id ) );
//var_dump($res);echo "<br>";echo "<br>";echo "<br>";
$objects = array();
while( $row = $res->fetchRow())
{
$objects[$row['id']] = $row;
}
return $objects;
}
It's a placeholder for parameter. In your query you have this:
AND o.object_status = ? AND od.languages_id = ?
And then you execute it like this:
$res = $sth->execute( array( 'active', $language_id ) );
So, when query is actually executed by database server, object_status is 'active' and language_id is $language_id.
This is done this way to guard from SQL injection. Another reason is efficiency. When you use prepared statements, database doesn't need to parse/compile query each time. It uses the template and just substitutes values in it. (more on this: Prepared statement)
The ? are placeholder the values of which are filled in in the $sth->execute( array( 'active', $language_id ) ) statement.
One of the main purposes for this construct is to prevent sql injection attacks.
Its a Used to set the value dynamically ,in other words place holder
These are "parametrized queries". While evaluating "?" are replaced with given values (it's called binding). They protect from sql injection and makes possible to optimize queries.

Using more than one queries on perl DBI

I'm sure there is a better way to do this on the same line, but I'm unable to figure out how, since I'm a beginner in Perl. Basically what I need to do is select, delete and count the results.
$sth = $dbh->prepare("SELECT env,server, mwp.is_reference where env='$ARGV[1]';");
$sth->execute();
$sth2 = $dbh->prepare("delete from mwp.is_info_package where env='$ARGV[1]'");
$sth2->execute();
$sth3 = $dbh->prepare("SELECT count(1) from mwp.is_reference where env='$ARGV[1]'");
$sth3->execute()
The objective is how do i use the 3 queries at the same line, instead having 3 executes.
Well you could start out using placeholders ( '?' ).
my #qlist
= ( 'SELECT env,server FROM mwp.is_reference where env=?'
, 'DELETE mwp.is_info_package WHERE env=?'
, 'SELECT count(1) FROM mwp.is_reference where env=?'
);
And then you can iterate through them like this:
my $env = $ARGV[1];
foreach my $query ( #qlist ) {
$dbh->prepare( $query )->execute( $env );
Carp::croak( $dbh->errstr ) if $dbh->err;
}
But of course, you really want to select the two outputs, don't you?
use Carp qw<croak>;
my $select_query = 'SELECT env,server FROM mwp.is_reference where env=?';
my $delete_query = 'DELETE mwp.is_info_package WHERE env=?';
my $count_query = 'SELECT count(1) FROM mwp.is_reference where env=?';
my %empty_atts;
my $rows
= $dbh->selectall_arrayref( $select_query, \%empty_atts, $env )
;
croak( $dbh->errstr ) if $dbh->err;
$dbh->prepare( $delete_query )->execute( $env );
croak( $dbh->errstr ) if $dbh->err;
my ( $count )
= $dbh->selectrow_array( $count_query, \%empty_atts, $env )
;
croak( $dbh->errstr ) if $dbh->err;
I solved the problem using the following query statement:
$sth = $dbh->prepare("select env,iserver, ( select count(1) from is_reference where env='$ARGV[1]' ) as total from is_reference where env='$ARGV[1]'");
not the most elegant way, but solved my problem with the less lines. Regarding the delete query, i moved to another condition to check if the table have data or not.
THanks all.
You could use a stored procedure that performs those functions and returns the results of the select as well as a count, then you only need to do:
my $sth = $dbh->prepare("EXEC procedure_name ?");
$sth->execute( $ARGV[1] );
As an aside, the way you're using prepare and execute is undesirable. You use prepare to avoid having to have Perl variables directly in the query; your Perl variables should be passed to execute() as values, not part of the string given to prepare(). There are a number of good reasons to do this, including protection against SQL Injection attacks.
I also noticed oddness in your last SQL query. I think you probably want
SELECT count(env) FROM mwp.is_reference where env=?
Otherwise it will always return "1" as the count... Likewise, unless there are database triggers doing something interesting, you could combine the first and last query into one this way (I'll leave count(1) for this in case that's really what you want):
my $sth = $dbh->prepare('SELECT env,server FROM mwp.is_reference where env=?');
$sth = $sth->execute( $ARGV[1] );
my $result_set = $sth->fetchall_arrayref();
my $count = scalar #{ $result_set };
The $result_set will be a reference to an ARRAY of ARRAYRefs containing the results; $count will contain the number of rows in that result set.

How do I update records only if I find a duplicate or otherwise insert data?

In the code below there is a hash which contains records with fields like name, pid, type and time1.
pid and name are repetitive fields which contain duplicates.
I duplicate found update the fields which need modification
else insert, here name and pid have duplicates (repetitive fields).
The rest are unique. Also I have a unique field while creating the table Serial no. How should I go on? I have done only an insertion in this code. I dont know how to store the retrieved record into an array using Perl. Please guide me.
for my $test11 (sort keys %seen) {
my $test1 = $seen{$test11}{'name'};
my $test2 = $seen{$test11}{'pid'};
my $test3 = $seen{$test11}{'type'};
my $test4 = $seen{$test11}{'time1'};
print "$test11\t$test1$test2$test3$test4\n";
$db_handle = &getdb_handle;
$sth = $dbh->prepare("Select username,pid,sno from svn_log1");
$sth->execute() or die "SQL Error: $DBI::errstr\n";
my $ref = $sth->fetchall_arrayref();
print "hai";
print "****$ref";
$sth = $dbh->prepare("INSERT INTO svn_log1 values('$sno','$test11','$test1','$test4','$test2','$test3')");
$sth->execute() or die "SQL Error: $DBI::errstr\n";
}
I think what you're trying to say is that you don't want to try to insert some data if you already have that name/pid combination in the database, but I can't tell, so I can't help you out there.
However, here are a few things which can clear up your code. First, choose sensible variable names. Second, always, always, always use placeholders in your SQL statements to protect them:
for my $test11 ( sort keys %seen ) {
my $name = $seen{$test11}{'name'};
my $pid = $seen{$test11}{'pid'};
my $type = $seen{$test11}{'type'};
my $time1 = $seen{$test11}{'time1'};
my $dbh = getdb_handle();
my $sth = $dbh->prepare("Select username,pid,sno from svn_log1");
$sth->execute() or die "SQL Error: $DBI::errstr\n";
my $ref = $sth->fetchall_arrayref();
# XXX why are we fetching this data and throwing it away?
$sth = $dbh->prepare("INSERT INTO svn_log1 values(?,?,?,?,?,?)");
$sth->execute( $sno, $test11, $name, $time1, $pid, $type )
or die "SQL Error: $DBI::errstr\n";
}
Assuming that you want to not insert something into the database if "$name" and "$pid" are there (and some cleanup to avoid preparing the same SQL over and over):
my $dbh = getdb_handle();
my $seen_sth = $dbh->prepare( "Select 1 from svn_log1 where username = ? and pid = ?");
# This really needs to be "INSERT INTO svnlog1 (#columns) VALUES (#placeholders)
my $insert_sth = $dbh->prepare("INSERT INTO svn_log1 values(?,?,?,?,?,?)");
for my $test11 ( sort keys %seen ) {
my $name = $seen{$test11}{'name'};
my $pid = $seen{$test11}{'pid'};
my $type = $seen{$test11}{'type'};
my $time1 = $seen{$test11}{'time1'};
$seen_sth->execute($name, $pid) or die "SQL Error: $DBI::errstr\n";
my #seen = $seen_sth->fetchrow_array;
next if $seen[0];
$insert_sth->execute( $sno, $test11, $name, $time1, $pid, $type )
or die "SQL Error: $DBI::errstr\n";
}
That's not quite the way I would write this, but it's fairly clear. I suspect it's not really exactly what you want, but I hope it gets you closer to a solution.
You want to insert some data, but if it exists, then update the existing row?
How do you test that the data already exists in the database? Are you using username and pid?
If so, you may like the change the structure of your database:
ALTER TABLE svn_log1 ADD UNIQUE (username, pid);
This create a composite, and unique index on username and pid. This means that every username/pid combination must be unique.
This allows you to do the following:
INSERT INTO svn_log1 (username, pid, ...) VALUES (?, ?, ...) ON DUPLICATE KEY UPDATE time = NOW();
What database is this?
My feeling is that you want an UPDATE or INSERT query, more commonly known as an UPSERT query.
If this is PostgreSQL you can create an upsert function to handle what you need. See the comments for a decent example. Otherwise, search Stack Overflow for "upsert" and you should find what you need.

Is there SQL parameter binding for arrays?

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.
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).
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.
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 );
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 ( ?, ?, ? ) )
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);
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]
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.
Use
SELECT * FROM junk WHERE junk.id = ANY (?);
instead