Previous Next

Zend_Db_Select

導入

Zend_Db_Select オブジェクトは、SQLSELECT 文を表すものです。 このクラスには、クエリの各部分を追加するためのメソッドが用意されています。 PHP のメソッドやデータをもとにクエリの各部分を指定すると、 このクラスが正確な SQL 文を作成してくれます。クエリを作成したら、 あとは通常の文字列と同じようにそれを用いてクエリを実行できます。

Zend_Db_Select は次のような機能を提供します。

  • SQL クエリを少しずつ組み立てていくための オブジェクト指向のメソッド

  • SQL クエリの一部について、データベースに依存しない抽象化

  • 大半のメタデータ識別子の自動クォート処理による、 予約語や特殊文字を含む SQL のサポート

  • 識別子や値のクォートによる、 SQL インジェクション攻撃対策

必ず Zend_Db_Select を使わなければならないというわけではありません。 単純な SELECT クエリを実行するのなら、SQL クエリ全体を文字列で指定し、 アダプタの query() メソッドや fetchAll() メソッドを使用したほうがずっとシンプルになるでしょう。 Zend_Db_Select を使うと便利なのは、 何らかの条件にもとづいて、アプリケーション内で SELECT クエリを動的に組み立てていく必要があるような場合です。

Select オブジェクトの作成

Zend_Db_Select オブジェクトのインスタンスを作成するには、 Zend_Db_Adapter_Abstract オブジェクトの select() メソッドを使用します。

例1 データベースアダプタの select() メソッドの例

$db = Zend_Db::factory( ...options... );
$select = $db->select();

Zend_Db_Select オブジェクトを作成するもうひとつの方法は、 コンストラクタの引数でデータベースアダプタを指定することです。

例2 新しい Select オブジェクトの作成の例

$db = Zend_Db::factory( ...options... );
$select = new Zend_Db_Select($db);

Select クエリの作成

クエリを作成する際に、クエリの句を個別に追加していくことができます。 Zend_Db_Select オブジェクトには、 個々の句を追加していくメソッドが用意されています。

例3 メソッドを使用して句を追加する例

// Zend_Db_Select オブジェクトを作成します
$select = $db->select();

// FROM 句を追加します
$select->from( ...テーブルとカラムを指定します... )

// WHERE 句を追加します
$select->where( ...検索条件を指定します... )

// ORDER BY 句を追加します
$select->order( ...ソート条件を指定します... );

Zend_Db_Select オブジェクトの大半のメソッドで、便利な 「流れるようなインターフェイス」形式を利用できます。これは、 各メソッドが、自分自身への参照を結果として返すということです。 つまり、その結果を使用してすぐに別のメソッドをコールできるのです。

例4 流れるようなインターフェイスの使用例

$select = $db->select()
    ->from( ...テーブルとカラムを指定します... )
    ->where( ...検索条件を指定します... )
    ->order( ...ソート条件を指定します... );

この節の例では流れるようなインターフェイスを使用しますが、 この方式を使用せずに使用することも可能です。 そうしなければならないこともよくあるでしょう。たとえば、 クエリに句を追加する前にアプリケーションで何らかの処理が必要な場合などです。

FROM 句の追加

このクエリのテーブルを指定するために from() メソッドを使用します。テーブル名は、単純に文字列で指定できます。 Zend_Db_Select はテーブル名を識別子としてクォートするので、 特殊文字を使用することもできます。

例5 from() メソッドの例

// できあがるクエリは、このようになります
//   SELECT *
//   FROM "products"

$select = $db->select()
             ->from( 'products' );

テーブルの相関名 (あるいは "エイリアス" とも言われます) を指定することもできます。その場合は、 単純な文字列ではなく連想配列を使用し、相関名とテーブル名の対応を指定します。 SQL のその他の句で、この相関名を使用できるようになります。 複数のテーブルを結合したクエリを作成する場合は、 Zend_Db_Select はそのテーブル名に基づいた一意な相関名を作成します。

例6 テーブルの相関名を指定する例

// できあがるクエリは、このようになります
//   SELECT p.*
//   FROM "products" AS p

$select = $db->select()
             ->from( array('p' => 'products') );

RDBMS によっては、テーブル名の前にスキーマ名をつなげる方式をサポートしているものもあります。 テーブル名として、"schemaName.tableName" のように指定できます。この場合、 Zend_Db_Select は各部分を個別にクォートします。 あるいはスキーマ名とテーブル名を別々に指定することもできます。 もし両方でスキーマ名を指定した場合は、 テーブル名と同時に指定したもののほうが優先されます。

例7 スキーマ名の指定の例

// できあがるクエリは、このようになります
//   SELECT *
//   FROM "myschema"."products"

$select = $db->select()
             ->from( 'myschema.products' );

// あるいは

$select = $db->select()
             ->from('products', '*', 'myschema');

カラムの追加

from() メソッドの二番目の引数で、 対応するテーブルから取得するカラムを指定できます。 カラムを指定しなかった場合のデフォルトは "*" で、これは "すべてのカラム" を表す SQL のワイルドカードです。

カラム名を指定するには、単純な文字列の配列を使用するか、 あるいは連想配列でエイリアスとカラム名を対応させます。 取得したいカラムがひとつだけの場合でエイリアスを使用しない場合は、 配列ではなく単純な文字列で指定することもできます。

空の配列をカラムの引数として指定すると、 対応するテーブルからのカラムは結果セットに含まれなくなります。 join() メソッドの コード例 を参照ください。

カラム名を "correlationName.columnName" の形式で指定することもできます。この場合、 Zend_Db_Select は各部分を個別にクォートします。 カラムの correlationName (相関名) を指定しなかった場合は、 現在の from() メソッドで指定したテーブルの名前を使用します。

例8 カラムを指定する例

// できあがるクエリは、このようになります
//   SELECT p."product_id", p."product_name"
//   FROM "products" AS p

