ダイナミッククエリ

~ Dynamic queries 和訳 ~

ダイナミッククエリとは、静的なクエリー文字列ではなく、Drupalによって動的に構築されるクエリーのことです。すべてのインサート、アップデート、デリートそしてマージのクエリは動的に生成されます。セレクトクエリーは、静的と動的、どちらでも可能ですが、これも一般的に「ダイナミッククエリ」といえば、動的に生成されたセレクトクエリのことを指します。

すべての動的クエリは、適切な接続オブジェクトによってリクエストされたクエリーオブジェクトによって生成されます。静的クエリの場合と同様に、大部分のケースでは、オブジェクトのリクエストにラッパーが使われます。Subsequent directives to the query, however, take the form of methods invoked on the query object.

目次

  1. The Big Picture 
  2. Joins
  3. Fields
  4. Distinct 
  5. Expressions 
  6. Ordering
  7. Random Ordering
  8. Grouping
  9. Ranges and Limits
  10. Table Sorting
  11. Conditionals
  12. Executing The Query
  13. Count Queries
  14. Debugging

動的セレクトクエリは、以下に示すように、db_select()から始まります。:

<?php
$query = db_select('node', 'n', $options);
?>

このケースでは、「node」がこのクエリーのベーステーブルとなります。FROM句の最初のテーブルです。テーブルはブラケット{}で囲わないことに注意しましょう。それはクエリビルダーが行います。2つめのパラメータはテーブルのエイリアスです。指定しない場合はテーブル名がそのまま使われます。$options配列は省略可能です。静的クエリの$options配列と同じです。ダイナミックセレクトクエリは非常にシンプルにも、非常に複雑にもなり得ます。全てをカバーしようとすると、それで1冊の本になってしまうので、ここでは基本的な原則について触れたいと思います。

The Big Picture

ユーザーテーブルの比較的単純なクエリを見てみます。以下に、このクエリを構成する個々のパーツと、JOINのような高度なテクニックを示します。

<?php
$query = db_select('users', 'u');

$query
  ->condition('u.uid', 0, '<>')
  ->fields('u', array('uid', 'name', 'status', 'created', 'access'))
  ->range(0, 50);

$result = $query->execute();
?>

これは次のようなクエリに相当します。

$result = db_query("SELECT uid, name, status, created, access FROM {users} u WHERE uid <> 0 LIMIT 50 OFFSET 0");

これはuser administration pageで使用されているクエリを簡素化したものです。詳しくはリンク先をご覧ください。

Joins

他のテーブルにJOINするには、join(), innerJoin(), leftJoin(), or rightJoin() といったメソッドを使います。

<?php
$table_alias = $query->join('user', 'u', 'n.uid = u.uid AND u.uid = :uid', array(':uid' => 5));
?>

上記ディレクティブは、「u」のエイリアスを持つ「ユーザー」テーブルに対してINNER JOIN(デフォルトのタイプです)を追加します。The join will be ON the condition " n.uid = u.uid AND u.uid = :uid", where :uid has a value of 5. Note the use of a prepared statement fragment. それは、安全な方法でJOIN文の変数を追加できます。クエリのフラグメントに、リテラルな値や変数を直接置くべきではありません。これは静的クエリの場合と同じです(SQLインジェクションの脆弱性に繋がります)。innerJoin()、leftJoin()、そしてrightJoin() といったメソッドもそれぞれ同様に動作します。

joinメソッドの返り値はテーブルのエイリアスです。通常はエイリアスが指定されていればそれが使われます。そのエイリアスが別のテーブルに使用されている場合は、システムによって別のエイリアスが使われます。

すべてのjoinメソッドでは、'user'のようなリテラルなテーブル名の代わりに、第1引数にセレクトクエリを使用することが出来ます。例:

<?php
$myselect = db_select('mytable')
  ->fields('mytable')
  ->condition('myfield', 'myvalue');
$alias = $query->join($myselect, 'myalias', 'n.nid = myalias.nid');
?>

Fields

addField()メソッドによってセレクトクエリにフィールドを追加します。:

<?php
$title_field = $query->addField('n', 'title', 'my_title');
?>

