Chapter 9. More SQL

9.1. Sorting results

We now return to SQL to get a more thorough spectrum of the types of queries that one can use to access a database. We won't see any PHP concepts in this chapter, though we'll get more practice with what we know. Our primary goal is to extend our SQL knowledge beyond the basic SELECT queries that we saw before.

The first extension we'll make is to add one more clause to our SELECT query: After the SELECT, FROM, and the optional WHERE clause we can also have an ORDER BY clause to specify in what order we want the returned rows to be.

In the previous chapter, we saw how we could list all of the posts in our forum; but MySQL would have the right send them in no particular order, which isn't the behavior we would want. To amend this, we would add an ORDER BY clause to our query. (This clause needs to come after the WHERE clause; in the case of a query like this without a WHERE clause, it belongs after where the WHERE clause would be.)

SELECT subject, body
FROM Posts
ORDER BY postdate

The posts will now be retrieved in order, starting with the earliest post. If we wanted to report the most recent post first, we can do this by adding DESC afterwards.

SELECT subject, body
FROM Posts
ORDER BY postdate DESC

You may list several values in the ORDER BY clause, separated by commas. The results will be ordered by the first-mentioned value, then the second-mentioned value would break ties; then the subsequent values.

9.2. Joins

A more sophisticated type of SELECT query is the join query, where we list multiple tables in its FROM clause. We can then draw out information from the various tables.

SELECT subject, body, name
FROM Posts, Users
ORDER BY postdate

This won't work as you probably expect, however: A simple SQL join will join every row from the first table with every row from the second table. Thus a post by the sherlock user will be joined with each of sherlock, marple, and nancy, each yielding a separate row. If there are 5 posts in the database, and our Web page used the above query, it would list 15 posts, once for each combination of a post with a user. This is certainly not we wanted.

To select only those results where the post and user rows correspond, we need to add that requirement into the WHERE clause.

SELECT subject, body, name
FROM Posts, Users
WHERE poster = userid
ORDER BY postdate

This is the query we'll use in our updated version of the page that we saw in the previous chapter for listing all current posts. This page has also been modified to use the for statement that we saw at the end of the previous chapter.

<?php import_request_variables("pg""form_"); ?>
<html>
<head>
<title>Current Posts</title>
</head>

<body>
<h1>Current Posts</h1>

<?php
    $db = mysql_connect("localhost:/export/mysql/mysql.sock");
    mysql_select_db("forum", $db);
    $sql = "SELECT subject, body, name"
        . " FROM Posts, Users"
        . " WHERE poster = userid"
        . " ORDER BY postdate";
    $rows = mysql_query($sql, $db);
    if(!$rows) {
        echo "<p>SQL error: " . mysql_error() . "</p>\n";
    } elseif(mysql_num_rows($rows) == 0) {
        echo "<p>There are not yet any posts.</p>\n";
    } else {
        for($row = 0; $row < mysql_num_rows($rows); $rows++) {
            $post_subject = mysql_result($rows, $row, 0);
            $post_body = mysql_result($rows, $row, 1);
            $post_name = mysql_result($rows, $row, 2);

            echo "<h2>$post_subject</h2>\n";
            echo "<p>By: $post_name</p>\n";
            echo "<p>$post_body</p>\n";
        }
    }
?>
</body>
</html>

9.3. Inserts

Another common thing one would want to do with a database via the Web is to insert new items into a table. This is done via a different type of SQL query, called an INSERT query. As an example of an SQL query, suppose we want a PHP script that adds a new post into the database. The relevant SQL query would be the following.

INSERT INTO Posts (poster, postdate, subject, body)
VALUES ('sherlock', '2007-07-04 03:23', 'Case closed', 'The butler did it.')

An INSERT query has two clauses, the INSERT INTO clause and the VALUES clause. The INSERT INTO clause starts with the name of the table into which we wish to insert (Posts here), followed by a set of parentheses enclosing a list of the names of columns whose values we will specify for this new row. The VALUES clause consists of a set of parentheses enclosing the values for the columns named in the INSERT INTO clause, in the same order. Incidentally, you are allowed to omit most columns from the INSERT query, in which case the DBMS will choose some default value for the newly created row.

Unlike a SELECT query, an INSERT query doesn't really have any results: The information is going into the database, not coming out. As a result, you'd have no reason in your PHP script to use the mysql_result function after executing an INSERT query.

Let us now look at a PHP script that will execute an INSERT query. First, we need to specify the form that the user will complete.

<form method="post" action="post.php">
<p>Name: <input type="text" name="user" />
<br />Password: <input type="password" name="passwd" />
<br />Subject: <input type="text" name="subj" />
<br />Body: <input type="text" name="body" />
<br /><input type="submit" value="Post" />
</p></form>

Note that we have the user enter a password. Our PHP script will check the password first using a SELECT query; if that query finds a row with the relevant user/password combination, then it will proceed to add the post using a INSERT query.

<?php
    import_request_variables("pg""form_");

    $db = mysql_connect("localhost:/export/mysql/mysql.sock");
    mysql_select_db("forum", $db);
    $sql = "SELECT userid"
        . " FROM Users"
        . " WHERE userid = '$form_user' AND passwd = '$form_passwd'";
    $rows = mysql_query($sql, $db);
    if(!$rows) {
        $message = "Password lookup error: " . mysql_error();
    } elseif(mysql_num_rows($rows) == 0) {
        $message = "Password not accepted.";
    } else {
        $sql = "INSERT INTO Posts (poster, postdate, subject, body)"
            . " VALUES ('$form_user', NOW(),"
            . "        '$form_subj', '$form_body')";
        $rows = mysql_query($sql, $db);
        if(!$rows) {
            $message = "Insert error: " . mysql_error();
        } else {
            $message = "The post was successfully inserted.";
        }
    }
?>
<html>
<head>
<title>Post requested</title>
</head>

<body>
<h1>Post requested</h1>

<p><?php echo $message; ?></p>

<p>[<a href="view.php">List Posts</a>]</p>
</body>
</html>

9.4. Other SQL

SQL provides many other types of queries, but they occur less often in PHP scripts than SELECT and INSERT queries. One that you may find useful, though, is the UPDATE query, which says to alter existing rows in a table. Let us look at an example that changes the password for a user.

UPDATE Users
SET passwd = 'sillyhat'
WHERE userid = 'sherlock'

The UPDATE clause specifies which table contains the row we wish to modify; the SET clause says how we want to change that row; and the WHERE clause provides a condition for identifying the row to modify. In fact, we are permitted to use a WHERE clause that matches multiple rows in the table: MySQL will simply apply the SET clause for all rows for which the WHERE clause holds.

Another thing you occassionally might want to do via PHP is to delete rows from a table. This is done, appropriately enough, with a DELETE query, which consists of a DELETE FROM clause specifying the table from which we wish to drop rows, and a WHERE clause specifying a condition for identifying which rows to drop. The following example will delete all posts from before 2006.

DELETE FROM Posts
WHERE postdate < '2006-01-01'

Of course, you would want to be careful with a DELETE query, because if you mess up your WHERE clause you could end up clobbering the entire table. Generally speaking, PHP scripts won't delete information: The database will only accumulate information.

SQL provides many other query types, but they are targeted toward managing a database, such as creating tables, or renaming a column, or adding new columns to an existing table.