$select = $db->select()
             ->from(array('p' => 'products'),
                    array('product_id', 'product_name'));

// 同じクエリを、相関名を指定して作成します
//   SELECT p."product_id", p."product_name"
//   FROM "products" AS p

$select = $db->select()
             ->from(array('p' => 'products'),
                    array('p.product_id', 'p.product_name'));

// このクエリを、カラムのひとつにエイリアスを指定して作成します
//   SELECT p."product_id" AS prodno, p."product_name"
//   FROM "products" AS p

$select = $db->select()
             ->from(array('p' => 'products'),
                    array('prodno' => 'product_id', 'product_name'));

式によるカラムの追加

SQL クエリでは、単にテーブルのカラムを使用するだけでなく 何らかの式をカラムとして使用することもあります。 このような場合は、相関名をつけたりクォートを適用したりしてはいけません。 カラム文字列に括弧が含まれている場合に、Zend_Db_Select はそれを式として扱います。

Zend_Db_Expr 型のオブジェクトを明示的に作成し、 文字列がカラム名と解釈されてしまうことを防ぐこともできます。 Zend_Db_Expr は、文字列をひとつだけ含む最小限のクラスです。 Zend_Db_SelectZend_Db_Expr 型のオブジェクトを認識し、 それを文字列に変換しますが、 その際にクォートや相関名などの処理を適用しません。

注意:

カラムとして使用する式に括弧が含まれている場合は、 カラム名で Zend_Db_Expr を指定する必要はありません。 Zend_Db_Select は、括弧を発見すると自動的にその文字列を式として扱います。 クォートや相関名の設定はされません。

例9 式を含むカラムの指定の例

// できあがるクエリは、このようになります
//   SELECT p."product_id", LOWER(product_name)
//   FROM "products" AS p
// 括弧つきの式は、暗黙のうちに
// Zend_Db_Expr として扱われます

$select = $db->select()
             ->from(array('p' => 'products'),
                    array('product_id', 'LOWER(product_name)'));

// できあがるクエリは、このようになります
//   SELECT p."product_id", (p.cost * 1.08) AS cost_plus_tax
//   FROM "products" AS p

$select = $db->select()
             ->from(array('p' => 'products'),
                    array('product_id',
                          'cost_plus_tax' => '(p.cost * 1.08)')
                   );

// このクエリを、明示的に Zend_Db_Expr を指定して作成します
//   SELECT p."product_id", p.cost * 1.08 AS cost_plus_tax
//   FROM "products" AS p

$select = $db->select()
             ->from(array('p' => 'products'),
                    array('product_id',
                          'cost_plus_tax' =>
                              new Zend_Db_Expr('p.cost * 1.08'))
                    );

上の例では、Zend_Db_Select は 相関名の設定や識別子のクォートといった処理を行いません。 あいまいさを解決するためにそのような処理が必要な場合は、 手動で文字列を変更する必要があります。

カラム名が SQL キーワードや特殊文字を含んでいる場合は、 アダプタの quoteIdentifier() メソッドを使用して結果を操作する必要があります。 quoteIdentifier() は、 識別子に対して SQL のクォート処理を行います。 これによりテーブルやカラムといった識別子を SQL のそれ以外の部分と区別できるようになります。

クォート処理を直接文字列に埋め込んでしまわずに quoteIdentifier() メソッドを使用することで、 あなたのコードをデータベースに依存しないものにできます。 というのも、RDBMS によってはあまり標準的ではない文字でクォートを行うものもあるからです。 quoteIdentifier() メソッドは、 アダプタの型に応じて適切なクォート文字を使用するように設計されています。 quoteIdentifier() メソッドはまた、 識別子の名前の中に登場するクォート文字自体もエスケープします。

例10 式の中のカラムをクォートする例

// このクエリを作成する際に、式の中にある特別なカラム名 "from" をクォートします
//   SELECT p."from" + 10 AS origin
//   FROM "products" AS p

$select = $db->select()
             ->from(array('p' => 'products'),
                    array('origin' =>
                              '(p.' . $db->quoteIdentifier('from') . ' + 10)')
                   );

既存の FROM あるいは JOIN テーブルへのカラムの追加

既存の FROM や JOIN のテーブルに対して、それらのメソッドをコールした後で カラムを追加したくなることもあるかもしれません。 columns() メソッドを使用すると、 クエリを実行する前ならいつでも好きなときに特定のカラムを追加できます。 カラムは、文字列あるいは Zend_Db_Expr、 あるいはその配列で指定します。 このメソッドの 2 番目の引数は省略可能です。 省略した場合は、FROM テーブルにカラムが追加されます。 指定する場合は、既存の相関名を使用しなければなりません。

例11 columns() メソッドでカラムを追加する例

// できあがるクエリは、このようになります
//   SELECT p."product_id", p."product_name"
//   FROM "products" AS p

$select = $db->select()
             ->from(array('p' => 'products'), 'product_id')
             ->columns('product_name');

// 同じクエリを、相関名を指定して作成します
//   SELECT p."product_id", p."product_name"
//   FROM "products" AS p

$select = $db->select()
             ->from(array('p' => 'products'), 'p.product_id')
             ->columns('product_name', 'p');
             // あるいは columns('p.product_name')

JOIN による、クエリへの別のテーブルの追加

有用なクエリの多くは、JOIN を使用して複数テーブルの行を結合しています。 テーブルを Zend_Db_Select クエリに追加するには、 join() メソッドを使用します。 このメソッドの使用法は from() メソッドと似ていますが、ほとんどの場合に結合条件を指定するという点が異なります。

例12 join() メソッドの例

// できあがるクエリは、このようになります
//   SELECT p."product_id", p."product_name", l.*
//   FROM "products" AS p JOIN "line_items" AS l
//     ON p.product_id = l.product_id