上のコードはクエリの'n'のエイリアスのテーブルから'title'をセレクトし、それに'my_title'というエイリアスを与えます。エイリアスの指定が無い場合は自動的に生成されます。自動的に生成されるエイリアスは通常は単純にフィールド名です。この例では'title'です。そのエイリアスが既に存在する場合は、エイリアスはテーブル名とフィールド名となります。この例では"n_title"です。それも既に存在する場合は、カウンターが追加され、"n_title_2"のようになります。

もしあなたがエイリアスを指定しないクエリを作成、生成し、デフォルトのエイリアスが返ってこないという場合は、まずあなたのコードにバグが存在する可能性が高いです。もしあなたがhook_query_alter() の実装部分を書いている場合、エイリアスが既に使用されているかを確実に知ることは出来ないため、常に生成されたエイリアスを使用する必要があります。

複数のフィールドをセレクトするには、単純にaddField()を複数回呼び出します。通常はフィールドの順番は重要ではありません。そうでなければ、モジュールのビジネスロジックに欠陥がある可能性があります。

もしくは、fields()メソッドを使用する方法もあります。

<?php
$query->fields('n', array('nid', 'title', 'created', 'uid'));
?>

上の例はaddField()を4回呼び出したことに相当します。しかしfields()は特定のフィールドのエイリアスをサポートせず、クエリオブジェクトそのものを返します。これによりメソッドを連鎖的に使えるようになります。エイリアスが必要な場合は、addField()もしくはgetFields()を使用してください。

fields()でフィールドの引数を省略した場合は、ワイルドカード "SELECT *" になります。

<?php
$query->fields('n');
?>

この場合は結果クエリのフィールド部分は"n.*"のようになります。エイリアスは生成されません。SELECT * を使用したテーブルに、他のテーブルが直接指定したフィールドが含まれる場合、フィールド名の衝突が起こり得ます。この場合、結果セットはフィールド共通の名前をひとつ含みます。このような理由により、SELECT * の使用は推奨されません。

Distinct

クエリが重複した結果を返す場合があります。この場合は、静的クエリなら"DISTINCT"を使って重複を解消することが出来ます。ダイナミッククエリではdistinct()メソッドが使用できます。

<?php
// Force filtering of duplicate records in the result set.
$query->distinct()
?>

DISTINCTはパフォーマンスの低下をもたらすことがあります。他に方法が無い場合意外は使用は控えるべきです。

Expressions

