Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

I have a tough question if someone understands how this might be done.

The following SQL statement works but is not working in the way I need it to.

my $data = qq(SELECT engine, name1, name2, name3, name4, lable1, l +able2, lable3, lable4, settings1, settings2, settings3, settings4 FROM special_fields WHERE engin +e IN (".join(",",map($dbh->quote($_), @choices)).") AND settings1="public" OR settings2="public" OR settings +3="public" OR settings4="public");
I'm using IN to only bring back fields that have the engine name found in @choices (which populates properly). But it brings back everything that meets the OTHER critera, too. Like it'll bring back anything that has settings1-4 that are public, even if the name for @choices wasn't found.

My guess is my IN, ANDS, ORS are contradicting each other. I need it so it only brings back the records if IN name is found in @choices and at least one of the settings1-4 are set to "public".

Replies are listed 'Best First'.
Re: in, ands, ors in query
by rhesa (Vicar) on Dec 13, 2006 at 01:22 UTC
    It's the same as in other languages (Perl not excluded): and and or have different precedences, so you need to be explicit in the order you want:
    select foo from bar where baz in (1,2,3) and ( quux = 4 or bar2 = 5 -- running out of funny names )
      Hi.

      I did try to put parenthesis around the AND but now nothing is pulled back from the database. I know without a doubt that @choices containes the engine's names properly but with the parenthesis, nothing mathches. Without it, everything that has settings1-4 to "public" load.

      my $data = qq(SELECT engine, name1, name2, name3, name4, lable1, l +able2, lable3, lable4, settings1, settings2, settings3, settings4 FROM special_fields WHERE engin +e IN (".join(",",map($dbh->quote($_), @choices)).") AND settings1="public" OR settings2="public" OR settings +3="public" OR settings4="public");

        Your Perl syntax is broken. If you start a quoted string with qq(, then it won't end until the matching ), but you have a double quote followed by Perl code that you clearly expect to run (".join...). So please cut'n'paste the actual code you are using (since you say you get rows returned you must have a working query, just not quite the right query).

        - tye        

        As tye suggested, you need parentheses around all of the OR options too.

Re: in, ands, ors in query ()
by tye (Sage) on Dec 13, 2006 at 01:22 UTC

    Same as in a Perl expression: ... AND ( ... OR ... OR ... )

    - tye        

Re: in, ands, ors in query
by graff (Chancellor) on Dec 13, 2006 at 02:50 UTC
    I hope you've gotten the syntax fixed by now so that it's working -- the replies above are clear enough about what the problem is. Once you have it working, you might think about some "stylistic" improvements:
    my @getfields = qw/engine/; for my $type ( qw/name lable settings/ ) { for my $num ( 1 .. 4 ) { push @getfields, "$type$num"; } } my $sql = 'select '. join( ',', @getfields ). ' from special_fields WHERE engine IN ('. join( ',', ('?') x scalar @choices ). ') AND ('. join( ' OR ', map { "settings$_ = 'public'" } 1..4 ). ')'; my $sth = $dbh->prepare( $sql ); $sth->execute( @choices );
    Notes:
    • "$data" is not a good name for a string that is supposed to store an SQL "select ..." statement;
    • having one array to store the names of all the columns being selected can be really handy later;
    • maybe the for loop for putting the names into an array is overkill, but it does save you from having to type the same names four times each;
    • you really should be using placeholders for variable values that are being plugged into the query, and passing those values when you execute the query;
    • formatting the lines of code for legibility in a narrower window can really help.
    (update: having seen jdporter's reply, I have to agree -- his version is much easier to read)

      I personally find that style of sql construction hard to read. How about this:

      my $sql = do { my $getfields = join ',', @getfields; my $placeholders = join ',', ('?') x @choices; my $settingspublic = join ' OR ', map "settings$_ = 'public'", 1 .. +4; <<EOF; select $getfields from special_fields where engine in ($placeholders) and ($settingspublic) EOF };
      We're building the house of the future together.
Re: in, ands, ors in query
by davidrw (Prior) on Dec 13, 2006 at 16:04 UTC
    I think (might be db-dependent) you can simplify the SQL to be:
    WHERE engine IN (...) AND 'public' IN (settings1, settings2, settings3 +, settings4)
    Also, it seems from some of the above comments that quoting is one of the issues here.. here's a couple/few options:
    my $engines = join(",",map($dbh->quote($_), @choices)); my $sql = <<EOF; SELECT engine, name1, name2, name3, name4, lable1, lable2, lable3, l +able4, settings1, settings2, settings3, settings4 FROM special_fields WHERE engine IN ( $engines ) AND ( settings1="public" OR settings2="public" OR settings3="public" +OR settings4="public" ) EOF
    Placeholders are probably a good idea here, too:
    my $qs = join(",",map('?', @choices)); my $sql = <<EOF; SELECT engine, name1, name2, name3, name4, lable1, lable2, lable3, l +able4, settings1, settings2, settings3, settings4 FROM special_fields WHERE engine IN ( $qs ) AND ( settings1="public" OR settings2="public" OR settings3="public" +OR settings4="public" ) EOF my $rows = $dbh->selectall_arrayref( $sql, {Slice=>{}}, @choices );
    And there's also solutions with things like SQL::Abstract:
    use SQL::Abstract; my $sa = SQL::Abstract->new( logic=>'and' ); my($sql, @bind) = $sa->select( 'special_fields', [qw/ engine name1 name2 name3 name4 lable1 lable2 lable3 lable4 settings1 settings2 settings3 settings4 /], [ { engine => \@choices, }, [ settings1 => 'public', settings2 => 'public', settings3 => 'public', settings4 => 'public', ] ], ); my $rows = $dbh->selectall_arrayref( $sql, {Slice=>{}}, @bind );