$select = $db->select()
             ->from(array('p' => 'products'),
                    array('product_id', 'product_name'))
             ->join(array('l' => 'line_items'),
                    'p.product_id = l.product_id');

join() の二番目の引数として、 結合条件を文字列で指定します。これは、 あるテーブルの行が別のテーブルのどの行と対応するのかを表す条件式です。 式の中では相関名を使用できます。

注意:

結合条件に指定した式に関しては、クォート処理は行われません。 クォートする必要のあるカラム名を使用する場合は、 結合条件の文字列を作成する際に quoteIdentifier() を使用しなければなりません。

join() の三番目の引数はカラム名を表す配列です。 これは from() メソッドで使用する形式と似ています。 デフォルトは "*" です。 相関名や式、Zend_Db_Expr についての扱いは、 from() メソッドにおけるカラム名の配列と同じです。

テーブルからカラムを取得しない場合は、 カラムリストに空の配列を使用します。 これは from() メソッドでも同様に動作しますが、 普通は最初のテーブルからは何らかのカラムを取得するでしょう。 一方、連結するテーブルについてはカラムを取得しないこともありえます。

例13 カラムを指定しない例

// できあがるクエリは、このようになります
//   SELECT p."product_id", p."product_name"
//   FROM "products" AS p JOIN "line_items" AS l
//     ON p.product_id = l.product_id

$select = $db->select()
             ->from(array('p' => 'products'),
                    array('product_id', 'product_name'))
             ->join(array('l' => 'line_items'),
                    'p.product_id = l.product_id',
                    array() ); // 空のカラムリスト

上の例で、連結したテーブルのカラム一覧の場所に 空の配列 array() を指定していることに注意しましょう。

SQL の結合にはいくつかの形式があります。 以下に、Zend_Db_Select がサポートする結合の形式をまとめます。

  • join(table, join, [columns]) メソッドあるいは joinInner(table, join, [columns]) メソッドによる INNER JOIN

    これはもっとも一般的な結合形式です。各テーブルの行を、 指定した結合条件に基づいて比較します。 結果セットには、その結合条件を満たす行のみが含まれます。 条件を満たす行がない場合は、結果セットが空になることもあります。

    すべての RDBMS が、この結合形式に対応しています。

  • joinLeft(table, condition, [columns]) メソッドによる LEFT JOIN

    左側のテーブルのすべての行と 右側のテーブルの条件にマッチする行が含まれます。 右側のテーブルからのカラムのうち、 左側のテーブルに対応する行がないものについては NULL で埋められます。

    すべての RDBMS が、この結合形式に対応しています。

  • joinRight(table, condition, [columns]) メソッドによる RIGHT JOIN

    右外部結合は、左外部結合を補完するものです。 右側のテーブルのすべての行と 左側のテーブルの条件にマッチする行が含まれます。 左側のテーブルからのカラムのうち、 右側のテーブルに対応する行がないものについては NULL で埋められます。

    RDBMS によっては、この結合形式に対応していないものもあります。 しかし、一般に右外部結合は、 テーブルの順番を入れ替えれば左外部結合として表すことが可能です。

  • joinFull(table, condition, [columns]) メソッドによる FULL JOIN

    完全外部結合は、左外部結合と右外部結合を組み合わせたようなものです。 両側のテーブルのすべての行が含まれます。 結合条件を満たす組み合わせがあった場合はそれらが同一行にまとめられ、 それ以外の場合は、対応するデータがないカラムについては NULL で埋められます。

    RDBMS によっては、この結合形式に対応していないものもあります。

  • joinCross(table, [columns]) メソッドによる CROSS JOIN

    クロス結合とは、デカルト積のことです。 最初のテーブルの各行に対して、 二番目のテーブルのすべての行がマッチします。 つまり、結果セットの行数は、 ふたつのテーブルの行数の積と等しくなります。 結果セットをフィルタリングするには、WHERE 句で条件を指定します。 この方法によるクロス結合は、昔の SQL-89 の結合構文と似ています。

    joinCross() メソッドには、 結合条件を指定するパラメータがありません。 RDBMS によっては、この結合形式に対応していないものもあります。

  • joinNatural(table, [columns]) メソッドによる NATURAL JOIN

    自然結合は、両方のテーブルに同じ名前で登場するカラムを比較します。 比較はすべてのカラムに対して行われます。 この API でサポートしているのは、自然内部結合のみです。 SQL で自然外部結合がサポートされていたとしても、使用できません。

    joinNatural() メソッドには、 結合条件を指定するパラメータはありません。

これらの結合メソッドに加え、クエリを単純にするために JoinUsing メソッドを使用できます。完全な結合条件を渡すかわりに、 単純に結合するカラム名の配列を渡してやれば Zend_Db_Select オブジェクトが結合条件を作成してくれます。

例14 joinUsing() メソッドの例

// できあがるクエリは、このようになります
//   SELECT *
//   FROM "table1"
//   JOIN "table2"
//   ON "table1".column1 = "table2".column1
//   WHERE column2 = 'foo'

$select = $db->select()
             ->from('table1')
             ->joinUsing('table2', 'column1')
             ->where('column2 = ?', 'foo');

Zend_Db_Select の結合メソッドには、 それぞれ対応する 'using' メソッドがあります。

  • joinUsing(table, join, [columns]) および joinInnerUsing(table, join, [columns])

  • joinLeftUsing(table, join, [columns])

  • joinRightUsing(table, join, [columns])

  • joinFullUsing(table, join, [columns])

WHERE 句の追加

結果セットの行を制限するための条件を指定するには where() メソッドを使用します。 このメソッドの最初の引数は SQL の式で、これをクエリの SQLWHERE 句として使用します。

例15 where() メソッドの例

// できあがるクエリは、このようになります
//   SELECT product_id, product_name, price
//   FROM "products"
//   WHERE price > 100.00

