Last updated June 30, 2011. Created by Xano on September 18, 2008.
Edited by xjm, kiamlaluno, tutumlum, nicholasThompson. Log in to edit this page.
Dynamic queries refer to queries that are built dynamically by Drupal rather than provided as an explicit query string. All Insert, Update, Delete, and Merge queries must be dynamic. Select queries may be either static or dynamic. Therefore, "dynamic query" generally refers to a dynamic Select query.
All dynamically built queries are constructed using a query object, requested from the appropriate connection object. As with static queries, in the vast majority of cases the procedural wrapper may be used to request the object. Subsequent directives to the query, however, take the form of methods invoked on the query object.
Table Of Contents
- The Big Picture
- Joins
- Fields
- Distinct
- Expressions
- Ordering
- Random Ordering
- Grouping
- Ranges and Limits
- Table Sorting
- Conditionals
- Executing The Query
- Count Queries
- Debugging
Dynamic select queries are started using the db_select() function as follows:
<?php
$query = db_select('node', 'n', $options);
?>In this case, "node" is the base table for the query; that is, the first table after the FROM statement. Note that it should not have brackets around it. The query builder will handle that automatically. The second parameter is the alias for the table. If not specified, the name of the table is used. The $options array is optional, and is identical to the $options array for static queries.
Dynamic select queries can be very simple or very complex. We will cover the basic principles of how they work here, but an exhaustive treatment would be a book unto itself.
The Big Picture
Here is a relatively simple query of the users table. Below we'll look at the individual parts that make up this query, and more advanced techniques like joins.
<?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();
?>The above is roughly equivalent to$result = db_query("SELECT uid, name, status, created, access FROM {users} u WHERE uid <> 0 LIMIT 50 OFFSET 0");
It is a simplified form of the query used by the user administration page, which can be referenced for further study.
Joins
To join against another table, use the join(), innerJoin(), leftJoin(), or rightJoin() methods, like so:
<?php
$table_alias = $query->join('user', 'u', 'n.uid = u.uid AND u.uid = :uid', array(':uid' => 5));
?>The above directive will add an INNER JOIN (the default join type) against the "user" table, which will get an alias of "u". 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. That allows for the addition of variable join statements in a secure fashion. Never put a literal value or variable directly into a query fragment, just as literals and variables should never be placed into a static query directly (they can lead to SQL injection vulnerabilities). The innerJoin(), leftJoin(), and rightJoin() methods operate identically for their respective join types.
The return value of a join method is the alias of the table that was assigned. If an alias is specified it will be used except in the rare case that alias is already in use by a different table. In that case, the system will assign a different alias.
Note that in place of a literal such as 'user' for the table name, all of the join methods will accept a select query as their first argument. Example:
<?php
$myselect = db_select('mytable')
->fields('mytable')
->condition('myfield', 'myvalue');
$alias = $query->join($myselect, 'myalias', 'n.nid = myalias.nid');
?>Fields
To add a field to the Select query, use the addField() method:
<?php
$title_field = $query->addField('n', 'title', 'my_title');
?>The above code will instruct the query to select the "title" field of the table with alias "n", and give it an alias of "my_title". If no alias is specified, one will be generated automatically. In the vast majority of cases the generated alias will simply be the field name. In this example, that would be "title". If that alias already exists, the alias will be the table name and field name. In this example, that would be "n_title". If that alias already exists, a counter will be added to the alias until an unused alias is found, such as "n_title_2".
Note that if you are creating and populating the query yourself and do not specify an alias and the default alias is not available, there is almost certainly a bug in your code. If you are writing a hook_query_alter() implementation, however, you cannot know with certainty what aliases are already in use so you should always use the generated alias.
To select multiple fields, simply call addField() multiple times in the order desired. Note that in most cases the order of fields should not matter, and if it does then there is likely a flaw in the business logic of the module.
As an alternate shorthand, you can use the fields() method to add multiple fields at once.
<?php
$query->fields('n', array('nid', 'title', 'created', 'uid'));
?>The above method is equivalent to calling addField() four times, once for each field. However, fields() does not support specifying an alias for a field. It also returns the query object itself so that the method may be chained rather than returning any generated aliases. If you need to know the generated alias, either use addField() or use getFields() to access the raw internal fields structure.
Calling fields() with no field list will result in a "SELECT *" query.
<?php
$query->fields('n');
?>That will result in "n.*" being included in the field list of the query. Note that no aliases will be generated. If a table using SELECT * contains a field that is also specified directly from another table, it is possible for a field name collision to occur in the result set. In that case, the result set will only contain one of the fields with the common name. For that reason the SELECT * usage is discouraged.
Distinct
Some SQL queries may produce duplicate results. In such cases, duplicate rows may be filtered out using the "DISTINCT" keyword in a static query. In a dynamic query, use the distinct() method.
<?php
// Force filtering of duplicate records in the result set.
$query->distinct()
?>Note that DISTINCT can introduce a performance penalty, so do not use it unless there is no other way to restrict the result set to avoid duplicates.
Expressions
The Select query builder supports the use of expressions in the field list. Examples of expressions include "twice the age field", "a count of all name fields", and a substring of the title field. Be aware that many expressions may use SQL functions, and not all SQL functions are standardized across all databases. It is up to the module developer to ensure that only cross-database compatible expressions are used. (Refer to this list: http://drupal.org/node/773090)
To add an expression to a query, use the addExpression() method.
<?php
$count_alias = $query->addExpression('COUNT(uid)', 'uid_count');
$count_alias = $query->addExpression('created - :offset', 'uid_count', array(':offset' => 3600));
?>The first line above will add "COUNT(uid) AS uid_count" to the query. The second parameter is the alias for the field. In the rare case that alias is already in use, a new one will be generated and the return value of addExpression() will be the alias used. If no alias is specified, a default of "expression" (or expression_2, expression_3, etc.) will be generated.
The optional third parameter is an associative array of placeholder values to use as part of the expression.
Note that some expressions may not function unless accompanied by a Group By clause. It is up to the developer to ensure that the query that is generated is in fact valid.
Ordering
To add an order by clause to a dynamic query, use the orderBy() method:
<?php
$query->orderBy('title', 'DESC');
?>The above code will instruct the query to sort by the title field in descending order. The second parameter may be either "ASC" or "DESC" for ascending or descending, respectively, and defaults to "ASC". Note that the field name here should be the alias created by the addField() or addExpression() methods, so in most cases you will want to use the return value from those methods here to ensure the correct alias is used. To order by multiple fields, simply call orderBy() multiple times in the order desired.
Random ordering
Random ordering of queries requires slightly different syntax on different databases. Therefore, that is best handled by a dynamic query.
To indicate that a given query should order randomly, call the orderRandom() method on it.
<?php
$query->orderRandom();
?>Note that orderRandom() is chainable, and stackable with orderBy(). That is, it is safe to do something like the following:
<?php
$query->orderBy('term')->orderRandom()->execute();
?>The above would order first by the "term" field of the query and then, for records that have the same term, order randomly.
Grouping
To group by a given field, use the groupBy() method.
<?php
$query->groupBy('uid');
?>The above code will instruct the query to group by the uid field. Note that the field name here should be the alias created by the addField() or addExpression() methods, so in most cases you will want to use the return value from those methods here to ensure the correct alias is used. To group by multiple fields, simply call groupBy() multiple times in the order desired.
Ranges and Limits
Queries may also be restricted to a certain subset of the records found. In general this is known as a "range query". In MySQL, this is implemented using the LIMIT clause. To limit the range of a query, use the range() method:
<?php
$query->range(5, 10);
?>The above code will instruct the result set to start at the 5th record found rather than the first, and to return only 10 records. In most cases one will want "the first n records". To do that, pass 0 as the first argument and n as the second.
Calling the range() method a second time will overwrite previous values. Calling it with no parameters will remove all range restrictions on the query.
Table sorting
To produce a result table which can be sorted by any column, use the TableSort extender 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
Conditionals are a complex subject and are shared by Select, Update, and Delete queries. They are therefore explained separately. Unlike Update and Delete queries, however, Select queries have two types of conditionals: The WHERE clause and the HAVING clause. The Having clause behaves identically to the WHERE clause, except that it uses methods havingCondition() and having() instead of condition() and where().
Executing the query
Once the query is built, call the execute() method to compile and run the query.
<?php
$result = $query->execute();
?>The execute() method will return a result set / statement object that is identical to that returned by db_query(), and it may be iterated or fetched in the exact same way:
<?php
$result = $query->execute();
foreach ($result as $record) {
// Do something with each $record
}
?>Note: Be careful when using the following methods with a multi-column, dynamic query:
These methods currently require numeric column indicies (0, 1, 2, etc.) rather than table aliases. However, the query builder does not currently guarantee any specific order for the returned fields, so the data columns may not be in the order that you expect. In particular, expressions are always added after fields, even if you add them to your query first. (This issue does not apply to static queries, which always return the data columns in the order you specify.)
Count queries
Any query may have a corresponding "count query". The count query returns the number of rows in the original query. To obtain a count query, use the countQuery() method.
<?php
$count_query = $query->countQuery();
?>$count_query is now a new Dynamic Select query with no ordering restrictions that when executed will return a result set with only one value, the number of records that would be matched by the original query. Because PHP supports chaining methods on returned objects, the following idiom is a common approach:
<?php
$num_rows = $query->countQuery()->execute()->fetchField();
?>Debugging
To examine the SQL query that the query object will build at a particular point in its lifecycle, call its __toString() method:
<?php
print_r($query->__toString());
?>
Comments
Legal Values for $query->condition comparators.
String comparitors and others can be found at: http://drupal.org/node/310086 and http://drupal.org/node/773090
UNION statements
Even though UNION isn't listed here, it is supported. For documentation see the SelectQueryInterface::union() and SelectQueryInterface::getUnion() methods.
Basically, you just do
$query->union($otherQuery, 'UNION ALL');. The return value is just$query, no new object is created.adding the range after an
adding the range after an addfield gives an error
this works
$query = db_select('users', 'u');
$query
->fields('u', array('uid', 'name', 'mail'))
->range(0, 3);
$result = $query->execute();
this works too using fields and field alias
$query = db_select('users', 'u');
$query
->fields('u', array('uid', 'name', 'mail'))
->addField('u', 'pass', 'userpass');
$result = $query->execute();
but gives the error 'Call to a member function range() on a non-object' when the range is added
$query = db_select('users', 'u');
$query
->fields('u', array('uid', 'name', 'mail'))
->addField('u', 'pass', 'userpass')
->range(0, 3);
$result = $query->execute();
not sure why yet.
addField() does not return
addField() does not return the query object, but the field alias. See http://api.drupal.org/api/drupal/includes--database--select.inc/function....
My name is Bart - web & events
Reply : adding the range after an (Chaining problem)
@ndmaque
addField() is not the chain-able function that's why it do not return $query object and we can't add more function after if but we can add more before it.
please refer below link for more clarification :
Chaining
Regards
Rajat Gusain
Leftjoin and pager issue
Strangely pager extender fails to work when doing some database joins.
Nothing happens I just get a non paginated list from the database.
Solved: the pager statements have to come among the firsts in the definition of the query.
Reply : Leftjoin and pager issue
@pcoloma
Please refer given below link to create pager :
Pager for drupal 7
Regards
Rajat Gusain
Clear Example For db_select JOIN and Paging
i hope this clear code example is helpful for others,
mixing db_select() join and paging together:
<?php
$query = db_select(TABLE_DATA_SIDANG, 'ds')
->condition('tanggal', $tanggal . " 00:00:00")
->extend('PagerDefault')
->limit(10)
->extend('TableSort')
->orderByHeader($header)
->fields ('ds', array (
'no_perk',
'tingkat',
'tanggal',
'ruang',
'tim',
'no_majelis',
));
$query->leftJoin(TABLE_DATA_REG_AC, 'ra', 'ra.no_perk = ds.no_perk');
$query->fields ('ra', array (
'nama_pe',
'nama_ter',
));
$res = $query->execute();
?>
--
Hannan Gigih Prastawa
http://www.sapua.com
http://www.edufren.com
http://hannanxp.net
http://hannanxp.wordpress.com
join() and addJoin()
An important note, that you can use join() only at once so if you need fetch data from a several tables, use addJoin() instead.
For example:
$query = db_select('node', 'n')
->condition('n.type', 'dialysis_center')
->leftJoin('content_field_phone', 'phone', 'n.nid = phone.nid')
->leftJoin('content_field_website', 'website', 'n.nid = phone.nid')
->leftJoin('location_instance', 'lin', 'n.nid = lin.nid')
->leftJoin('location', 'ln', 'lin.lid = lm.lid')
->leftJoin('location_phone', 'lphone', 'lin.lid = lphone.lid')
->fields('n', array('nid', 'title', 'status', 'sticky'))
->fields('lphone', array('phone'))
->fields('website', array('field_website_url'))
->fields('ln', array('name', 'street', 'additional', 'city',
'province', 'postal_code', 'country', 'latitude', 'longitude'));
It might not be the best
It might not be the best code, but if I found this a couple of hours ago, I would have saved myself a lot of time. This is how you get usernames to go with the uids you might get on a query:
<?php$query = db_select('node', 'n'); //Select the node table and give it the alias of n
$query->join('users', 'u', 'u.uid = n.uid'); //join users, give it alias u, where u.uid = n.uid
$query
->fields('n', array('nid', 'title', 'created', 'uid')) //get these fields from the node table
->fields('u', array('name')) //get these fields from the users table
->condition('n.status', 1); //only published nodes, the n in n.status is needed
return $query->execute();
?>
Thanks to Willie for getting me on the right track.