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

All

I'm trying to get a particular field to update in my database. The code below reads from a table in my database. If the value of $row4 matches what I'm looking for change the values of $row3.

Now this works from the command prompt with no problem. What I'm trying to figure out is how do I update $row3 in my database table. Do I have to do a push or prepare another statement and then call it from there?

I'm just looking for some guidence on this one. Thanks in advance

Bobby

b.curtis@stanleyassociates.com

#!/usr/bin/perl # #Connects to a Sybase Database use DBI qw(neat_list); #Loads DBI module #Connect.... my $dbh = DBI->connect("dbi:ODBC:davs", "<username>", "<password>", { PrintError => 0, RaiseError => 1 }) or die "Can't connect to database: $DBI::errstr\n"; #Prepare statement.... my $sth = $dbh->prepare("SELECT * FROM STAGING.STAGING_PREPOUICS;") or + die "Can't prepare SQL statement: $DBI::errstr\n"; #Excute statement.... $sth->execute or die "Can't execute SQL statment: $DBI::errstr\n"; #Retrive Rows.... while( @row = $sth->fetchrow_array() ) { if($row[4] =~ m/ENG/i) { $row[3]='EN'; } elsif($row[4] =~ m/AVN/i) { $row[3]='AV'; } elsif($row[4] =~ m/TRAN/i) { $row[3]='TN'; } elsif($row[4] =~ m/QM/i) { $row[3]='QM'; } elsif($row[4] =~ m/MI/i) { $row[3]='MI'; } elsif($row[4] =~ m/POLICE/i || $row[4] =~ m/MP/i) { $row[3]='MP'; } elsif($row[4] =~ m/CHAPLAIN/i) { $row[3]='CH'; } elsif($row[4] =~ m/INF/i) { $row[3]='IN'; } elsif($row[4] =~ m/FA/i) { $row[3]='FA'; } elsif($row[4] =~ m/MED/i) { $row[3]='MD'; } elsif($row[4] =~ m/SIG/i) { $row[3]='SC'; } elsif($row[4] =~ m/MAIN/i) { $row[3]='SS'; } elsif($row[4] =~ m/AR/i) { $row[3]='AR'; } else { $row[3]='PU'; } print "Unit Info:\n"; print "$row[0],$row[1],$row[2],$row[3],$row[4],$row[5]\n"; } warn "Data fetching terminated early by error: $DBI::errstr\n" if $DBI::err; #Finish statement.... $sth->finish; #Disconnect..... $dbh->disconnect or warn "Disconnection failed: $DBI::errstr\n"; exit;

Replies are listed 'Best First'.
Re: Updating database question
by dragonchild (Archbishop) on Jun 30, 2005 at 18:21 UTC
    You may want to look at something like Class::DBI or Tie::DBI to do the grunt work for you. But, to answer your question, yes you will need to add an UPDATE statement to the code you posted in order for your code to do what you want.

    Seriously, I'd use Class::DBI.

    foreach my $row (MyCDBI::Class->retrieve_all) { if ($row->column1 =~ m/AR/i ) { $row->column2( 'AR' ); } $row->update; }
    Done. :-)

    My criteria for good software:
    1. Does it work?
    2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
      Do you mean I will need to write a sub-function to do the update and then call it after I set the value?
      Bobby
        inline or another sub is a matter of preference. My code example was how easy it would be to do what you're trying to do if you were using Class::DBI instead of directly using DBI.

        My criteria for good software:
        1. Does it work?
        2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
