Handling NULL Values in Result Sets
2.9.1 Problem
A query result includes NULL values, but you're not sure how to tell where they are.
2.9.2 Solution
Your API probably has some value that represents NULL by convention. You just have to know what it is and how to test for it.
2.9.3 Discussion
Recipe 2.9 described how to refer to NULL values when you send queries to the database. In this section, we'll deal instead with the question of how to recognize and process NULL values that are returned from the database. In general, this is a matter of knowing what special value the API maps NULL values onto, or what function to call. These values are shown in the following table:
Language |
NULL-detection value or function |
---|---|
Perl |
undef |
PHP |
an unset value |
Python |
None |
Java |
wasNull( ) |
The following sections show a very simple application of NULL value detection. The examples retrieve a result set and print all values in it, mapping NULL values onto the printable string "NULL".
To make sure the profile table has a row that contains some NULL values, use mysql to issue the following INSERT statement, then issue the SELECT query to verify that the resulting row has the expected values:
mysql> INSERT INTO profile (name) VALUES('Juan'); mysql> SELECT * FROM profile WHERE name = 'Juan'; +----+------+-------+-------+-------+------+ | id | name | birth | color | foods | cats | +----+------+-------+-------+-------+------+ | 11 | Juan | NULL | NULL | NULL | NULL | +----+------+-------+-------+-------+------+
The id column might contain a different number, but the other columns should appear as shown.
2.9.4 Perl
In Perl DBI scripts, NULL is represented by undef. It's easy to detect such values using the defined( ) function, and it's particularly important to do so if you use the -w option on the #! line that begins your script. Otherwise, accessing undef values causes Perl to issue the following complaint:
Use of uninitialized value
To avoid this warning, test column values that might be undef with defined( ) before using them. The following code selects a few columns from the profile column and prints "NULL" for any undefined values in each row. This makes NULL values explicit in the output without activating any warning messages:
my $sth = $dbh->prepare ("SELECT name, birth, foods FROM profile"); $sth->execute ( ); while (my $ref = $sth->fetchrow_hashref ( )) { printf "name: %s, birth: %s, foods: %s ", defined ($ref->{name}) ? $ref->{name} : "NULL", defined ($ref->{birth}) ? $ref->{birth} : "NULL", defined ($ref->{foods}) ? $ref->{foods} : "NULL"; }
Unfortunately, all that testing of column values is ponderous, and becomes worse the more columns there are. To avoid this, you can test and set undefined values in a loop prior to printing them. Then the amount of code to perform the tests is constant, not proportional to the number of columns to be tested. The loop also makes no reference to specific column names, so it can be copied and pasted to other programs more easily, or used as the basis for a utility routine:
my $sth = $dbh->prepare ("SELECT name, birth, foods FROM profile"); $sth->execute ( ); while (my $ref = $sth->fetchrow_hashref ( )) { foreach my $key (keys (%{$ref})) { $ref->{$key} = "NULL" unless defined ($ref->{$key}); } printf "name: %s, birth: %s, foods: %s ", $ref->{name}, $ref->{birth}, $ref->{foods}; }
If you fetch rows into an array rather than into a hash, you can use map( ) to convert any undef values:
my $sth = $dbh->prepare ("SELECT name, birth, foods FROM profile"); $sth->execute ( ); while (my @val = $sth->fetchrow_array ( )) { @val = map { defined ($_) ? $_ : "NULL" } @val; printf "name: %s, birth: %s, foods: %s ", $val[0], $val[1], $val[2]; }
2.9.5 PHP
PHP represents NULL values in result sets as unset values, so you can use the isset( ) function to detect NULL values in query results. The following code shows how to do this:
$result_id = mysql_query ("SELECT name, birth, foods FROM profile", $conn_id); if (!$result_id) die ("Oops, the query failed "); while ($row = mysql_fetch_row ($result_id)) { while (list ($key, $value) = each ($row)) { if (!isset ($row[$key])) # test for unset value $row[$key] = "NULL"; } print ("name: $row[0], birth: $row[1], foods: $row[2] "); } mysql_free_result ($result_id);
PHP 4 has a special value NULL that is like an unset value. If you can assume your scripts will run under PHP 4, you can test for NULL values like this:
if ($row[$key] === NULL) # test for PHP NULL value $row[$key] = "NULL";
Note the use of the === "triple-equal" operator, which in PHP 4 means "exactly equal to." The usual == "equal to" comparison operator is not suitable here; with ==, the PHP NULL value, the empty string, and 0 all compare equal to each other.
2.9.6 Python
Python DB-API programs represent NULL values in result sets using None. The following example shows how to detect NULL values:
try: cursor = conn.cursor ( ) cursor.execute ("SELECT name, birth, foods FROM profile") for row in cursor.fetchall ( ): row = list (row) # convert non-mutable tuple to mutable list for i in range (0, len (row)): if row[i] == None: # is the column value NULL? row[i] = "NULL" print "name: %s, birth: %s, foods: %s" % (row[0], row[1], row[2]) cursor.close ( ) except: print "Oops, the query failed"
The inner loop checks for NULL column values by looking for None and converts them to the string "NULL". Note how the example converts row to a mutable object prior to the loop; that is done because fetchall( ) returns rows as sequence values, which are non-mutable (read-only).
2.9.7 Java
For JDBC programs, if it's possible for a column in a result set to contain NULL values, it's best to check for them explicitly. The way to do this is to fetch the value and then invoke wasNull( ), which returns true if the column is NULL and false otherwise. For example:
Object obj = rs.getObject (index); if (rs.wasNull ( )) { /* the value's a NULL */ }
The preceding example uses getObject( ), but the principle holds for other getXXX( ) calls as well.
Here's an example that prints each row of a result set as a comma-separated list of values, with each NULL value printed as the string "NULL":
Statement s = conn.createStatement ( );
s.executeQuery ("SELECT name, birth, foods FROM profile");
ResultSet rs = s.getResultSet ( );
ResultSetMetaData md = rs.getMetaData ( );
int ncols = md.getColumnCount ( );
while (rs.next ( )) // loop through rows of result set
{
for (int i = 0; i < ncols; i++) // loop through columns
{
String val = rs.getString (i+1);
if (i > 0)
System.out.print (", ");
if (rs.wasNull ( ))
System.out.print ("NULL");
else
System.out.print (val);
}
System.out.println ( );
}
rs.close ( ); // close result set
s.close ( ); // close statement
Категории