$select = $db->select()
             ->from('products',
                    array('product_id', 'product_name', 'price'))
             ->where('price > 100.00');

注意:

where() メソッドや orWhere() メソッドで指定する式にはクォート処理は行われません。 クォートする必要のあるカラム名を使用する場合は、 条件の文字列を作成する際に quoteIdentifier() を使用しなければなりません。

where() メソッドの二番目の引数はオプションです。 これは式を置き換える値となります。 Zend_Db_Select は値をクォートし、式の中の クエスチョンマーク ("?") をその値で置き換えます。

例16 where() メソッドでのパラメータの例

// できあがるクエリは、このようになります
//   SELECT product_id, product_name, price
//   FROM "products"
//   WHERE (price > 100.00)

$minimumPrice = 100;

$select = $db->select()
             ->from('products',
                    array('product_id', 'product_name', 'price'))
             ->where('price > ?', $minimumPrice);

SQL の IN 演算子を使うとき、 where() メソッドに 第2引数として配列を渡せます。

例17 where() メソッドでの配列パラメータ例

// クエリをビルド
//   SELECT product_id, product_name, price
//   FROM "products"
//   WHERE (product_id IN (1, 2, 3))

$productIds = array(1, 2, 3);

$select = $db->select()
             ->from('products',
                    array('product_id', 'product_name', 'price'))
             ->where('product_id IN (?)', $productIds);

Zend_Db_Select オブジェクト上で、 where() メソッドを複数回実行することもできます。その結果のクエリは、 指定した条件を AND でひとつにまとめたものとなります。

例18 複数の where() メソッドの例

// できあがるクエリは、このようになります
//   SELECT product_id, product_name, price
//   FROM "products"
//   WHERE (price > 100.00)
//     AND (price < 500.00)

$minimumPrice = 100;
$maximumPrice = 500;

$select = $db->select()
             ->from('products',
                    array('product_id', 'product_name', 'price'))
             ->where('price > ?', $minimumPrice)
             ->where('price < ?', $maximumPrice);

複数の条件を OR で連結したい場合は、 orWhere() メソッドを使用します。 このメソッドの使用法は where() メソッドとほとんど同じですが、条件の前には AND ではなく OR がつくことになります。

例19 orWhere() メソッドの例

// できあがるクエリは、このようになります
//   SELECT product_id, product_name, price
//   FROM "products"
//   WHERE (price < 100.00)
//     OR (price > 500.00)

$minimumPrice = 100;
$maximumPrice = 500;

$select = $db->select()
             ->from('products',
                    array('product_id', 'product_name', 'price'))
             ->where('price < ?', $minimumPrice)
             ->orWhere('price > ?', $maximumPrice);

Zend_Db_Select は、 where() メソッドや orWhere() メソッドで指定した式の両側に 自動的に括弧をつけます。これにより、 論理演算子が予期せぬ結果を引き起こすことを防ぎます。

例20 論理式を括弧で囲む例

// できあがるクエリは、このようになります
//   SELECT product_id, product_name, price
//   FROM "products"
//   WHERE (price < 100.00 OR price > 500.00)
//     AND (product_name = 'Apple')

$minimumPrice = 100;
$maximumPrice = 500;
$prod = 'Apple';

$select = $db->select()
             ->from('products',
                    array('product_id', 'product_name', 'price'))
             ->where("price < $minimumPrice OR price > $maximumPrice")
             ->where('product_name = ?', $prod);

上の例では、括弧がなければ結果はまったく異なるものとなります。 なぜなら、AND のほうが OR よりも優先順位が高いからです。Zend_Db_Select は括弧をつけるので、それぞれの where() で指定された式の結合度が AND より高くなります。

GROUP BY 句の追加

SQLGROUP BY 句を使用すると、 結果セットの行数を減らすことができます。 GROUP BY 句で指定したカラムの一意な値ごとに、 結果が一行にまとめられます。

Zend_Db_Select では、行のグループ化を行うためのカラムを group() メソッドで指定します。 このメソッドへの引数は、GROUP BY 句で使用するカラムあるいは複数カラムの配列となります。

例21 group() メソッドの例

// できあがるクエリは、このようになります
//   SELECT p."product_id", COUNT(*) AS line_items_per_product
//   FROM "products" AS p JOIN "line_items" AS l
//     ON p.product_id = l.product_id
//   GROUP BY p.product_id

$select = $db->select()
             ->from(array('p' => 'products'),
                    array('product_id'))
             ->join(array('l' => 'line_items'),
                    'p.product_id = l.product_id',
                    array('line_items_per_product' => 'COUNT(*)'))
             ->group('p.product_id');

from() メソッドでのカラムの配列と同様、 カラム名には相関名を使用できます。また、 カラム名は識別子としてクォートされます。 ただし、文字列に括弧が含まれたり Zend_Db_Expr 型のオブジェクトを指定したりした場合は別です。

HAVING 句の追加

SQLHAVING 句を使用すると、 グループ化した行に制約を適用します。これは、 WHERE 句が行に対して制約を適用するのと同じです。 しかし、これらには相違点があります。 WHERE 条件はグループ化の前に適用されますが、 HAVING 条件はグループ化された後に適用されます。

Zend_Db_Select では、グループに対する制約を指定するには having() メソッドを使用します。 このメソッドの使用法は where() メソッドと似ています。 最初の引数が SQL の式を含む文字列です。二番目の引数はオプションで、 SQL 式の中のパラメータプレースホルダを置き換える値となります。 having() を複数回実行すると、それらの条件が 論理演算子 AND で連結されます。 orHaving() メソッドを使用した場合は、論理演算子 OR で連結されます。

例22 having() メソッドの例

// できあがるクエリは、このようになります
//   SELECT p."product_id", COUNT(*) AS line_items_per_product
//   FROM "products" AS p JOIN "line_items" AS l
//     ON p.product_id = l.product_id
//   GROUP BY p.product_id
//   HAVING line_items_per_product > 10

