Professional Documents
Culture Documents
Why 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 query
{
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.
id;
};
int
socks;
// pairs of socks
int
favorite_number;
nickname
password
socks
int,
favorite_number int,
primary key
(user_id),
unique
(nickname)
);
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:
nickname
password
socks
int,
favorite_number int,
primary key
(user_id),
unique
(nickname)
);
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 |
+----------+
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,
...
);
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
(user_id),
...
)
(
id
user_id
(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
user_id
vote
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;
$dbh
$sth
$h
$rc
$rv
@ary
A filehandle
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;
INSERT Operation
INSERT 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 steps
Prearing 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() API
If 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;
my $first_name = "john";
my $last_name = "poul";
my $sex = "M";
my $income = 13000;
my $age = 30;
READ Operation
READ 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 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.
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
$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 Operation
UPDATE 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() API
If everything goes fine then commit this operation otherwise you can
rollback complete transaction. See next section for commit and rollback
APIs.
AGE = AGE + 1
$sex = 'M';
my $sth = $dbh->prepare("UPDATE TEST_TABLE
SET
AGE = AGE + 1
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 = ?
DELETE Operation
DELETE 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 Statement
If 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');
COMMIT Operation
Commit 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 Operation
If 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 Transaction
Many 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 Option
If 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:
Disconnecting Database
To disconnect Database connection, use disconnect API.
$rc = $dbh->disconnect
or warn $dbh->errstr;
$sth = $dbh->prepare(qq{
INSERT INTO TEST_TABLE (FIRST_NAME, AGE) VALUES (?, ?)
});
$sth->execute("Joe", undef);
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:
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 to
CONCAT('one', CHAR(12), 'two', CHAR(0), 'three')
$str = $DBI::errstr
or
$str = $h->errstr
Returns 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::rows
This 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 Prohebited
It 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 ...
}
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.
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.
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 database
use DBI;
my $dbh=DBI>connect(DBI:mysql:database=testdb;host=localhost,'test,'test123);
Connect requires three arguments : datasource, username,password
First 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=localhost
Here 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 2
Run the select query on the server.
First store sql in a variable like this
my $query=select * from name ;
Then send the sql to the server for parsing and checking
my $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.
$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 3
Get 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 4
Close the statement handle
$sth->finish();
Step 5
Close the database connection
$dbh->disconnect();
Here is the output of running the script.
Objective 2.
Insert a record accepting input from terminal
Here is the code to insert a row into name table.
Step 1:
Establishing connection with database. For explanation see previous
example
use DBI;
my $dbh=DBI>connect(DBI:mysql:database=testdb;host=localhost,'test,'test123);
Step 2
Accept input from keyboard
print Enter id:;
$id=<STDIN>;
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();
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.
Before exiting the script, disconnect from the database using the
disconnect function.
$dbh->disconnect();
Basic Queries
The 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":
$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";
}
if ($sth->rows == 0) {
print "No matches for `$lastname'.\n\n";
}
The statement handle is tidied up with the finish method, and the loop
continues:
$sth->finish;
print "Search for last name: ";
}
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)");