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

comment on

( [id://3333]=superdoc: print w/replies, xml ) Need Help??

Tricks with DBI

1. Check for database errors.

You're going to run into errors with databases for similar reasons that you do when using system calls and the like. And just as you should always check the return code of your system calls, so should you always check the return status of your database calls.

The easiest way to do this is by setting DBI's RaiseError attribute to 1; first connect to the database (and check the return), then set the RaiseError attribute:

my $dbh = DBI->connect('foo', 'bar', 'baz', 'mysql') or die "Can't connect: ", $DBI::errstr; $dbh->{RaiseError} = 1;
By doing this, you ensure that any database error will cause a die. Why is this good? Because generally, if you're writing a database application and you have a database error, you don't want to continue as if nothing happened. :) (Besides, you can always catch the die in an eval; just make sure that you handle the errors rather than ignoring them.)

The other way to check for errors, of course, is to check the return of each method call, eg.:

my $sth = $dbh->prepare("select id from foo") or die "Can't prepare: ", $dbh->errstr; $sth->execute or die "Can't execute: ", $dbh->errstr;
So as not to make your code one big C-like mess of error checking, though, you might as well just use RaiseError.

2. Use placeholders instead of literal values.

This is covered pretty thoroughly in What are placeholders in DBI. Suffice it to say here, then, that you should use placeholders instead of literal values. Always (or pretty much, at least).

And, for the same reasons, you should use prepare_cached instead of prepare.

3. The fastest way to fetch.

When you execute a SELECT statement, you want to get the data back as quickly as possible. The fastest way to do this is to use the bind_columns and fetch methods, because they don't copy a bunch of memory around.

bind_columns binds Perl variables to columns returned from your SELECT statement. For example, if you had the following SQL statement:

select id, name, phone from people
You'd want to bind 3 variables to the associated columns. So you set up the variables, then use bind_columns to bind them:
my($id, $name, $phone); $sth->bind_columns(undef, \$id, \$name, \$phone);
(The first argument to bind_columns is actually a hash reference specifying DBI attributes to associate with this particular method; we don't want to associate any attributes with this particular method, so we'll just pass it undef.)

After you've called execute on your statement handle, you'll want to fetch the data. To do this, use the fetch method, which fetches the next row and returns an array reference holding the field values. But you don't need to use that array reference, because you've got the column values bound to Perl variables, and you can just use those directly:

while ($sth->fetch) { print join("\t", $id, $name, $phone), "\n"; }
Each time you call fetch on your statement handle, the values of the bound variables get updated.

A Brief Interlude

Putting Tricks 1-3 together, we get something like the following:
# Connect to the database and set the RaiseError # attribute so that any database error will # cause a die my $dbh = DBI->connect('foo', 'bar', 'baz', 'mysql') or die "Can't connect: ", $DBI::errstr; $dbh->{RaiseError} = 1; # Setup our statement handle using a placeholder my $sth = $dbh->prepare_cached(<<SQL); select id, name, phone from people where birth_month = ? SQL my @months = qw/January February March April May June July August September October November December/; # Bind Perl variables to columns returned # from SELECT my($id, $name, $phone); $sth->bind_columns(undef, \$id, \$name, \$phone); for my $month (@months) { print "People born in $month:\n"; # Execute the statement for this $month $sth->execute($month); # Fetch each row and print out the values while ($sth->fetch) { print "\t", join("\t", $id, $name, $phone), "\n"; } } $sth->finish; $dbh->disconnect;

4. Using DBI with mod_perl.

Part of the purpose of mod_perl is to make Perl programs run much faster on the Apache web server. mod_perl accomplishes this by building a Perl interpreter into the web server and compiling your programs in memory; thus, when it gets a new request, it maps that request onto a program compiled into memory--this is very fast.

If you're going to use DBI with mod_perl, you'll want to make sure that your database transactions are fast, as well (because you don't want the database being the bottleneck). The easiest way to accomplish this is to use the Apache::DBI module, which gives your programs persistent database connections. It does so by overriding the DBI connect method and keeping a cache of open database handles. One of the nicest things about Apache::DBI is that you won't have to modify your existing DBI code to use it. Just add

use Apache::DBI;
BEFORE the
use DBI;
in your code, and you're set. You don't even have to take out the calls to disconnect, because Apache::DBI overloads those calls and makes sure that the handle doesn't actually get disconnected.

See Also

The new Programming the Perl DBI; Mark-Jason Dominus's A Short Guide to DBI; the DBI manpage (also (probably) available as perldoc DBI).

In reply to Tricks with DBI by btrott

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post; it's "PerlMonks-approved HTML":



  • Are you posting in the right place? Check out Where do I post X? to know for sure.
  • Posts may use any of the Perl Monks Approved HTML tags. Currently these include the following:
    <code> <a> <b> <big> <blockquote> <br /> <dd> <dl> <dt> <em> <font> <h1> <h2> <h3> <h4> <h5> <h6> <hr /> <i> <li> <nbsp> <ol> <p> <small> <strike> <strong> <sub> <sup> <table> <td> <th> <tr> <tt> <u> <ul>
  • Snippets of code should be wrapped in <code> tags not <pre> tags. In fact, <pre> tags should generally be avoided. If they must be used, extreme care should be taken to ensure that their contents do not have long lines (<70 chars), in order to prevent horizontal scrolling (and possible janitor intervention).
  • Want more info? How to link or How to display code and escape characters are good places to start.
Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others exploiting the Monastery: (4)
As of 2024-03-29 10:28 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found