$select = $db->select()
             ->from(array('p' => 'products'),
                    array('product_id'))
             ->join(array('l' => 'line_items'),
                    'p.product_id = l.product_id',
                    array('line_items_per_product' => 'COUNT(*)'))
             ->group('p.product_id')
             ->having('line_items_per_product > 10');

注意:

having() メソッドや orHaving() メソッドで指定する式にはクォート処理は行われません。 クォートする必要のあるカラム名を使用する場合は、 条件の文字列を作成する際に quoteIdentifier() を使用しなければなりません。

ORDER BY 句の追加

SQLORDER BY 句では、 クエリの結果セットの並べ替えの基準となるカラムや式を指定します。 複数のカラムを指定すると、最初のカラムの値が同じだった場合に 二番目のカラムを用いて並べ替えを行います。 デフォルトでは、小さいほうから大きいほうに向かって並べ替えます。 逆に大きいほうから小さいほうに向かって並べ替えるには、 カラムリストの中のそのカラム名の後に、キーワード DESC を指定します。

Zend_Db_Select では、 order() メソッドを使用して 並べ替えの基準となるカラムあるいはカラムの配列を指定します。 配列の各要素はカラム名を表す文字列です。オプションとして、 スペースをはさんでキーワード ASCDESC を続けます。

from() メソッドや group() メソッドと同様、カラム名は識別子としてクォートされます。 ただし、文字列に括弧が含まれたり Zend_Db_Expr 型のオブジェクトを指定したりした場合は別です。

例23 order() メソッドの例

// できあがるクエリは、このようになります
//   SELECT p."product_id", COUNT(*) AS line_items_per_product
//   FROM "products" AS p JOIN "line_items" AS l
//     ON p.product_id = l.product_id
//   GROUP BY p.product_id
//   ORDER BY "line_items_per_product" DESC, "product_id"

$select = $db->select()
             ->from(array('p' => 'products'),
                    array('product_id'))
             ->join(array('l' => 'line_items'),
                    'p.product_id = l.product_id',
                    array('line_items_per_product' => 'COUNT(*)'))
             ->group('p.product_id')
             ->order(array('line_items_per_product DESC',
                           'product_id'));

LIMIT 句の追加

RDBMS によっては、SQL を拡張して、いわゆる LIMIT 句を使用できるようにしているものもあります。 これは、結果セットの行数を、最大でも指定した数までに制限します。 また、出力を始める前に読み飛ばす行数を指定することもできます。 この機能を使用すると、結果セットの一部だけを取得することが簡単になります。 たとえば、クエリの結果をページに分けて出力する場合などに便利です。

Zend_Db_Select では、 limit() メソッドを使用して結果の行数および読み飛ばしの行数を指定します。 このメソッドの最初の引数は取得したい行数、 そして二番目の引数は読み飛ばす行数となります。

例24 limit() メソッドの例

// できあがるクエリは、このようになります
//   SELECT p."product_id", p."product_name"
//   FROM "products" AS p
//   LIMIT 10, 20
// 以下に相当します
//   SELECT p."product_id", p."product_name"
//   FROM "products" AS p
//   LIMIT 20 OFFSET 10
$select = $db->select()
             ->from(array('p' => 'products'),
                    array('product_id', 'product_name'))
             ->limit(20, 10);

注意:

LIMIT 構文は、すべての RDBMS でサポートされているわけではありません。RDBMS によっては、 似た機能を別の構文でサポートしているものもあります。 各 Zend_Db_Adapter_Abstract クラスには、 その RDBMS に対応した適切な SQL を作成するメソッドが用意されています。

一方、 limitPage() メソッドを用いることによっても行数とオフセットを指定できます。 このメソッドは、クエリの結果セット全体から特定の箇所の連続した行のみを取得するものです。 つまり、結果の「ページ」を指定することで、 そのページに該当する部分の結果のみを取得するというわけです。 limitPage() メソッドの最初の引数にページ数、 2 番目の引数にページあたりの行数を指定します。 どちらの引数も必須で、デフォルト値はありません。

例25 limitPage() メソッドの例

// できあがるクエリは、このようになります
//   SELECT p."product_id", p."product_name"
//   FROM "products" AS p
//   LIMIT 10, 20

$select = $db->select()
             ->from(array('p' => 'products'),
                    array('product_id', 'product_name'))
             ->limitPage(2, 10);

クエリ修飾子 DISTINCT の追加

distinct() メソッドを使用すると、SQL クエリに DISTINCT キーワードを追加できます。

例26 distinct() メソッドの例

// できあがるクエリは、このようになります
//   SELECT DISTINCT p."product_name"
//   FROM "products" AS p

$select = $db->select()
             ->distinct()
             ->from(array('p' => 'products'), 'product_name');

クエリ修飾子 FOR UPDATE の追加

forUpdate() メソッドを使用すると、SQL クエリに FOR UPDATE 修飾子を追加できます。

例27 forUpdate() メソッドの例

// できあがるクエリは、このようになります
//   SELECT FOR UPDATE p.*
//   FROM "products" AS p

$select = $db->select()
             ->forUpdate()
             ->from(array('p' => 'products'));

UNION クエリの構築

union()メソッドにZend_Db_Selectの配列、 または SQL クエリ文字列を渡すことによって、 Zend_Db_Selectで結合クエリを構築できます。 どの種類の結合を実行したいか指定するために、 第2引数として、Zend_Db_Select::SQL_UNION、 またはZend_Db_Select::SQL_UNION_ALL定数を渡せます。

例28 union() メソッド例

$sql1 = $db->select();
$sql2 = "SELECT ...";

$select = $db->select()
    ->union(array($sql1, $sql2))
    ->order("id");

Select クエリの実行

この節では、Zend_Db_Select オブジェクトが表すクエリを実行する方法を説明します。

