Using Perl DBI to interface to MySQLWhy Perl? Why not PHP? After
all, PHP4 is supposed to be fast now that it's using a new
interpreter.Because Perl is still what's most widely used. I like
going with the industry standard. Perl has massive support online
and the most books written about it. There is CPAN, the
Comprehensive Perl Archive Network, where you'll find modules to do
almost anything you'd want. DBI gives you an interface that'll work
with the "primitive" CSV format (comma separated value text files)
all the way up to the highest end Oracle RDBMS.Let's start by
connecting to the database:use DBI;my $dsn =
'DBI:mysql:my_database:localhost';my $db_user_name = 'admin';my
$db_password = 'secret';my ($id, $password);my $dbh =
DBI->connect($dsn, $db_user_name, $db_password);Let's assume
we've received as form input a nickname and password from a login
screen. So right now,$input_nickname = 'Cowlick' and
$input_password = 'udder'We want to verify that the entered
password matches what we have in our database.my $sth =
$dbh->prepare(qq{ select id, password from users where nickname
= $input_nickname});$sth->execute();Notice there is no
command-terminating semi-colon.How do we get the results? Since we
only expect one row,($id, $password) =
$sth->fetchrow_array();$sth->finish(): # we're done with this
queryif ($input_password eq $password) # case-sensitive{ ... #
login successful}What if our result is more than one row?
Successive calls to$sth->fetchrow_array()will return the rest of
the result set.my $sth = $dbh->prepare(qq{ select nickname,
favorite_number from users});$sth->execute();while (my
($nickname, $favorite_number) = $sth->fetchrow_array()) # keep
fetching until # there's nothing left{ print "$nickname,
$favorite_number\n";}$sth->finish();If we want to save the
entire result set first for processing later,my (@matrix) =
();while (my @ary = $sth->fetchrow_array()){ push(@matrix,
[@ary]); # [@ary] is a reference}$sth->finish();A reference, for
C programers, can be thought of as a pointer. The Matrix is now an
array of array references, or a two-dimensional array.You can
access row $i with:@{matrix[$i]}Or, you can access a specific row
and column ($i, $j) in the table with:$matrix[$i][$j]For MySQL
operations that don't return a result you can use the do method
instead of prepare then execute.$dbh->do("insert into
message_votes (message_id, user_id, vote) values (1, 3,
'good')");When you're done with the
database:$dbh->disconnect();That should be enough to get you
started. You can see that using Perl DBI is a matter of calling a
method with the MySQL command as a string.For more in-depth
information regarding Perl DBI and MySQL refer to the book MySQL
(OTHER NEW RIDERS) by Paul Dubois. Great reviews on Amazon
convinced me to buy it and while I can't quite rave about the book,
because it's hard to find what you want most of the time, it seems
very competently written. Of course, I have no frame of reference
since this is the only MySQL book I've looked at.For the best
all-around CGI book out there, you must get the excellent CGI
Programming with Perl (2nd Edition) published by O'Reilly. You
should know some Perl before starting in on this but you'll learn
more than a little bit about everything CGI by the time you've
finished. Plenty of relevant examples and excellent sections on
Security, DBI, and Maintaining State make this book a must have.
Disregard the bad reviews on Amazon unless you're the kind of
person who likes to type in the examples from the book, in which
case, you will have a tough time. Use it instead to learn the
concepts and then write your own code!
Introduction to MySQL (using Perl DBI)My theory is, if the title
doesn't sound alien to you, then you're probably in the right
place.MySQL is a relational database management system. It sounds
fancy, but doesn't have to be. The novice, like me, can treat it as
a black box. Put stuff in, ask for certain stuff out. It just
works. That simple.MySQL is based on a client/server model, so the
black box is the MySQL server and your interface to it is the MySQL
client program. All the examples of MySQL commands in this article
can be entered straight into the MySQL client command line. MySQL
supports multi-line commands and uses a semicolon to designate the
end of the command.Why MySQL and not another database system that
supports SQL?Because MySQL is free, well-supported and fast.Even
for a novice, it's probably more helpful to think of MySQL as a
secretary ready to do your filing for you. You give her
instructions on how to file the items. If you know that you'll be
asking for items by their date, then tell the secretary to file by
date and when you ask for things, it doesn't take her all day to
find what you're looking for.Data is organized into rows and
columns, defining a matrix. In SQL-speak, the matrix is called a
table.The best way for a C programmer to think of it is:Each row is
a structure reference.Every column is a member of that
structure.Here's a typical C data structure:struct users{ int id;
char nickname[17]; // 16 char NULL terminated string char
password[17]; // ditto int socks; // pairs of socks int
favorite_number;};It looks like user information collected by a
web-site, perhaps.The MySQL version of this structure is created
with this command to the MySQL client program:create table users(
id int auto_increment not null, nickname varchar(16) not null,
password varchar(16) not null, socks int, favorite_number int,
primary key (user_id), unique (nickname));See any
similairities?This is what it looks like as a row in
MySQL:+----+----------+----------+-------+-----------------+| id |
nickname | password | socks | favorite_number
|+----+----------+----------+-------+-----------------+What is The
Matrix?The matrix of data for three hypothetical
users:+----+----------+----------+-------+-----------------+| 1 |
GdayMate | dingo | 57 | 42
|+----+----------+----------+-------+-----------------+| 2 | Javier
| cigar | 1 | 945
|+----+----------+----------+-------+-----------------+| 3 | Rolo |
pudding | 9 | 8
|+----+----------+----------+-------+-----------------+That's it?
Yup. In fact, these tables are exactly what you'd see if you gave
MySQL this command:select * from users;The asterisk means to select
all the columns from the users table.The table is the structural
foundation for the multi-billion dollar a year database industry
which includes companies like Oracle and Informix.Simple MySQL
CommandsLet's run through some simple SQL commands. You already
know the create table command.create table users( id int
auto_increment not null, nickname varchar(16) not null, password
varchar(16) not null, socks int, favorite_number int, primary key
(user_id), unique (nickname));What if we just want user nicknames
and their favorite number?select nickname, favorite_number from
users;This gives us:+----------+-----------------+| nickname |
favorite_number |+----------+-----------------+| GdayMate | 42
|+----------+-----------------+| Javier | 945
|+----------+-----------------+| Rolo | 8
|+----------+-----------------+And if we want all nicknames of
users with less than 10 pairs of socks and whose favorite number is
greater than 100?select nickname from users where socks < 10 and
favorite_number > 100;+----------+| nickname |+----------+|
Javier |+----------+So how do you insert a row of data into the
table? Simple.insert into users (nickname, socks) values
('Cowlick', 0);Uh oh, the row that's created is missing the
password column! Remember the line used to create the password
column in the users table?create table users( ... password
varchar(16) not null, ...);The not null means you must have some
value for the column. So MySQL gives an error in this case. We
should use instead:insert into users (nickname, password, socks)
values ('Cowlick', 'udder', 0);This results in a row like
this:+----+----------+----------+-------+-----------------+| id |
nickname | password | socks | favorite_number
|+----+----------+----------+-------+-----------------+ | 4 |
Cowlick | udder | 0 | NULL
|+----+----------+----------+-------+-----------------+But wait!
Why didn't we have to specify an id? That's not null also. The line
from the create table users command:create table users( id int
auto_increment not null, ...);In the case of the id column, we have
specified auto_increment and MySQL creates the value for us by
adding 1 to the greatest value it finds in that column (Rolo has an
id of 3).We've forgotten to include Cowlick's favorite number.
Which is -1, by the way. For this we use the update command.update
users set favorite_number = -1 where id = 4;We could have also
used:update users set favorite_number = -1 where nickname =
'Cowlick';But what if there were more than one user with the
nickname Cowlick? In our example, there can't be, because in the
create table command we've specifiedcreate tables users( ... unique
(nickname));If we try and insert another user with the nickname
Cowlick, we'd get an error from MySQL.Let's say you've built up a
large community of sock aficionados/numerologists and you've been
having a lot of problems with Javier. He keeps talking off subject
about high jumping and hunting for truffles. The other members of
the community are up in arms because they see Javier as a trouble
maker and his posts as noise.delete from users where nickname =
'Javier';Of course, this doesn't stop Javier from re-registering
and continuing his annoying banter.Let's say you're sick of all the
talk about gold toe vs. tube and Western-Lucky-7 vs.
Chinese-Good-Fortune-8. You realize there is a demand for a site
about high jumping and hunting for truffles. You want to revamp the
site.drop table users;The table definition and all the data are now
gone. Be very careful with this command.MySQL types and primary
keyWe haven't talked about the primary key directive in the create
table command.create table users( ... primary key (user_id),
...)This is an instruction to the MySQL "secretary" to file things
by user_id. The constraint imposed on a primary key is that each
row must have a unique value for the key. Technically, MySQL
creates a B-tree to make lookup of a specific row by user_id fast.
So thisselect * from users where user_id = 2;is faster thanselect *
from users where favorite_number = 945;Is MySQL limited to small
data types? Only if you think 4 gigabytes is small. That's what the
LongBlob and LongText types can hold.Let's say we want to create a
message system. A simple example of a message row is created
with:create table messages( id int auto_increment not null, user_id
int not null, posting_date datetime not null, comment_body text
primary key (id))This introduces two new SQL types: datetime and
text.The datetime column data is structured like so, "YYYY-MM-DD
hh:mm:ss". This way, datetimes can be ASCII sorted into
chronological order. To us, it's a string, that's the format we
give to MySQL and the format we get out, but internally, it's an 8
byte bitstream.The text type holds up to 64Kb of data, more than
enough for a message.The user_id column, that's the relational part
of Relational Database Management System (RDBMS). The user_id in
our example references the id column of the users table. This way
we're relating the message to the user who is the author. The table
is the structural foundation but this concept of referencing is the
functional basis for RDBMS.
Here's an example message row (the message_body can be much
longer):+----+---------+---------------------+--------------+| id |
user_id | posting_date | message_body
|+----+---------+---------------------+--------------+| 1 | 3 |
2000-10-10 10:00:00 | Wassup!
|+----+---------+---------------------+--------------+Let's say we
have a voting system, where users can vote on whether or not the
message was worth reading. We'd create a table like this:create
table message_votes( message_id int not null, user_id int not null,
vote enum('good', 'bad') not null, primary key (message_id,
user_id));In this example, the vote column can contain either the
value 'good' or the value 'bad'.The primary key directive specifies
two columns to "file" by. Since primary keys are by definition
unique, (message_id, user_id) as a value pair must be unique. This
imposes the constraint that each user can only vote on a specific
message once.Also, in this example, MySQL "files" message votes
"sorting" by message_id first then by user_id. Which means lookups
like:select * from message_votes where message_id = 3;are going to
be faster thanselect * from message_votes where user_id = 2;But the
fastest way to lookup a message vote is:select * from message_votes
where message_id = 3 and user_id = 2;Now we're ready to use Perl
DBI to interface with MySQL.
Database Management in PERL - DBIThis session will teach you how
to access Oracle Database and other databases using PERL.Starting
from Perl 5 it has become very easy to write database applications
using DBI. DBI stands for Database independent interface for Perl
which means DBI provides an abstraction layer between the Perl code
and the underlying database, allowing you to switch database
implementations really easily.The DBI is a database access module
for the Perl programming language. It defines a set of methods,
variables, and conventions that provide a consistent database
interface, independent of the actual database being
used.Architecture of a DBI ApplicationDBI is independent of any
database available in backend. You can use DBI whether you are
working with Oracle, MySQL or Informix etc. This is clear from the
following architure diagram.Here DBI is responsible of taking all
SQL commands through the API, or Application Programming Interface,
and to dispatch them to the appropriate driver for actual
execution. And finally DBI is responsible of taking results from
the driver and giving back it to the calling scritp.Notation and
ConventionsThroughout this chapter following notations will be used
and it is recommended that you should also follow the same
convention. $dsn Database source name $dbh Database handle object
$sth Statement handle object $h Any of the handle types above
($dbh, $sth, or $drh) $rc General Return Code (boolean: true=ok,
false=error) $rv General Return Value (typically an integer) @ary
List of values returned from the database. $rows Number of rows
processed (if available, else -1) $fh A filehandle undef NULL
values are represented by undefined values in Perl \%attr Reference
to a hash of attribute values passed to methodsDatabase
ConnectionAssuming we are going to work with MySQL database. Before
connecting to a database make sure followings:You have created a
database TESTDB.You have created TEST_TABLE in TESTDB.This table is
having fields FIRST_NAME, LAST_NAME, AGE, SEX and INCOME.User ID
"testuser" and password "test123" are set to access TESTDBPerl
Module DBI is installed properly on your machine.You have gone
through MySQL tutorial to understand MySQL Basics.
Following is the example of connecting with MySQL database
"TESTDB"#!/usr/bin/perluse DBIuse strict;
my $driver = "mysql"; my $database = "TESTDB"; my $dsn =
"DBI:$driver:database=$database"; my $userid = "testuser"; my
$password = "test123"; my $dbh = DBI->connect($dsn, $userid,
$password ) or die $DBI::errstr;If a connection is established with
the datasource then a Database Handle is returned and saved into
$dbh for further use otherwise $dbh is set to undef value and
$DBI::errstr returns an error string.
INSERT OperationINSERT operation is required when you want to
create your records into TEST_TABLE. So once our database
connection is established, we are ready to create records into
TEST_TABLE. Following is the procedure to create single record into
TEST_TABLE. You can create many records in similar fashion.Record
creation takes following stepsPrearing SQL statement with INSERT
statement. This will be done using prepare() API.Executing SQL
query to select all the results from the database. This will be
done using execute() API.Releasing Stattement handle. This will be
done using finish() APIIf everything goes fine then commit this
operation otherwise you can rollback complete transaction. Commit
and Rollback are explained in next sections. my $sth =
$dbh->prepare("INSERT INTO TEST_TABLE (FIRST_NAME, LAST_NAME,
SEX, AGE, INCOME ) values ('john', 'poul', 'M', 30, 13000)");
$sth->execute() or die $DBI::errstr; $sth->finish();
$dbh->commit or die $DBI::errstr;Using Bind ValuesThere may be a
case when values to be entered is not given in advance. In such
case binding values are used. A question mark is used in place of
actual value and then actual values are passed through execute()
API. Following is the example.
my $first_name = "john"; my $last_name = "poul"; my $sex = "M";
my $income = 13000; my $age = 30; my $sth =
$dbh->prepare("INSERT INTO TEST_TABLE (FIRST_NAME, LAST_NAME,
SEX, AGE, INCOME ) values (?,?,?,?)");
$sth->execute($first_name,$last_name,$sex, $age, $income) or die
$DBI::errstr; $sth->finish(); $dbh->commit or die
$DBI::errstr;READ OperationREAD Operation on any databasse means to
fetch some useful information from the database. So once our
database connection is established, we are ready to make a query
into this database. Following is the procedure to query all the
records having AGE greater than 20. This will take four
stepsPrearing SQL query based on required conditions. This will be
done using prepare() API.Executing SQL query to select all the
results from the database. This will be done using execute()
API.Fetching all the results one by one and printing those
results.This will be done using fetchrow_array() API.Releasing
Stattement handle. This will be done using finish() API
my $sth = $dbh->prepare("SELECT FIRST_NAME, LAST_NAME FROM
TEST_TABLE WHERE AGE > 20"); $sth->execute() or die
$DBI::errstr; print "Number of rows found :" + $sth->rows; while
(my @row = $sth->fetchrow_array()) { my ($first_name, $last_name
) = @row; print "First Name = $first_name, Last Name =
$last_name\n"; } $sth->finish();
Using Bind ValuesThere may be a case when condition is not given
in advance. In such case binding values are used. A question mark
is used in place of actual value and then actual value is passed
through execute() API. Following is the example.
$age = 20; my $sth = $dbh->prepare("SELECT FIRST_NAME,
LAST_NAME FROM TEST_TABLE WHERE AGE > ?"); $sth->execute(
$age ) or die $DBI::errstr; print "Number of rows found :" +
$sth->rows; while (my @row = $sth->fetchrow_array()) { my
($first_name, $last_name ) = @row; print "First Name = $first_name,
Last Name = $last_name\n"; } $sth->finish();UPDATE
OperationUPDATE Operation on any databasse means to update one or
more records already available in the database. Following is the
procedure to update all the records having SEX as 'M'. Here we will
increase AGE of all the males by one year. This will take three
steps
Prearing SQL query based on required conditions. This will be
done using prepare() API.Executing SQL query to select all the
results from the database. This will be done using execute()
API.Releasing Stattement handle. This will be done using finish()
APIIf everything goes fine then commit this operation otherwise you
can rollback complete transaction. See next section for commit and
rollback APIs.
my $sth = $dbh->prepare("UPDATE TEST_TABLE SET AGE = AGE + 1
WHERE SEX = 'M'"); $sth->execute() or die $DBI::errstr; print
"Number of rows updated :" + $sth->rows; $sth->finish();
$dbh->commit or die $DBI::errstr;
Using Bind ValuesThere may be a case when condition is not given
in advance. In such case binding values are used. A question mark
is used in place of actual value and then actual value is passed
through execute() API. Following is the example.
$sex = 'M'; my $sth = $dbh->prepare("UPDATE TEST_TABLE SET
AGE = AGE + 1 WHERE SEX = ?"); $sth->execute('$sex') or die
$DBI::errstr; print "Number of rows updated :" + $sth->rows;
$sth->finish(); $dbh->commit or die $DBI::errstr;In some case
you would like to set a value which is not given in advance so you
can use binding value as follows. In this example income of all
males will be set to 10000.
$sex = 'M'; $income = 10000; my $sth = $dbh->prepare("UPDATE
TEST_TABLE SET INCOME = ? WHERE SEX = ?"); $sth->execute(
$income, '$sex') or die $DBI::errstr; print "Number of rows updated
:" + $sth->rows; $sth->finish();DELETE OperationDELETE
operation is required when you want to delete some records from
your database. Following is the procedure to delete all the records
from TEST_TABLE where AGE is equal to 30. This operation will take
following steps.Prearing SQL query based on required conditions.
This will be done using prepare() API.Executing SQL query to delete
required records from the database. This will be done using
execute() API.Releasing Stattement handle. This will be done using
finish() API
If everything goes fine then commit this operation otherwise you
can rollback complete transaction.
$age = 30; my $sth = $dbh->prepare("DELETE FROM TEST_TABLE
WHERE AGE = ?"); $sth->execute( $age ) or die $DBI::errstr;
print "Number of rows deleted :" + $sth->rows;
$sth->finish(); $dbh->commit or die $DBI::errstr;
Using do StatementIf you're doing an UPDATE, INSERT, or DELETE
there is no data that comes back from the database, so there is a
short cut to perform this operation. You can use do statement to
execute any of the command as follows.$dbh->do('DELETE FROM
TEST_TABLE WHERE age =30');do returns a true value if it succeeded,
and a false value if it failed. Actually, if it succeeds it returns
the number of affected rows. In the example it would return the
number of rows that were actually deleted.
COMMIT OperationCommit is the operation which gives a green
signal to database to finalize the changes and after this operation
no change can be reverted back.Here is a simple example to call
commit API.$dbh->commit or die $dbh->errstr;
ROLLBACK OperationIf you are not satisfied with all the changes
and you want to revert back those changes then use rollback
API.Here is a simple example to call rollback API.$dbh->rollback
or die $dbh->errstr;
Begin TransactionMany databases support transactions. This means
that you can make a whole bunch of queries which would modify the
databases, but none of the changes are actually made. Then at the
end you issue the special SQL query COMMIT, and all the changes are
made simultaneously. Alternatively, you can issue the query
ROLLBACK, in which case all the queries are thrown away.begin_work
API enables transactions (by turning AutoCommit off) until the next
call to commit or rollback. After the next commit or rollback,
AutoCommit will automatically be turned on again.$rc =
$dbh->begin_work or die $dbh->errstr;
AutoCommit OptionIf your transactions are simple, you can save
yourself the trouble of having to issue a lot of commits. When you
make the connect call, you can specify an AutoCommit option which
will perform an automatic commit operation after every successful
query. Here's what it looks like:
my $dbh = DBI->connect($dsn, $userid, $password, {AutoCommit
=> 1}) or die $DBI::errstr;Here AutoCommit can take value 1 or
0.Automatic Error HandlingWhen you make the connect call, you can
specify a RaiseErrors option that handles errors for you
automatically. When an error occurs, DBI will abort your program
instead of returning a failure code. If all you want is to abort
the program on an error, this can be convenient. Here's what it
looks like:my $dbh = DBI->connect($dsn, $userid, $password,
{RaiseError => 1}) or die $DBI::errstr;Here RaiseError can take
value 1 or 0.
Disconnecting DatabaseTo disconnect Database connection, use
disconnect API.$rc = $dbh->disconnect or warn
$dbh->errstr;The transaction behaviour of the disconnect method
is, sadly, undefined. Some database systems (such as Oracle and
Ingres) will automatically commit any outstanding changes, but
others (such as Informix) will rollback any outstanding changes.
Applications not using AutoCommit should explicitly call commit or
rollback before calling disconnect.Using NULL valuesUndefined
values, or undef, are used to indicate NULL values. You can insert
and update columns with a NULL value as you would a non-NULL value.
These examples insert and update the column age with a NULL
value:
$sth = $dbh->prepare(qq{ INSERT INTO TEST_TABLE (FIRST_NAME,
AGE) VALUES (?, ?) }); $sth->execute("Joe", undef);Here qq{} is
used to return q quoted string to prepare API.However, care must be
taken when trying to use NULL values in a WHERE clause.
Consider:
SELECT FIRST_NAME FROM TEST_TABLE WHERE age = ?Binding an undef
(NULL) to the placeholder will not select rows which have a NULL
age! At least for database engines that conform to the SQL
standard. Refer to the SQL manual for your database engine or any
SQL book for the reasons for this. To explicitly select NULLs you
have to say "WHERE age IS NULL".A common issue is to have a code
fragment handle a value that could be either defined or undef
(non-NULL or NULL) at runtime. A simple technique is to prepare the
appropriate statement as needed, and substitute the placeholder for
non-NULL cases:
$sql_clause = defined $age? "age = ?" : "age IS NULL";$sth =
$dbh->prepare(qq{ SELECT FIRST_NAME FROM TEST_TABLE WHERE
$sql_clause });$sth->execute(defined $age ? $age : ());Some
other DBI functionsavailable_drivers@ary =
DBI->available_drivers;@ary =
DBI->available_drivers($quiet);Returns a list of all available
drivers by searching for DBD::* modules through the directories in
@INC. By default, a warning is given if some drivers are hidden by
others of the same name in earlier directories. Passing a true
value for $quiet will inhibit the warning.installed_drivers%drivers
= DBI->installed_drivers();Returns a list of driver name and
driver handle pairs for all drivers 'installed' (loaded) into the
current process. The driver name does not include the 'DBD::'
prefix.data_sources@ary = DBI->data_sources($driver);Returns a
list of data sources (databases) available via the named driver. If
$driver is empty or undef, then the value of the DBI_DRIVER
environment variable is used.quote$sql =
$dbh->quote($value);$sql = $dbh->quote($value,
$data_type);Quote a string literal for use as a literal value in an
SQL statement, by escaping any special characters (such as
quotation marks) contained within the string and adding the
required type of outer quotation marks.$sql = sprintf "SELECT foo
FROM bar WHERE baz = %s", $dbh->quote("Don't");For most database
types, quote would return 'Don''t' (including the outer quotation
marks). It is valid for the quote() method to return an SQL
expression that evaluates to the desired string. For
example:$quoted = $dbh->quote("one\ntwo\0three")may produce
results which will be equivalent toCONCAT('one', CHAR(12), 'two',
CHAR(0), 'three')Methods Common to all Handleserr$rv =
$h->err;or$rv = $DBI::error$rv = $h->errReturns the native
database engine error code from the last driver method called. The
code is typically an integer but you should not assume that. This
is equivalent to $DBI::err or $h->err.errstr$str =
$h->errstr;or$str = $DBI::errstror$str = $h->errstrReturns
the native database engine error message from the last DBI method
called. This has the same lifespan issues as the "err" method
described above. This is equivalent to $DBI::errstr or
$h->errstr.
rows$rv = $h->rows;or$rv = $DBI::rowsThis returns the number
of rows effected by previous SQL statement and equivalent to
$DBI::rows.trace$h->trace($trace_settings);DBI sports an
extremely useful ability to generate runtime tracing information of
what it's doing, which can be a huge time-saver when trying to
track down strange problems in your DBI programs. You can use
different values to set trace level. These values varies from 0 to
4. The value 0 means disable trace and 4 means generate complete
trace.Interpolated Statements are ProhebitedIt is highly
recommended not to use interpolated statements as follows:while
($first_name = ) { my $sth = $dbh->prepare("SELECT * FROM
TEST_TABLE WHERE FIRST_NAME = '$first_name'"); $sth->execute();
# and so on ...}There are following reasons to avoid interploated
statements:First, prepare calls can take a long time. The database
server has to compile the SQL and figure out how it is going to run
the query. If you have many similar queries, that is a waste of
time.Second, it will not work if $first_name contains a name like
O'Brien or D'Fecto or some other name with an '. The ' has a
special meaning in SQL, and the database will not understand when
you ask it to prepare an SQL statement.
Finally, if you're going to be constructing your query based on
a user input then it's unsafe to simply interpolate the input
directly into the query, because the user can construct a strange
input in an attempt to trick your program into doing something it
didn't expect. For example, suppose the user enters the following
bizarre value for $input:x' or first_name = first_name or
first_name = 'yNow our query has become something very
surprising:SELECT * FROM TEST_TABLE WHERE first_name = 'x' or
first_name = first_name or first_name = 'y'The part of this query
that our sneaky user is interested in is the second or clause. This
clause selects all the records for which first_name is equal to
first_name; that is, all of them.Thus don't use interpolated
statement instead use bind value to prepare dynamic SQL
statement.
A simple introduction to database interface using perl dbi.DBI
DataBase Interface module (more precisely a collection of modules)
is a feature rich, efficient and yet simple tool to access
databases using perl. Almost all Linux distributions have them, if
not you can download from cpan.org. The interface to any DBMS
requires two sets of tools one DBI itself which is generic, two the
DBD::the_database. DBD is the driver component and you should
install drivers for whatever database you are using. DBI drivers
are available for almost all standard databases.Normally database
access workflow is like this:a.Connect to the database (logging)
using username,password etc.. Once properly authenticated a
database-handle will be given.b.Create the sql query, use
database-handle to send the query to the server and ask it to
prepare the query.c. Server parses the sql, if no errors, returns a
statement-handle.d.Use the statement-handle to execute the
query.f.Use statement-handle to fetch data single row or multiple
rows at a time.g.Close the statement handleh. Repeat steps b to g
as long as you want, with new queriesi.Finally disconnect from
database(logout) using database-handle.
Let us see them by means of a sample code.Assumptions:Database
Server: mysql running on local hostDatabase user name: test
Password: test123Database name : testdbTable : namesColumns in the
table: id,name,ageObjective 1:1. List all records in the table and
find out average age
Let us start the code. (codes are give in italics and bold)
Step 1:Connecting to the databaseuse DBI;my
$dbh=DBI->connect(DBI:mysql:database=testdb;host=localhost,'test,'test123);Connect
requires three arguments : datasource, username,passwordFirst
argument -datasource gives information about the database server
like type of dbms, location etc.In our example datasource is
specified as DBI:mysql:database=testdb;host=localhostHere DBI:mysql
means use mysql driver.database=testdb means use the database
testdb.host=localhost means the host in which the database is
running.Other two arguments are username and password which need no
explanation.
Step 2Run the select query on the server.First store sql in a
variable like thismy $query=select * from name ;Then send the sql
to the server for parsing and checkingmy
$sth=$dbh->prepare($query) or die could not prepare $query\n;In
the above statement$dbh is the database connection handle we got
while using DBI->connect earlier.$sth is the statement handle
returned upon successful preparation.
$query refers to the sql statement. The query can be given
directly as string also.Here we do some error checking by using
die. The $sth that is returned will be required for any further
opertion on this query.Now we will run the query on the
server$sth->execute();Note here, we are simply using $sth to run
the query. Once we call execute, the server runs the query and
keeps the result set ready for retrieval.Step 3Get results from the
server one row at a time.fetchrow_array() is a function that will
return one row of data and store result in an array.We will use a
while loop to fetch all rows from the server.while
(($id,$name,$age)=$sth->fetchrow_array()){print id=$id
name=$name age=$age\n;}$sth->fetchrow will return a null when
there are no more rows. Thus this loop will run until
null.($id,$name,$age)=$sth->fetchrow_array() is used to equate
the rows returned to a set of variables.Step 4Close the statement
handle$sth->finish();Step 5Close the database
connection$dbh->disconnect();Here is the output of running the
script.id=1 name=RAMAN age=45id=2 name=RAVI age=35
For the sake convenience I am repeating program listing here.use
DBI;my
$dbh=DBI->connect(DBI:mysql:database=testdb;host=localhost,'test,'test123);my
$query=select * from name ;my $sth=$dbh->prepare($query) or die
could not prepare $query\n;$sth->execute();while
(($id,$name,$age)=$sth->fetchrow_array()){print id=$id
name=$name age=$age\n;}$sth->finish();$dbh->disconnect()
Objective 2.Insert a record accepting input from terminalHere is
the code to insert a row into name table.Step 1:Establishing
connection with database. For explanation see previous exampleuse
DBI;my
$dbh=DBI->connect(DBI:mysql:database=testdb;host=localhost,'test,'test123);Step
2Accept input from keyboardprint Enter id:;$id=;print Enter
Name:;$name=;
print Enter age;$age=;use chomp to remove any newlineschomp
$id;chomp $age;chomp $name;Explanation : The print statement is
simple just shows message on the screen.$id= means accept value
from standard input (by default keyboard) and store the value in
the variable $id;Step 3Create the Sql statement$query=sprintf
insert into name(id,name,age)
values(%d,%s,%d),$id,$dbh->quote($name),$age;Explanation: Here
we create a string using sprintf function, by variable
substitution.The sprintf in perl is similar to sprintf in
c.$dbh->quote() function is used on string values, so that
quotes are properly taken care of -e.g names like DSilva. It is a
best practice to use this.At the end of this line the $query
variable will have an sql which is an insert statement.Step 4.Run
the query on the server and insert data.$dbh->do($query) or die
could not do $query\n;The sql execution in the previous example was
done in three stages namely prepare, execute,fetchrow_array. In the
case of insert statement no rows are returned. Hence, we can use
do(), which combines all three stages into one. It will return
number of rows affected.Step 5.Disconnect from the
server$dbh->disconnect();
For the sake of convenience the code is repeated here.use DBI;my
$dbh=DBI->connect(DBI:mysql:database=testdb;host=localhost,'test,'test123);print
Enter id:;$id=;print Enter Name:;$name=;print Enter age;$age=;chomp
$id;chomp $age;chomp $name;$query=sprintf insert into
name(id,name,age)
values(%d,%s,%d),$id,$dbh->quote($name),$age;$dbh->do($query)
or die could not do $query\n;$dbh->disconnect();As you can see
from the above examples dbi is pretty simple. Same code can be used
for any database. Only change required will be in connection
step.The performance of dbi is very good, and it has lot features
like fetching column names, types etc.
MySQL Perl DBI TutorialBy Deborah Lee Soltesz, eHow
ContributorOne of the most powerful features of Perl is its ability
to process, parse, manipulate and format text and data, making it
an ideal language for developing database applications. The Perl
Database Interface (DBI) Module makes it easy to connect to and use
a wide range of database systems, including MySQL. In addition to
Perl, the Perl::DBI module and DBD::mysql database driver must be
installed on the system where the scripts are developed and
executed. Most of the functionality of MySQL can be accessed
through the MySQL driver for Perl::DBI.Connecting to the MySQL
ServerBefore you can execute queries and other statements on a
database, your script needs to establish a connection. Import the
DBI module in your script with "use DBI":use DBI;The
DBI->connect function connects to a database and returns a
database handle. Establish a connection to a local database,
providing the name of your database in the source parameter, and
the user name and password for the MySQL user your script will use
for the connection:my $dbh = DBI->connect('DBI:mysql:dbname',
'user', 'password') or die "Connection failed:
$DBI::errstr";Connecting to a remote database is similar to a local
connection by providing the remote host address in the source
parameter. In the following example, the RaiseError attribute is
set to report errors via die() in place of the "or die" manual
error checking clause in the previous example. The PrintError
attribute is disabled. PrintError automatically reports errors via
warn() when enabled.my $dbh =
DBI->connect('DBI:mysql:dbname;host=db.server.edu', 'user',
'password', { PrintError => 0, RaiseError => 1 });Before
exiting the script, disconnect from the database using the
disconnect function.$dbh->disconnect();Basic QueriesThe most
common statement executed on a database is the SELECT statement.
Create a statement handle by calling the prepare function with the
SELECT statement. For example, this SELECT will query a table
listing of people for the first name field for all entries where
the last name is "Johnson":my $sth = $dbh->prepare("SELECT
firstname FROM people WHERE lastname='Johnson'");Execute the
statement:
$sth->execute();Retrieve one row of data at a time as a hash
and print the results:print "Query for last name Johnson: \n";while
(my $resultrow = $sth->fetchrow_hashref()) {my $fn =
$resultrow->{firstname};print "$fn\n";}There are several
functions for retrieving query results, such as fetchrow_array to
fetch the next row as an array and fetchall_hashref to fetch all of
the results at once into a hash.Using PlaceholdersPlaceholders can
be used in the statement prepare function. This is useful in
interactive scripts where query filter values are provided by the
user, particularly if a loop allows the user to submit multiple
queries before exiting. For example, the placeholder (the question
mark) provides the spot where user input will be provided when the
statement is executed:my $sth = $dbh->prepare('SELECT * FROM
people WHERE lastname = ?') or die "Statement error: " .
$dbh->errstr;A while loop repeatedly prompts the user for a last
name:print "Search for last name: ";while ($lastname = ) {chomp
$lastname;my @results;The statement prepared earlier is executed,
with $lastname provided as a parameter. This parameter will be
inserted into the query statement in place of the question mark
placeholder:$sth->execute($lastname) or die "Statement error: "
. $sth->errstr;The results, if any, are printed out:if
($sth->rows == 0) {print "No matches for `$lastname'.\n\n";}
while (@results = $sth->fetchrow_array()) {my $firstname =
$results[1];my $age = $results[3];print "$firstname $lastname
($age)\n";}The statement handle is tidied up with the finish
method, and the loop continues:$sth->finish;print "Search for
last name: ";}Adding, Updating and Deleting RecordsUpdates, inserts
and deletes can be executed simply by using the do function. For
example:$dbh->do("INSERT INTO people(firstname, lastname, age)
VALUES('Bob', 'Johnson', 32)");$dbh->do("UPDATE people set
firstname='Robert' where firstname=?", undef,
"Bob");$dbh->do("DELETE FROM people WHERE
lastname='Johnson'");Building and Managing a DatabaseDatabase
administration functions are executed with the func function using
the database handle obtained from the
connection:$dbh->func("createdb", $dbname,
'admin');$dbh->func("dropdb", $dbname, 'admin');The server can
also be reloaded and shut down. This functionality is useful for
simplifying and automating database systems administration tasks.
Sufficient privileges are required for these actions.Database table
create and alter statements can be executed with the do function.
For example, this statement creates the people
table:$dbh->do("CREATE TABLE people (id INT NOT NULL
AUTO_INCREMENT PRIMARY KEY, firstname VARCHAR(50), lastname
VARCHAR(50), age INT)");Creating and updating existing databases is
useful for automating the installation of distributed applications,
such as discussion boards, photo galleries and blogs.