Beefy Boxes and Bandwidth Generously Provided by pair Networks Joe
P is for Practical
 
PerlMonks

SQL LIKE and Binding Variables

by Mad_Mac (Novice)
 | Log in | Create a new user | The Monastery Gates | Super Search | 
 | Seekers of Perl Wisdom | Meditations | PerlMonks Discussion | 
 | Obfuscation | Reviews | Cool Uses For Perl | Perl News | Q&A | Tutorials | 
 | Poetry | Recent Threads | Newest Nodes | Donate | What's New | 

on Nov 02, 2009 at 22:17 UTC ( #804575=perlquestion: print w/ replies, xml ) Need Help??
Mad_Mac has asked for the wisdom of the Perl Monks concerning the following question:

I'm working on a little Perl script to write m3u playlists for me based on id3 tag data I have in a SQLite table. I want a SQL statement like SELECT * FROM Songs WHERE Songs.Artist LIKE '%artist 1%' OR Songs.Artist LIKE '%artist 2%'

What is the syntax to pass an array of variables via ->prepare and ->execute and have them properly wrapped in % and single quotes?

Comment on SQL LIKE and Binding Variables
Select or Download Code
Re: SQL LIKE and Binding Variables
by moritz (Chancellor) on Nov 02, 2009 at 22:54 UTC
    SELECT * FROM Songs WHERE Songs.Artist LIKE ? OR Songs.Artist LIKE '?
    End then
    s/%/%%/ for $artist1, $artist2; $dbh->execute("%$artist1%", "%$artist2%");

    (I hope I remember correctly that % is escaped as %% in LIKE queries).

    Perl 6 - links to (nearly) everything that is Perl 6.
[reply]
[d/l]
[select]
      (I hope I remember correctly that % is escaped as %% in LIKE queries).

      Um, nope. Perhaps you're confusing SQL LIKE with (s)printf?

[reply]

        Oracle, PostgreSQL, MySQL, SQLite, and even MS SQL Server have an ESCAPE keyword for this purpose. I'm too lazy today to find out if it is an official SQL standard or just one implementation copying the other one. But five very different databases agreeing on the same syntax for the same feature is simply too good to be just luck.

        Alexander

        --
        Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
[reply]
[d/l]
Re: SQL LIKE and Binding Variables
by graff (Canon) on Nov 03, 2009 at 06:51 UTC
    Following up on what moritz said:

    First (for mysql, at least) the way to include a literal "%" or literal "_" as part of the search condition in the "LIKE" value is to put a backslash in front of that character -- e.g.  % 1\_000\% % will match any string field value that contains a literal " 1_000% ", with anything (or nothing) before and after.

    As a more general method for conjoining different "like" conditions (updated as per "###" comments):

    my @sources = ( 'artist 1', 'artist 2', 'whatever' ); ### we make an +array first... my @values = map { s/([_%])/\\$1/g; "%$_%" } @sources; ### ...to avoi +d an error here my $like_clause = join( " or ", map { "Songs.Artist LIKE ?" } @values; my $sth = $dbh->prepare( "SELECT * FROM Songs where ($like_clause)" ); $sth->execute( @values );
    (I like putting parens around a set of related "OR" conditions -- it's a handy safeguard in case I need to include some other condition using "AND".)

    UPDATE: Sorry about having posted unusable code earlier -- I should have tested my initial version, to see that it produced the error mentioned in Mad_Mac's reply below. My code has been fixed now, as indicated in the comments above.

[reply]
[d/l]
[select]

      Thanks. I thought blackslash was the right way to escape "%" in a SQL statement also, but I couldn't seem to get the syntax right.

      I tried your recommendation, but all I managed to get was a different error: "Modification of a read-only value attempted at ..." and it gives me a reference to the line with the my @values = map { s/([_%])/\\$1/g; "%$_%" } bit. Maybe I didn't implement it quite right?

      Here's what my test code looks like at the moment:

      #!/usr/bin/perl use warnings; use DBI; my $db = DBI->connect("dbi:SQLite:music.sqlite","",""); my @values = map { s/([_%])/\\$1/g; "%$_%" } ( 'pearl', 'temple'); my $like_clause = join( " or ", map { "Songs.Artist LIKE ?" } @values) +; my $sth = $db->prepare( "SELECT * FROM Songs where ($like_clause)" ); my $all = $sth->execute( @values ) or die("Failed to Execute SQL"); foreach my $row (@$all) { my ($id, $Track, $Title, $Artist, $Album, $Year, $Genre, $Path) = @$ro +w; print "\nid: $id\nTrack: $Track\nTitle: $Title\nArtist: $Artist\nAlbum +: $Album\nYear: $Year\nGenre: $Genre\nPath: $Path\n";

      Any other thoughts?

[reply]
[d/l]
[select]
        $ perl -Mdiagnostics -e "s//1/g for 1" Modification of a read-only value attempted at -e line 1 (#1) (F) You tried, directly or indirectly, to change the value of a constant. You didn't, of course, try "2 = 1", because the compile +r catches that. But an easy way to do the same thing is: sub mod { $_[0] = 1 } mod(2); Another way is to assign to a substr() that's off the end of the s +tring. Yet another way is to assign to a foreach loop VAR when VAR is aliased to a constant in the look LIST: $x = 1; foreach my $n ($x, 2) { $n *= 2; # modifies the $x, but fails on attempt to mo +dify the 2 } Uncaught exception from user code: Modification of a read-only value attempted at -e line 1. at -e line 1
[reply]
[d/l]
Re: SQL LIKE and Binding Variables
by Jenda (Monsignor) on Nov 03, 2009 at 09:27 UTC

    Maybe this is what you are after:

    my $sth = $dbh->prepare(q{SELECT * FROM Songs WHERE Songs.Artist LIKE +'%'+?+'%' OR Songs.Artist LIKE '%'+?+'%' }); ... $sth->execute($one,$two);

    Jenda
    Enoch was right!
    Enjoy the last years of Rome.

[reply]
[d/l]

      I like that and didn't realize you could do it. Do you know if it's portable? Pg, mysql, sqlite, etc?

[reply]
[reply]

      Ok, thanks.

      If I combine the suggestions from Jenda and erix it seems to work pretty well.

      my @vals =("artist1", "artist2"); my $sth = $db->prepare(q{SELECT * FROM Songs WHERE Songs.Artist LIKE ' +%'||?||'%' OR Songs.Artist LIKE '%'||?||'%'});# suggestion from Jenda my $results = $sth->execute( @vals ) or die("Failed to Execute SQL");

      I'm now struggling to work out the syntax to combine the placeholder '%'||?\\'?' with a join and map statement that I can pass a an array of values.

      Does anyone know the trick to get this syntax right?

      If it matters - I am running ActivePerl on Vista X64 and using SQLite for my DB back end.

[reply]
[d/l]
[select]

        Each placeholder can accept only one value so you have to build your SQL statement with the right number of question marks. Or (if your database supports table valued functions) use something like this to split a single string on the database side. (I doubt things like this are supported by SQLite, but I may be mistaken.)

        Jenda
        Enoch was right!
        Enjoy the last years of Rome.

[reply]

Back to Seekers of Perl Wisdom


Login:
Password
remember me
What's my password?
Create A New User

Node Status
node history
Node Type: perlquestion [id://804575]
Front-paged by Arunbear
help
Community Ads
Chatterbox
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users
Others lurking in the Monastery: (9)
Moriarty
atcroft
herveus
ctilmes
Eyck
Gangabass
AnomalousMonk
baxy77bax
gnosti
As of 2009-11-21 11:16 GMT
Sections
The Monastery Gates
Seekers of Perl Wisdom
Meditations
PerlMonks Discussion
Categorized Q&A
Tutorials
Obfuscated Code
Perl Poetry
Cool Uses for Perl
Perl News
Information
PerlMonks FAQ
Guide to the Monastery
What's New at PerlMonks
Voting/Experience System
Tutorials
Reviews
Library
Perl FAQs
Other Info Sources
Find Nodes
Nodes You Wrote
Super Search
List Nodes By Users
Newest Nodes
Recently Active Threads
Selected Best Nodes
Best Nodes
Worst Nodes
Saints in our Book
Leftovers
The St. Larry Wall Shrine
Offering Plate
Awards
Craft
Snippets Section
Code Catacombs
Quests
Editor Requests
Buy PerlMonks Gear
PerlMonks Merchandise
Planet Perl
Perlsphere
Use Perl
Perl.com
Perl 5 Wiki
Perl Jobs
Perl Mongers
Perl Directory
Perl documentation
CPAN
Random Node
Voting Booth

Future historians will find that the material characteristic of the current era is...

Aluminium
Plastic
Oil
Water
Carbon dioxide
Copper
Iron
Silicon
Salt
Uranium
Hydrogen
Other

Results (730 votes), past polls