Db アダプタからの Select クエリの実行

Zend_Db_Select オブジェクトが表すクエリを実行するには、それを Zend_Db_Adapter_Abstract オブジェクトの query() メソッドの最初の引数として渡します。すると、 文字列のクエリのかわりに Zend_Db_Select オブジェクトを使用するようになります。

query() メソッドは、アダプタの型によって Zend_Db_Statement あるいは PDOStatement 型のオブジェクトを返します。

例29 Db アダプタの query() メソッドの使用例

$select = $db->select()
             ->from('products');

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

オブジェクトからの Select クエリの実行

アダプタオブジェクトの query() メソッドを使用する以外の方法としては、Zend_Db_Select オブジェクトの query() メソッドを使用するものがあります。 どちらのメソッドも、アダプタの型によって Zend_Db_Statement あるいは PDOStatement 型のオブジェクトを返します。

例30 Select オブジェクトの query メソッドの使用例

$select = $db->select()
             ->from('products');

$stmt = $select->query();
$result = $stmt->fetchAll();

Select オブジェクトから SQL 文字列への変換

Zend_Db_Select オブジェクトに対応する SQL クエリ文字列にアクセスしたい場合は、 __toString() メソッドを使用します。

例31 __toString() メソッドの例

$select = $db->select()
             ->from('products');

$sql = $select->__toString();
echo "$sql\n";

// 出力は、次のような文字列になります
//   SELECT * FROM "products"

その他のメソッド

この節では、これまでにあげてこなかった Zend_Db_Select クラスのメソッドである getPart() および reset() について説明します。

Select オブジェクトの一部の取得

getPart() メソッドは、SQL クエリの一部を返します。 たとえば、このメソッドを使用すると、 WHERE 句の式を表す配列や SELECT するカラム (あるいは式) の配列、または LIMIT 句のカウントやオフセットを取得できます。

返り値は、SQL の一部を抜き取った文字列ではありません。 オブジェクトでの内部表現で、通常は値と式を含む配列となります。 クエリの各部分によって、その構造は異なります。

getPart() メソッドの引数はひとつで、 Select クエリのどの部分を返すのかをここで指定します。 たとえば、文字列 'from' を指定すると、 Select オブジェクトが FROM 句として保持しているテーブルの情報を返します。 ここには結合している他のテーブルも含まれます。

Zend_Db_Select クラスでは、SQL クエリの各部分を指定するための定数を定義しています。 これらの定数、あるいはリテラル文字列のいずれかで指定できます。

getPart() および reset() で使用する定数
定数 文字列値
Zend_Db_Select::DISTINCT 'distinct'
Zend_Db_Select::FOR_UPDATE 'forupdate'
Zend_Db_Select::COLUMNS 'columns'
Zend_Db_Select::FROM 'from'
Zend_Db_Select::WHERE 'where'
Zend_Db_Select::GROUP 'group'
Zend_Db_Select::HAVING 'having'
Zend_Db_Select::ORDER 'order'
Zend_Db_Select::LIMIT_COUNT 'limitcount'
Zend_Db_Select::LIMIT_OFFSET 'limitoffset'

例32 getPart() メソッドの例

$select = $db->select()
             ->from('products')
             ->order('product_id');

// 文字列リテラルを使用して指定できます
$orderData = $select->getPart( 'order' );

// 同じことを、定数を用いて指定することもできます
$orderData = $select->getPart( Zend_Db_Select::ORDER );

// 返り値は、文字列ではなく配列となります。
// 各部分が異なる構造になっています。
print_r( $orderData );

Select オブジェクトの一部のリセット

reset() メソッドを使用すると、 SQL クエリの指定した部分のみを消去できます。 引数を省略した場合は、すべての部分を消去します。

引数はひとつで、これは省略可能です。 消去したい SQL の部分を、 getPart() メソッドの引数と同じ文字列で指定します。 クエリの指定した部分が、デフォルトの状態に戻ります。

パラメータを省略すると、 reset() はクエリのすべての部分をデフォルトの状態に戻します。 これにより、Zend_Db_Select オブジェクトは初期状態と同等になります。 つまり、最初にインスタンスを作成したときと同じ状態ということです。

例33 reset() メソッドの例

// できあがるクエリは、このようになります
//   SELECT p.*
//   FROM "products" AS p
//   ORDER BY "product_name"