Re: Updating database question
by Fletch (Bishop) on Jun 30, 2005 at 18:40 UTC

    And unrelated to your DBI question, but you might could use a data structure of regex objects rather than that frelling huge if monstrosity.

    use Tie::RefHash; tie my %m, "Tie::RefHash"; %m = ( qr/abc/ => "ABC", qr/def/ => "XYZ", ); $str = "def"; for my $r ( keys %m ) { if( $str =~ $r ) { $str = $m{$r}; last; } } print $str, "\n"

    --
    We're looking for people in ATL

      I'd agree about using a structure and walking it, rather than using that giant if/elsif/statement, but I wouldn't recommend using a hash in this case, without knowing more about the problem.

      The example given doesn't specify what is in $row[4], and the regexes given don't lock the match to the full width of the string. Now, it's possible that the original poster intended to match something more like:

      m/^ENG$/i; m/^AVN$/i; m/^TRAN$/i ...etc

      But with what's given, the item being matched might contain 'QMI' (matches 'QM' and 'MI') or 'POLICE TRAN' (again, matches more than one), and so the order that the searches are done might be significant.

      If the items being matched are the full width of the string, then the order isn't significant, but I'd probably not use regexes:

      my %matches = ( ENG => 'EN', AVN => 'AV', TRAN => 'TN', QM => 'QM', MI => 'MI', POLICE => 'MP', MP => 'MP', CHAPLAIN => 'CH', INF => 'IN', FA => 'FA', MED => 'MD', SIG => 'SC', MAIN => 'SS', AR => 'AR', ); if ( exists ( $match{uc($row[4])} ) { $row[3] = $match{uc($row[4])}; } else { $row[3] = 'PU'; }
      Fletch,

      Thanks for the note!

      Side note, what do you mean "We're looking for people in ATL? Who is looking?

      Thanks,
      Bobby

        We have an opening where I work; it was posted on jobs.perl.org but it's fallen out of the DB. I'm in the process of getting the details moved somewhere else that I can link to again and haven't re-linked my signature.

        Update: And it's linked.

        Updated again: And a more permanent official type link.

        --
        We're looking for people in ATL

Re: Updating database question
by shiza (Hermit) on Jun 30, 2005 at 18:55 UTC
    You could also approach this problem from a different angle. Instead of selecting everything from your table and running updates on every row you could just do the dirty work with SQL and let the DB handle the processing:
    UPDATE STAGING.STAGING_PREPOUICS SET column3 = 'EN' WHERE column4 = 'ENG'; etc...
    This will minimize the number of queries that you will run. Combine this with what fletch suggested and you'll have some code that's easier on your eyes, easier to maintain, and easier on your server. :)

    Note: I have never used Sybase so I'm not sure if it supports this.
      Shiza
      I have already gone down that road. The problem with that is that I would have to do substr(column4,0,?) to get the match. When the ENG doesn't appear in at the same spot each time, it would actually double the effort and time, hense the reason for the perl script. All this will be rolled into a automated process.
      Thanks
      Bobby
        How about:
        UPDATE STAGING.STAGING_PREPOUICS SET column3 = 'EN' WHERE column4 LIKE '%ENG%';
        Like I said, I'm not familiar with Sybase but I know this would work with MySQL.
        UPDATE foo SET col3 = 'EN' WHERE LOWER( col4 ) LIKE '%ENG%'
        (or LCASE or LC or whatever, depending on the function for your specific database)

        lots of options ... personally, I like the Class::DBI one. This is kinda what Class::DBI was created for.


        My criteria for good software:
        1. Does it work?
        2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
Re: Updating database question
by VSarkiss (Monsignor) on Jul 01, 2005 at 02:50 UTC

    You have a lot of options above for handling this in code, but personally I would suggest you handle it directly in the database.

    I don't know what columns $row[3] and $row[4] represent, but your code is doing a mapping between the two, so create a table to hold the pairs. Something like:

    create table curtisb_map ( row3_code varchar(20) -- pure guess , row4_code varchar(20) -- purer guess , primary key (row3_code, row4_code) );
    Then populate the table with your if-else data above, and you can just return both with a single select:
    select s.*, m.row_3_code from staging.staging_prepouics s inner join curtisb_map m on m.row3_code = s.whatever

    The advantage with this approach is that the data is in the database where it belongs, instead of half in the database and half in the code. The next maintenance programmer that comes along will thank you profusely when she finds the mapping right alongside the data. And the bonus is that this is likely to run much faster.

    As an aside, your code has something that makes me cringe every time I see it: select * is bad enough, but it's made worse by the assumption of column order (i.e., you're hoping that $row[3] and $row[4] are what you think they are). Do yourself a favor, call out the column names explicitly, and bind the results to meaningful variables. Sooner or later, this practice will bite you.