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

The following code works for me but I want to know how to change the query so that it will select rows based on matches in two columns. In SQL this would be something like SELECT * FROM charge WHERE colA = 'cat' AND colB = 'dog'. I am not sure how to translate that into a perl script. I am looking to get input from terminal like, What is the value to match in columnA? cat <enter> What is the value to match in columnB? dog <enter>

#!/usr/bin/perl -w # first experiments in perl front end for mysql queries use strict; use warnings; use DBI; my $dbh = DBI->connect('dbi:mysql:mydatabase:localhost:3306',' +abc','xyz') or die "Couldn't connect to database: " . DBI->errstr; my $sth = $dbh->prepare('SELECT * FROM charge WHERE cpt = ?') or die "Couldn't prepare statement: " . $dbh->errstr; print "Enter cpt code> "; while (my $cptcode = <>) { # Read input from the + user my @data; chomp $cptcode; exit 0 if ($cptcode eq ""); $sth->execute($cptcode) # Execute the quer +y or die "Couldn't execute statement: " . $sth->errstr; # Read the matching records and print them out while (@data = $sth->fetchrow_array()) { my $cptanswer = $data[1]; my $insid = $data[2]; my $preauth = $data[3]; my $updated = $data[5]; print "\t$cptanswer\t$insid\t$preauth\t$updated\n"; } if ($sth->rows == 0) { print "No names matched `$cptcode'.\n\n"; } $sth->finish; print "\n"; print "Enter cpt code> "; } $dbh->disconnect;