セレクトクエリビルダーはフィールドリストのexpressionsの使用をサポートします。例えば"twice the age field"、"a count of all name fields"、それとタイトルフィールドのsubstringです。多くのexpressionsはSQL functionsを使用しますが、すべてのデータベースにおいて標準化されてはいません。expressionsのデータベース間の互換性の確認は、モジュール開発者に委ねられます。(このリストを参照のこと。http://drupal.org/node/773090

expressionをクエリに追加するには、addExpression()メソッドを使います。

<?php
$count_alias = $query->addExpression('COUNT(uid)', 'uid_count');
$count_alias = $query->addExpression('created - :offset', 'uid_count', array(':offset' => 3600));
?>

上の例の1番目はクエリに対して"COUNT(uid) AS uid_count"を追加します。2番目のパラメータはフィールドのエイリアスです。まれにエイリアスが既に使われている場合は、別のエイリアスが生成されます。返り値はそのエイリアスです。エイリアスの指定が無い場合は、デフォルトとして"expression" (もしくは expression_2, expression_3, etc.) が生成されます。

オプションの3つ目のパラメータは、expressionのプレースホルダの値を表す連想配列です。

いくつかのexpressionsは、Group By句が無いと機能しないことがあります。モジュール開発者は生成されたクエリが実際に有効かどうか、確認する必要があります。

Ordering

クエリにORDER BY 句を追加する場合は、orderBy()メソッドを使います。

<?php
$query->orderBy('title', 'DESC');
?>

上のコードは、クエリにタイトルの降順でソートするように指示しています。2番めのパラメータは"ASC"もしくは"DESC"で、それぞれ昇順、降順を表します。デフォルトは"ASC"(昇順)です。ここで使うフィールド名はaddField()か、addExpression()で生成されたものでなければなりません。多くの場合、正しいエイリアスが使われているかを確認するために、これらのメソッドの返り値を使用したいでしょう。複数のフィールドでソートしたい場合は、単純にorderBy()メソッドを複数回呼び出します。

Random ordering

Random orderingはデータベースによって構文が多少異なります。したがってダイナミッククエリによって処理を行うのがベストです。

クエリをランダムに処理するには、orderRandom()を使います。

<?php
$query->orderRandom();
?>

orderRandom() は orderBy() とともに、メソッドチェーンが可能です。以下のような使い方が出来ます。

<?php
$query->orderBy('term')->orderRandom()->execute();
?>

上の例は最初に"term"でソートし、同じターム内のレコードをさらにランダムにソートしたものです。

Grouping

フィールドをグループ化するには、groupBy() を使います。

<?php
$query->groupBy('uid');
?>

上の例はクエリにuidフィールドでグループ化するよう指示しています。ここで使うフィールド名はaddField()か、addExpression()で生成されたものでなければなりません。多くの場合、正しいエイリアスが使われているかを確認するために、これらのメソッドの返り値を使用したいでしょう。複数のフィールドでグループ化したい場合は、単純にgroupBy()メソッドを複数回呼び出します。

Ranges and Limits

クエリを特定の範囲に限定したい場合があります。これは一般に"range query"として知られていて、MySQLでは、これはリミット句で実装されています。ダイナミッククエリではrange()メソッドを使います。

<?php
$query->range(5, 10);
?>

上記はレコードの5番目から、10レコード返すようなクエリを生成します。「最初のnレコード」を取得するには、メソッドの最初の引数に0、2番目の引数にnを渡します。

メソッドを複数回呼び出すと、値は新しいものにオーバーライトされます。引数なしで呼び出すと、設定されていた範囲がある場合は外されます。

Table sorting

To produce a result table which can be sorted by any column, use the エクステンダ and then add the table header. Note that an extender does return a new query object that you need to use from that point on.

<?php
$query = $query
  ->extend('TableSort')
  ->orderByHeader($header);
?>

Conditionals

コンディショナル(条件文)は少し複雑です。セレクト、アップデート、デリート文で使われます。詳しい説明は条件句(conditional clause)を参照してください。アップデート、デリート文とは異なり、セレクト文は条件定義に2つのタイプがあります。WHERE句とHAVING句です。HAVING句は、condition()の代わりにhavingCondition()、where()のかわりにhaving()を使うこと意外は、WHERE句と同じです。

Executing the query

クエリを構築したら、コンパイル、実行にはexcute()メソッドを使います。

<?php
$result = $query->execute();
?>

excute()メソッドは、db_query()の返り値と同じ結果セット/ステートメントを返します。

<?php
$result = $query->execute();
foreach ($result as $record) {
  // Do something with each $record
}
?>

マルチカラムの動的クエリで、以下のメソッドを使用する場合は注意が必要です。

これらのメソッドは現在、テーブルエイリアスではなく、numeric column indicies (0, 1, 2, etc.)を必要とします。しかし、クエリビルダーは現在、返されるフィールドの特定の順序を保証しません(期待通りの順番ではないかもしれません)。特にexpressionsは、たとえ最初に追加するようにしたとしても、常にフィールドの後に追加されます。(これは静的クエリには無い問題です。静的クエリの場合は、データカラムは常に期待した順番で帰されます。)

Count queries

あらゆるクエリはカウントクエリに対応しています。カウントクエリはそのクエリのレコード数を返します。カウントクエリを取得するにはcountQuery()メソッドを使います。

<?php
$count_query = $query->countQuery();
?>

$count_queryは新しい動的クエリで順序の制限は無く、元のクエリのレコード数のみを返します。PHPはメソッドチェーンをサポートしているため、次のような書き方が一般的なアプローチになります。

<?php
$num_rows = $query->countQuery()->execute()->fetchField();
?>

Debugging

クエリ構築の一連の処理の中の、特定のポイントのクエリを調べるには、__toString()メソッドを使います。

<?php
  print_r($query->__toString());
?>
コア: 
Drupal7