9.3. Zend_Db_Select
Prev | Chapter 9. Zend_Db | Next

## 9.3. Zend_Db_Select

### 9.3.1. Introduction

Zend_Db_Select is a tool to help you build SQL SELECT statements in a database-independent way. Obviously this can't be perfect, but it does go a long way toward helping your queries be portable between database backends. In addition, it aids you in making your queries resistant to SQL injection attacks.

The easiest way to create an instance of Zend_Db_Select is to use the Zend_Db_Adapter::select() method.

    <?php

    require_once 'Zend/Db.php';

    $params = array (
        'host'     => '127.0.0.1',
        'username' => 'malory',
        'password' => '******',
        'dbname'   => 'camelot'
    );

    $db = Zend_Db::factory('PDO_MYSQL', $params);

    $select = $db->select();
    // $select is now a Zend_Db_Select object configured for use only with the PDO_MYSQL adapter.

    ?>

You then construct a SELECT query using that object and its methods, then generate a string for passing back to Zend_Db_Adapter for queries or fetches.

    <?php

    //
    // SELECT round_table.*
    // FROM `round_table` round_table
    // WHERE `noble_title` = 'Sir'
    // ORDER BY `first_name`
    // LIMIT 10 OFFSET 20
    //

    // you can use an iterative style...
    $select->from('round_table');
    $select->where('noble_title = ?', 'Sir');
    $select->order('first_name');
    $select->limit(10,20);

    // ...or a "fluent" style:
    $select->from('round_table')
           ->where('noble_title = ?', 'Sir')
           ->order('first_name')
           ->limit(10,20);

    // regardless, fetch the results:
    $sql = $select->__toString();
    $result = $db->fetchAll($sql);

    // a second alternative: pass the $select object itself:
    $result = $db->fetchAll($select);

    // a third alternative: produce a Zend_Db_Statement
    // or PDOStatement directly from the $select object:
    $stmt = $select->query();
    $result = $stmt->fetchAll();

    ?>

You can also use bound parameters in your queries instead of quoting-as-you-go.

    <?php

    //
    // SELECT round_table.*
    // FROM `round_table` round_table
    // WHERE noble_title = 'Sir'
    // ORDER BY `first_name`
    // LIMIT 10 OFFSET 20
    //

    $select->from('round_table', '*')
           ->where('noble_title = :title')
           ->order('first_name')
           ->limit(10,20);

    // regardless, fetch the results using bound parameters
    $params = array('title' => 'Sir');
    $result = $db->fetchAll($select, $params);

    ?>

### 9.3.2. Columns FROM a Table

To select columns from a specific table, use the from() method, specifying the table and the columns you want from it. You can use both table and column aliases, and you can use from() as many times as you need.

    <?php

    // create a $db object, assuming Mysql as the adapter.
    $select = $db->select();

    // SELECT some_table.`a`, some_table.`b`, some_table.`c`
    // FROM `some_table` some_table

    $select->from('some_table', array('a', 'b', 'c'));

    // SELECT bar.`col`
    // FROM `foo` bar

    $select->from(array('foo' => 'bar'), 'col');

    ?>

The second argument to the from() method is a scalar to name a single column, or an array to name multiple columns. The scalar or element of the array can be a string, which is interpreted as a plain column name, and is quoted and prepended with the table alias. If you omit the second argument, it defaults to `'*'`, which is not quoted but is prepended with the table alias. If you want no columns from this table to be in the result set, use an empty `array()`.

Do not specify multiple columns as a single string of comma-separated column names. This syntax used in earlier preview releases of Zend_Db is no longer supported. Use an array instead.