Replies are listed 'Best First'.
Re: select from mysql table based on two column match
by huck (Prior) on Jan 24, 2017 at 04:34 UTC

    something like this?

    my $sth = $dbh->prepare('SELECT * FROM charge WHERE cpt = ? AND col2 = + ? ') or die "Couldn't prepare statement: " . $dbh->errstr; print "Enter cpt code> "; my $cptcode = <>; chomp $cptcode; if ( defined $cptcode && $cptcode ne '') { print "Enter col2 code> "; my $col2 = <>; chomp $col2; if (defined $col2 && $col2 ne '') { $sth->execute($cptcode,$col2) # Execute the query or die "Couldn't execute statement: " . $sth-> +errstr; .... } # col2 } # cpt

Re: select from mysql table based on two column match
by Marshall (Canon) on Jan 24, 2017 at 22:33 UTC
    If I understand correctly from this post as well as Re^2: select from mysql table based on two column match, there are two issues:
    1. How to write the UI code to get the two column values.
      I am looking to get input from terminal like, What is the value to match in columnA? cat <enter> What is the value to match in columnB? dog <enter>
    2. How to write the DBI code to use those values.

    To address problem (1), consider some UI code like below. There of course many variants on how to do this. I present one framework that might be useful:

    #!/usr/bin/perl use strict; use warnings; my @answers = get_user_answers ("First Name", "Last Name"); print "User Said: @answers\n"; @answers = get_user_answers ("Animal1", "Animal2"); print "User Said: @answers\n"; # # get array of answers to an array # of prompts. Any non-blank answer, that # is also a single space separated token is allowed # sub get_user_answers { my (@prompts) = @_; my @answers; foreach my $prompt (@prompts) { my $answer = prompt4answer($prompt); push (@answers, $answer); } return @answers; } # # get a non_blank, single token answer # for a single prompt # sub prompt4answer { my $prompt = shift; my $answer; while ( (print "$prompt: "), $answer = <STDIN>, $answer !~ /^\s*\S+\s*$/) { if ($answer =~ /^\s*\S+\s+\S+/) { print "Error! Only a single word allowed!\n"; } # note: Simply re-prompts (no Error!) on a blank line } # Normal UI convention is to ignore leading and # trailing white spaces $answer =~ s/^\s*//; # trim leading spaces $answer =~ s/\s*$//; # trim trailing spaces # also deletes line endings (\n) return $answer; } __END__ Example session: C:\Projects_Perl\testing>perl UI_multi_prompt.pl First Name: First Name: clark kent Error! Only a single word allowed! First Name: clark Last Name: kent User Said: clark kent Animal1: dog cat Error! Only a single word allowed! Animal1: Animal1: dog Animal2: cat User Said: dog cat
    I have some issues with problem (2) the DBI code. Let us know if problem (1) is solved.

    Addition:

    I would recommend in the connect statement that you use the RaiseError option. If you do this, that eliminates a lot of "or die" clauses. Connect looks similar to this:

    my $dbh = DBI->connect("dbi:SQLite:dbname=$dbfile","","",{RaiseError = +> 1}) or die "Couldn't connect to database: " . DBI->errstr;
    This SQL is fine:
    SELECT * FROM charge WHERE colA = 'cat' AND colB = 'dog'

    However, in your program, I would use named SQL columns instead of '*' in the SQL statement.
    Your code references columns 1,2,3,5 (skips 0 and 4)
    Just delete column 0 and 4 in the SELECT by using SQL named fields instead of using '*'.

    Instead of using $sth->fetchrow_array(), consider using $sth->fetchall_arrayref() for "small" result sets, 10K or 100K rows.

    #code could look like this (untested): my $aref = $sth->fetchall_arrayref(); foreach my($cptanswer, $insid, $preauth, $updated)(@$aref) { print "\t$cptanswer\t$insid\t$preauth\t$updated\n"; }
    Instead of:
    while (@data = $sth->fetchrow_array()) { my $cptanswer = $data[1]; my $insid = $data[2]; my $preauth = $data[3]; my $updated = $data[5]; print "\t$cptanswer\t$insid\t$preauth\t$updated\n"; }
      Lots of great info here. Thank you.
Re: select from mysql table based on two column match
by madtoperl (Hermit) on Jan 24, 2017 at 10:37 UTC
    If I understood your question correctly, you are trying to get two inputs from user in terminal say cat, dog and if those columns values in the table are matching with cat and dog respectively,
    you need to select some other columns in the matching row and print it out.
    If that is what you are looking for here you go, Untested though!
    #!/usr/bin/perl -w use strict; use warnings; use DBI; #Get first input print "Enter cpt code> "; my $cptcode = <STDIN>; #Get Second input print "Enter cpt2 code> "; my $cpt2code = <STDIN>; if ( ($cptcode != “”) && ($cpt2code != “”)) { my $dbh = DBI->connect('dbi:mysql:mydatabase:localhost:3306',' +abc','xyz') or die "Couldn't connect to database: " . DBI->errstr; my $sth = $dbh->prepare('SELECT * FROM charge WHERE column1 = +$cptcode AND column2 = $cpt2code’) or die "Couldn't prepare statement: " . $dbh->errstr; $sth->execute() # Execute the query or die "Couldn't execute statement: " . $sth->errstr; # Read the matching records and print them out while (my @data = $sth->fetchrow_array()) { my $cptanswer = $data[1]; my $insid = $data[2]; my $preauth = $data[3]; my $updated = $data[5]; print "\t$cptanswer\t$insid\t$preauth\t$updated\n"; } if ($sth->rows == 0) { print "No names matched `$cptcode'.\n\n"; } $sth->finish; $dbh->disconnect; }

      "If that is what you are looking for here you go, Untested though!"

      Some comments:

      #!/usr/bin/perl -w use strict; use warnings;

      You have use warnings; and perl -w, both activate warnings.

      #Get first input print "Enter cpt code> "; my $cptcode = <STDIN>;

      You're not chomping user input.

      my $sth = $dbh->prepare('SELECT * FROM charge WHERE column1 = $cptcode + AND column2 = $cpt2code’)

      This is dangerous. See SQL_injection/Bobby Tables. Use placeholders and bind variables.

        You have use warnings; and perl -w, both activate warnings.

        But the effects of use warnings; and perl -w are slightly different, this is explained in detail in warnings. Rule of thumb: perl -w enables warnings globally (bad), whereas use warnings; enables warnings only per file or block (good).

        Alexander

        --
        Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)

      Hi madtoperl,

      Your code contains “smart” quotes, so it won't compile.

      $dbh->prepare('SELECT * FROM charge WHERE column1 = $cptcode AND column2 = $cpt2code’)
      

      Single quotes won't interpolate these variables, and even if they did, you shouldn't ever interpolate user input into SQL! Use placeholders instead.

      my $cptcode = <STDIN>;
      

      These values need to be chomped to remove the trailing newline.

      Update: Looks like marto and I had almost the same thoughts at the same time :-)

      Regards,
      -- Hauke D

      Actually no you misunderstand. I want to get two separate inputs from the user via terminal. Then I want to query a mysql table and return the rows where one column matches the first input and another column matches the second input in the same row.
        I want to get two separate inputs from the user via terminal. Then I want to query a mysql table and return the rows where one column matches the first input and another column matches the second input in the same row.

        madtoperl's code does match that description.

Re: select from mysql table based on two column match
by abner (Novice) on Jan 25, 2017 at 03:13 UTC

    I wan't to thank everyone for all of the helpful information. Thanks in particular to huck as I have emulated your script almost verbatim and it is working. To the other respondents, your comments and suggestions are much appreciated.