$select = $db->select()
             ->from(array('p' => 'products')
             ->order('product_name');

// 条件を変更し、別のカラムで並べ替えます
//   SELECT p.*
//   FROM "products" AS p
//   ORDER BY "product_id"

// 再定義するため、いちどこの部分を消去します
$select->reset( Zend_Db_Select::ORDER );

// そして異なるカラムを指定します
$select->order('product_id');

// クエリ全体を消去します
$select->reset();
Previous Next
Zend Framework の紹介
概要
インストール
Learning Zend Framework
Zend Framework Quick Start
Zend Framework & MVC Introduction
Create Your Project
Create A Layout
Create a Model and Database Table
Create A Form
Congratulations!
Autoloading in Zend Framework
Introduction
Goals and Design
Basic Autoloader Usage
Resource Autoloading
Conclusion
Plugins in Zend Framework
Introduction
Using Plugins
Conclusion
Getting Started with Zend_Layout
Introduction
Using Zend_Layout
Zend_Layout: Conclusions
Getting Started Zend_View Placeholders
Introduction
Basic Placeholder Usage
Standard Placeholders
View Placeholders: Conclusion
Understanding and Using Zend Form Decorators
Introduction
Decorator Basics
Layering Decorators
Rendering Individual Decorators
Creating and Rendering Composite Elements
Conclusion
Getting Started with Zend_Session, Zend_Auth, and Zend_Acl
Building Multi-User Applications With Zend Framework
Managing User Sessions In ZF
Authenticating Users in Zend Framework
Building an Authorization System in Zend Framework
Getting Started with Zend_Search_Lucene
Zend_Search_Lucene Introduction
Lucene Index Structure
Index Opening and Creation
Indexing
Searching
Supported queries
Search result pagination
Getting Started with Zend_Paginator
Introduction
Simple Examples
Pagination Control and ScrollingStyles
Putting it all Together
Zend Framework リファレンス
Zend_Acl
導入
アクセス制御の洗練
高度な使用法
Zend_Amf
導入
Zend_Amf_Server
Zend_Application
導入
Zend_Application クイックスタート
Theory of Operation
コア機能
利用できるリソースプラグイン
Zend_Auth
導入
データベースのテーブルでの認証
ダイジェスト認証
HTTP 認証アダプタ
LDAP 認証
Open ID 認証
Zend_Barcode
導入
Barcode creation using Zend_Barcode class
Zend_Barcode Objects
Zend_Barcode Renderers
Zend_Cache
導入
キャッシュの仕組み
Zend_Cache のフロントエンド
Zend_Cache のバックエンド
The Cache Manager
Zend_Captcha
導入
Captcha の方法
CAPTCHA アダプタ
Zend_CodeGenerator
導入
Zend_CodeGeneratorサンプル
Zend_CodeGenerator リファレンス
Zend_Config
導入
動作原理
Zend_Config_Ini
Zend_Config_Xml
Zend_Config_Writer
Zend_Config_Writer
Zend_Console_Getopt
導入
Getopt の規則の宣言
オプションおよび引数の取得
Zend_Console_Getopt の設定
Zend_Controller
Zend_Controller クイックスタート
Zend_Controller の基本
フロントコントローラ
リクエストオブジェクト
標準のルータ
ディスパッチャ
アクションコントローラ
アクションヘルパー
レスポンスオブジェクト
プラグイン
モジュラーディレクトリ構造の規約の使用
MVC での例外
Zend_Currency
Zend_Currency について
Using Zend_Currency
Options for currencies
What makes a currency?
Where is the currency?
How does the currency look like?
How much is my currency?
Calculating with currencies
Exchanging currencies
Additional informations on Zend_Currency
Zend_Date
導入
動作原理
基本メソッド
Zend_Date API の概要
日付の作成
日付関数全般用の定数
動作例
Zend_Db
Zend_Db_Adapter
Zend_Db_Statement
Zend_Db_Profiler
Zend_Db_Select
Zend_Db_Table
Zend_Db_Table_Row
Zend_Db_Table_Rowset
導入
Zend_Db_Table_Definition
Zend_Debug
変数の出力
Zend_Dojo
導入
Zend_Dojo_Data: dojo.data エンベロープ
Dojo ビューヘルパー
Dojoフォーム要素とデコレーター
Zend_Dojo build layer support
Zend_Dom
導入
Zend_Dom_Query
Zend_Exception
例外の使用法
基本的な使用法
Previous Exceptions (前の例外)
Zend_Feed
導入
フィードの読み込み
ウェブページからのフィードの取得
RSS フィードの使用
Atom フィードの使用
単一の Atom エントリの処理
フィードおよびエントリの構造の変更
独自のフィードクラスおよびエントリクラス
Zend_Feed_Reader
Zend_Feed_Writer
Zend_Feed_Pubsubhubbub
Zend_File
Zend_File_Transfer
Zend_File_Transfer 用のバリデータ
Filters for Zend_File_Transfer
Zend_Filter
導入
標準のフィルタクラス群
フィルタチェイン
フィルタの書き方
Zend_Filter_Input
Zend_Filter_Inflector
Zend_Form
Zend_Form
Zend_Form クイックスタート
Zend_Form_Element を用いたフォーム要素の作成
Zend_Form によるフォームの作成
Zend_Form_Decorator による独自のフォームマークアップの作成
Zend Framework に同梱されている標準のフォーム要素
Zend Framework に同梱されている標準のデコレータ
Zend_Form の国際化
Zend_Form の高度な使用法
Zend_Gdata
導入
AuthSub による認証
Using the Book Search Data API
ClientLogin による認証
Google Calendar の使用法
Google Documents List Data API の使用法
Using Google Health
Google Spreadsheets の使用法
Google Apps Provisioning の使用法
Google Base の使用法
Picasa Web Albums の使用法
YouTube Data API の使用法
Gdata の例外処理
Zend_Http
導入
Zend_Http_Client - 高度な使用法
Zend_Http_Client - 接続アダプタ
Zend_Http_Cookie および Zend_Http_CookieJar
Zend_Http_Response
Zend_InfoCard
導入
Zend_Json
導入
Basic Usage
Zend_Json の高度な使用法
XML から JSON への変換
Zend_Json_Server - JSON-RPCサーバー
Zend_Layout
導入
Zend_Layout クイックスタート
Zend_Layout の設定オプション
Zend_Layout の高度な使用法
Zend_Ldap
導入
API概要
利用シナリオ
ツール
Zend_Ldap_Nodeを使用してLDAPツリーへのオブジェクト指向アクセス
LDAPサーバから情報を取得
LDIFへ、または、からのLDAPデータシリアライズ
Zend_Loader
ファイルやクラスの動的な読み込み
The Autoloader
Resource Autoloaders
プラグインのロード
Zend_Locale
導入
Zend_Locale の使用法
正規化および地域化
日付および時刻の扱い
サポートするロケール
Zend_Log
概要
ライター
フォーマッタ
フィルタ
Using the Factory to Create a Log
Zend_Mail
導入
SMTP 経由での送信
SMTP 接続による複数のメールの送信
異なる転送手段の使用
HTML メール
ファイルの添付
受信者の追加
MIME バウンダリの制御
追加のヘッダ
文字セット
エンコーディング
SMTP 認証
セキュアな SMTP トランスポート
メールメッセージの読み込み
Zend_Markup
導入
さあ始めましょう
Zend_Markup パーサー
Zend_Markup レンダラー
Zend_Measure
導入
計測値の作成
計測値の出力
計測値の操作
計測値の型
Zend_Memory
概要
メモリマネージャ
メモリオブジェクト
Zend_Mime
Zend_Mime
Zend_Mime_Message
Zend_Mime_Part
Zend_Navigation
導入
画面
Containers
Zend_Oauth
Introduction to OAuth
Zend_OpenId
導入
Zend_OpenId_Consumer の基本
Zend_OpenId_Provider
Zend_Paginator
導入
使用法
設定
高度な使用法
Zend_Pdf
導入
PDF ドキュメントの作成および読み込み
PDF ドキュメントへの変更内容の保存
ページの操作
描画
Interactive Features
ドキュメントの情報およびメタデータ
Zend_Pdf モジュールの使用例
Zend_ProgressBar
Zend_ProgressBar
Zend_Queue
導入
使用例
フレームワーク
アダプタ
Zend_Queueのカスタマイズ
Stomp
Zend_Reflection
導入
Zend_Reflectionサンプル
Zend_Reflectionリファレンス
Zend_Registry
レジストリの使用法
Zend_Rest
導入
Zend_Rest_Client
Zend_Rest_Server
Zend_Search_Lucene
概要
インデックスの構築
インデックスの検索
クエリ言語
クエリ作成用の API
文字セット
拡張性
Java Lucene との相互運用
応用
ベストプラクティス
Zend_Serializer
Introduction
Zend_Serializer_Adapter
Zend_Server
導入
Zend_Server_Reflection
Zend_Service
導入
Zend_Service_Akismet
Zend_Service_Amazon
Zend_Service_Amazon_Ec2
Zend_Service_Amazon_Ec2: Instances
Zend_Service_Amazon_Ec2: Windows Instances
Zend_Service_Amazon_Ec2: Reserved Instances
Zend_Service_Amazon_Ec2: CloudWatch Monitoring
Zend_Service_Amazon_Ec2: Amazon Machine Images (AMI)
Zend_Service_Amazon_Ec2: Elastic Block Storage (EBS)
Zend_Service_Amazon_Ec2: Elastic IP Addresses
Zend_Service_Amazon_Ec2: Keypairs
Zend_Service_Amazon_Ec2:リージョンおよび利用可能ゾーン
Zend_Service_Amazon_Ec2: Security Groups
Zend_Service_Amazon_S3
Zend_Service_Amazon_Sqs
Zend_Service_Audioscrobbler
Zend_Service_Delicious
Zend_Service_DeveloperGarden
Zend_Service_Flickr
Zend_Service_LiveDocx
Zend_Service_Nirvanix
Zend_Service_ReCaptcha
Zend_Service_Simpy
導入
Zend_Service_StrikeIron
Zend_Service_StrikeIron: バンドルされているサービス
Zend_Service_StrikeIron: 応用編
Zend_Service_Technorati
Zend_Service_Twitter
Zend_Service_WindowsAzure
Zend_Service_Yahoo
Zend_Session
導入
基本的な使用法
高度な使用法
グローバルセッションの管理
Zend_Session_SaveHandler_DbTable
Zend_Soap
Zend_Soap_Server
Zend_Soap_Client
WSDLアクセッサ
自動検出
Zend_Tag
導入
Zend_Tag_Cloud
Zend_Test
導入
Zend_Test_PHPUnit
Zend_Test_PHPUnit_Db
Zend_Text
Zend_Text_Figlet
Zend_Text_Table
Zend_TimeSync
導入
Zend_TimeSync の動作
Zend_Tool
Using Zend_Tool On The Command Line
Extending Zend_Tool
Zend_Tool_Framework
導入
CLIツールの使用
アーキテクチャ
Zend_Tool_Frameworkを利用してプロバイダを作成する
出荷されたシステムプロバイダー
Extending and Configuring Zend_Tool_Framework
Zend_Tool_Project
導入
プロジェクトを作成
Zend Toolプロジェクトプロバイダー
Zend_Tool_Project内部
Zend_Translate
導入
Zend_Translate のアダプタ
翻訳アダプタの使用法
ソースファイルの作成
翻訳用の追加機能
Plural notations for Translation
Zend_Uri
Zend_Uri
Zend_Validate
導入
標準のバリデーションクラス群
バリデータチェイン
バリデータの書き方
検証メッセージ
Zend_Version
Zend Framework のバージョンの取得
Zend_View
導入
コントローラスクリプト
ビュースクリプト
ビューヘルパー
Zend_View_Abstract
Zend_Wildfire
Zend_Wildfire
Zend_XmlRpc
導入
Zend_XmlRpc_Client
Zend_XmlRpc_Server
ZendX_Console_Process_Unix
ZendX_Console_Process_Unix
ZendX_JQuery
Introduction
ZendX_JQuery View Helpers
ZendX_JQuery Form Elements and Decorators
Zend Framework のシステム要件
導入
Zend Framework 移行上の注意
Zend Framework 1.10
Zend Framework 1.9
Zend Framework 1.8
Zend Framework 1.7
Zend Framework 1.6
Zend Framework 1.5
Zend Framework 1.0
Zend Framework 0.9
Zend Framework 0.8
Zend Framework 0.6
Zend Framework PHP 標準コーディング規約
概要
PHP ファイルの書式
命名規約
コーディングスタイル
Zend Framework Documentation Standard
Overview
Documentation File Formatting
Recommendations
Zend Framework MVC アプリケーションのために推奨されるプロジェクト構造
概要
推奨されるプロジェクト・ディレクトリ構造
モジュール構造
リライト設定ガイド
Zend Framework パフォーマンスガイド
導入
クラスの読み込み
Zend_Dbパフォーマンス
国際化(i18n)とローカライズ(l10n)
ビューのレンダリング
著作権に関する情報