The table alias is applied to every string element of the second argument, but if the element is an object of type Zend_Db_Expr, its string value is used without quoting or prepending with the table alias.

    <?php

    $select = $db->select();

    // SELECT foo.col AS col1, bar.col AS col2
    // FROM foo, bar
    $select->from('foo', array(
        new Zend_Db_Expr('foo.col AS col1'),
        new Zend_Db_Expr('CURDATE()')
    );
    $select->from('bar', new Zend_Db_Expr('bar.col AS col2'));

    ?>

Use an associative array to declare table aliases. The key is the table name and the value is the alias. If you specify a table as a simple string instead of an associative array to declare the alias, Zend_Db_Select generates a table alias. By default, the generated table alias is spelled the same as each table name, as if you had written a SQL query "`SELECT foo.* FROM `foo` foo`". If you add the same table more than once, for example in a self-join, Zend_Db_Select declares unique aliases for each table instance, like "`foo_1`", "`foo_2`", etc.

### 9.3.3. Columns from JOINed Tables

To select columns using joined tables, use the join() method. First give the joining table name, then the joining condition, and finally the columns you want from the join. You can use join() as many times as you need.

    <?php

    // create a $db object, assuming Mysql as the adapter.
    $select = $db->select();

    // SELECT foo.*, bar.*
    // FROM `foo` foo
    // JOIN `bar` bar ON foo.id = bar.id

    $select->from('foo');
    $select->join('bar', 'foo.id = bar.id');

    ?>

The first argument to the join() method is a table specifier. This is the same as the first argument of the from() method, in that it can be a string that names a table, or it can be an associative array mapping the table name to an alias name.

The second argument to the join() method is an expression that forms the join condition. You can use table aliases here, but remember that unless you specified the table alias, Zend_Db_Select generates one. If you omit the join condition, this is equivalent to a CROSS JOIN or cartesian product.

The third argument to the join() method is a list of columns from the joined table to include in the select-list. This is the same as the second argument of the from() method, in that it can be a scalar that names a column, or an array of scalars to name multiple columns. Each scalar can be either a string or an object of type Zend_Db_Expr. If you omit the columns argument, the default is `'*'`. If you want no columns from this table to be in the result set, use an empty `array()`.

Do not specify multiple columns as a single string of comma-separated column names. This syntax used in earlier preview releases of Zend_Db is no longer supported. Use an array instead.

The following join types are supported:

- INNER JOIN with the `join()` or `joinInner()` methods. All RDBMS brands support this join type.

- LEFT JOIN with the `joinLeft()` method. All RDBMS brands support this join type.

- RIGHT JOIN with the `joinRight()` method. Some RDBMS brands do not support this join type.

- FULL JOIN with the `joinFull()` method. Some RDBMS brands do not support this join type.

- CROSS JOIN with the `joinCross()` method. There is no parameter for this method to specify the join condition. Some RDBMS brands do not support this join type.

- NATURAL JOIN with the `joinNatural()` method. There is no parameter for this method to specify the join condition; a natural join assumes an equi-join on columns of the same name in the two tables. Only NATURAL INNER JOIN is supported.

### 9.3.4. WHERE Conditions

To add WHERE conditions, use the where() method. You can pass either a regular string, or you can pass a string with a question-mark placeholder and a value to quote into it (the value will be quoted using Zend_Db_Adapter::quoteInto).

Multiple calls to where() will AND the conditions together; if you need to OR a condition, use orWhere().

    <?php

    // create a $db object, then get a SELECT tool.
    $select = $db->select();

    // SELECT r.*
    // FROM `round_table` r
    // WHERE noble_title = 'Sir'
    //   AND favorite_color = 'yellow'

    $select->from(array('round_table' => 'r'));
    $select->where("noble_title = 'Sir'"); // embedded value
    $select->where('favorite_color = ?', 'yellow'); // quoted value

    // SELECT foo.*
    //     FROM `foo` foo
    //     WHERE bar = 'baz'
    //     OR id IN('1', '2', '3')

    $select->from('foo');
    $select->where('bar = ?', 'baz');
    $select->orWhere('id IN(?)', array(1, 2, 3));

    ?>

Zend_Db_Select does not apply quoting or table aliases to columns named in WHERE clauses. If you combine PHP variables into the string for a WHERE expression, you should use the Zend_Db_Adapter quoteIdentifier() method to protect against illegal syntax.

    <?php
    ...
    $columnName = 'bar'; // or set by an untrusted source
    $whereExpr = $db->quoteIdentifier($columnName) . ' = ?';
    $select->where($whereExpr, 'baz');
    ?>

### 9.3.5. GROUP BY Clause

To group rows, use the group() method as many times as you wish. Each

    <?php

    // create a $db object, then get a SELECT tool.
    $select = $db->select();

    // SELECT COUNT(id)
    // FROM `foo` foo
    // GROUP BY `bar`, `baz`

    $select->from('foo', new Zend_Db_Expr('COUNT(id)'));
    $select->group('bar');
    $select->group('baz');

    // an equivalent group() call:
    $select->group(array('bar', 'baz'));

    ?>

Do not specify multiple columns as a single string of comma-separated column names. This syntax used in earlier preview releases of Zend_Db is no longer supported. Use an array instead.

Quoting is applied to every string element of the group() argument, but if the element is an object of type Zend_Db_Expr, its string value is used without quoting.

### 9.3.6. HAVING Conditions

To add HAVING conditions to the selected results, use the having() method. This method is identical in function to the where() method.

If you call having() multiple times, the conditions are ANDed together; if you want to OR a condition, use orHaving().

    <?php

    // create a $db object, then get a SELECT tool.
    $select = $db->select();

    // SELECT COUNT(id) AS count_id
    //     FROM `foo` foo
    //     GROUP BY `bar`, `baz`
    //     HAVING count_id > '1'

    $select->from('foo', new Zend_Db_Expr('COUNT(id) AS count_id'));
    $select->group(array('bar', 'baz'));
    $select->having('count_id > ?', 1);

    ?>

Zend_Db_Select does not apply quoting or table aliases to columns named in HAVING clauses. If you combine PHP variables into the string for a HAVING expression, you should use the Zend_Db_Adapter quoteIdentifier() method to protect against illegal syntax.

    <?php
    ...
    $columnName = 'count_id'; // or set by an untrusted source
    $havingExpr = $db->quoteIdentifier($columnName) . ' > ?';
    $select->having($havingExpr, 1);
    ?>

### 9.3.7. ORDER BY Clause

To ORDER columns, use the order() method as many times as you wish.

    <?php

    // create a $db object, then get a SELECT tool.
    $select = $db->select();

    // SELECT r.*
    // FROM `round_table` r
    // ORDER BY `noble_title` DESC, `first_name` ASC

    $select->from('round_table');
    $select->order('noble_title DESC');
    $select->order('first_name');

    // an equivalent order() call:
    $select->order(array('noble_title DESC', 'first_name'));

    ?>

Do not specify multiple columns as a single string of comma-separated column names. This syntax used in earlier preview releases of Zend_Db is no longer supported. Use an array instead.

Quoting is applied to every string element of the order() argument, but if the element is an object of type Zend_Db_Expr, its string value is used without quoting.

### 9.3.8. LIMIT By Count and Offset

Zend_Db_Select offers database-abstracted LIMIT clause support. For many databases, such as MySQL and PostgreSQL, this is relatively easy, as they support the the "LIMIT :count [OFFSET :offset]" syntax.

For some other databases, this is not so easy, because they don't support the LIMIT clause. Microsoft SQL Server has a TOP clause that can provide the same result. Oracle and DB2 require a query to be written in a special fashion to emulate a LIMIT. Zend_Db_Select can rewrite the SELECT appropriately for each database adapter to provide the LIMIT functionality.

To LIMIT the returned results by count and offset, use the limit() method with a count and optional offset.

    <?php

    // first, a simple "LIMIT :count"
    $select = $db->select();
    $select->from('foo');
    $select->order('id');
    $select->limit(10);

    // In MySQL/PostgreSQL/SQLite, this translates to:
    //
    // SELECT foo.*
    // FROM "foo" foo
    // ORDER BY "id" ASC
    // LIMIT 10
    //
    // But in Microsoft SQL Server, this translates to:
    //
    // SELECT TOP 10 foo.*
    // FROM [foo] foo
    // ORDER BY [id] ASC

    // now, a more complex "LIMIT :count OFFSET :offset"
    $select = $db->select();
    $select->from('foo', '*');
    $select->order('id');
    $select->limit(10, 20);

    // In MySQL/PostgreSQL/SQLite, this translates to:
    //
    // SELECT foo.*
    // FROM "foo" foo
    // ORDER BY "id" ASC
    // LIMIT 10 OFFSET 20
    //
    // Microsoft SQL Server has no offset support, so this translates to:
    //
    // SELECT * FROM (
    //     SELECT TOP 10 * FROM (
    //         SELECT TOP 30 *
    //         FROM [foo] foo
    //         ORDER BY [id] DESC
    //     ) ORDER BY id ASC
    // )
    //
    // Zend_Db_Adapter does the query translation for you automatically.

    ?>

### 9.3.9. LIMIT By Page And Count

Zend_Db_Select offers page-based limits as well. If you wish to retrieve a certain "page" of results, use the limitPage() method; first pass the page number you want, and then the number of rows that appear on each page.

    <?php

    // build the basic select...
    $select = $db->select();
    $select->from('foo');
    $select->order('id');

    // ... and limit to page 3 where each page has 10 rows
    $select->limitPage(3, 10);

    // In MySQL/PostgreSQL/SQLite, this translates to:
    //
    // SELECT foo.*
    // FROM `foo` foo
    // ORDER BY `id` ASC
    // LIMIT 10 OFFSET 20

    ?>

### 9.3.10. Other methods

The distinct() method enables you to add the DISTINCT keyword to your SQL query.

    <?php

    // SELECT DISTINCT foo.`non_unique_column`
    // FROM `foo` foo

    $select = $db->select();
    $select->distinct();
    $select->from('foo', 'non_unique_column');

    ?>

The forUpdate() method enables you to add the FOR UPDATE keywords to your SQL query.

    <?php

    // SELECT FOR UPDATE foo.*
    // FROM `foo` foo

    $select = $db->select();
    $select->forUpdate();
    $select->from('foo');

    ?>

The query() method is like the query() method of the Zend_Db_Adapter. It returns an object of type Zend_Db_Statement or PDOStatement, depending on the adapter type.

    <?php

    $select = $db->select();
    $select->from('foo');
    $stmt = $select->query();
    $result = $stmt->fetchAll();

    // This is equivalent to the following:
    $select = $db->select();
    $select->from('foo');
    $stmt = $db->query($select);
    $result = $stmt->fetchAll();

    ?>

The getPart() method returns the data you provided for part of the SQL query. The Zend_Db_Select class includes constant definitions that you can use for parts of the SQL query.

    <?php

    // SELECT foo.*
    // FROM `foo` foo
    // ORDER `keyColumn`

    $select = $db->select();
    $select->from('foo');
    $select->order('keyColumn');

    print_r( $select->getPart( Zend_Db_Select::ORDER ) );

    ?>

The reset() method enables you to clear one specified part of the SQL query, or else clear all parts of the SQL query if you omit the argument.

    <?php

    // SELECT foo.*
    // FROM `foo` foo
    // ORDER BY `column1`

    $select = $db->select();
    $select->from('foo');
    $select->order('column1');

    // Instead we want a different order criterion:
    //
    // SELECT foo.*
    // FROM `foo` foo
    // ORDER BY `column2`

    // Clear one part so we can redefine it
    $select->reset( Zend_Db_Select::ORDER );
    $select->order('column2');

    // Clear all parts of the query
    $select->reset();

    ?>

Prev | Up | Next
9.2. Zend_Db_Profiler | Home | 9.4. Zend_Db_Table