Community Documentation

Conditional clauses

Last updated February 27, 2013. Created by solotandem on September 18, 2008.
Edited by sulav, Elijah Lynn, drupalshrek, xjm. Log in to edit this page.

A "conditional clause" is a portion of a query that restricts the rows matched by certain conditions. In SQL, that means the WHERE or HAVING portions of a SELECT, UPDATE, or DELETE query. In all dynamic queries in Drupal those are implemented using the same mechanism. Except where noted, everything that follows applies to all three of those query types.

Concepts

Conditional fragment
A conditional fragment is a self-contained portion of a conditional clause.
Conjunction
Every conditional clause consists of one or more conditional fragments joined by a conjunction. A conjunction is a term such as AND or OR that joins the two statements together.
Conditional object
Drupal represents each conditional fragment as an instance of class QueryConditional. A conditional object is an instance of that class.

As an example, the following query breaks down this way:

Query:
SELECT FROM {mytable} WHERE (a = 1 AND b = 'foo' OR (c = 'bar'))
Conditional clause:
WHERE (a = 1 AND b = 'foo' OR (c = 'bar'))
Conditional fragments:
(a = 1 AND b = 'foo' OR (c = 'bar'))
(c = 'bar')
Conjunctions:
AND, OR

The Select, Update, and Delete query objects implement the QueryConditionalInterface, which provides them all with the same interface. Internally they wrap a QueryConditional object. The QueryConditional class can also be instantiated directly.

Each conditional fragment in a conditional statement is joined by a conjunction. A conditional object consists of one or more conditional fragments that will all be joined by a specified conjunction. By default, that conjunction is AND. Each conditional fragment may optionally be a conditional object with a different conjunction, allowing for a conditional statement to be built up by nesting conditional fragments inside each other. In that way, arbitrarily complex conditional statements may be built.

API

There are two main methods that apply for all conditional objects:

$query->condition($field, $value = NULL, $operator = '=')
The condition() method allows for adding a standard $field $value $operator format of conditional fragment. That includes any case where the condition is a binary comparison such as =, <, >=, LIKE, etc. If no operator is specified, = is assumed. That means the most common case would be condition('myfield', $value), which results in a conditional fragment of myfield = :value, where :value will be replaced with $value when the query is run.
$query->where($snippet, $args = array())
The where() method allows for the addition of arbitrary SQL as a conditional fragment. $snippet may contain any legal SQL fragment, and if it has variable content it must be added using a named placeholder. The $args array is an array of placeholders and values that will be substituted into the snippet. It is up to the developer to ensure that the snippet is valid SQL. No database-specific modifications are made to the snippet.

The condition() method is preferred in most cases, unless the $field $value $operator format is not appropriate, such as happens when you have more complex things like expressions, or a condition on two fields instead of a field and a value. Both methods return the corresponding conditional object, so they may be chained indefinitely.

condition() also handles several other special cases.

Unlike Update and Delete queries, 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().

Array operators

Some operators are intended to work on an array for the value parameter. The most common of these are IN and BETWEEN. If the operator is IN, then the $value is assumed to be an array of values that the field may equal. Thus, the following call will evaluate this way:

<?php
$query
->condition('myfield', array(1, 2, 3), 'IN');
// Becomes: myfield IN (:db_placeholder_1, :db_placeholder_2, :db_placeholder_3)
?>

If the operator is BETWEEN, then $value is assumed to be a 2-element array of the values the field must be between. For example:

<?php
$query
->condition('myfield', array(5, 10), 'BETWEEN');
// Becomes: myfield BETWEEN :db_placeholder_1 AND :db_placeholder_2
?>

Nested conditionals

The first parameter of condition() can also accept another conditional object. That inner conditional object will be incorporated into the outer conditional, surrounded by parentheses. The inner object may also use a different conjunction than the outer object. That way, one can build complex nested conditional structures by creating and building up conditional objects "bottom up".

The db_condition() helper function will return a new conditional object. It takes a single parameter that is the conjunction that object will use. In general, the helper methods db_and(), db_or(), and db_xor() will cover almost any expected case. That allows conditionals to be inserted inline in a query for a very compact syntax.

For example, consider the following construct:

<?php
$query
 
->condition('field1', array(1, 2), 'IN')
  ->
condition(db_or()->condition('field2', 5)->condition('field3', 6))
// Results in:
// (field1 IN (:db_placeholder_1, :db_placeholder_2) AND (field2 = :db_placeholder3 OR field3 = :db_placeholder_4))
?>

Null values

To filter a database field on whether the value is or is not NULL, use the following methods:

<?php
$query
->isNull('myfield');
// Results in (myfield IS NULL)

$query->isNotNull('myfield');
// Results in (myfield IS NOT NULL)
?>

Both methods may be chained and combined with condition() and where() as desired.

Note: Although it is possible in Drupal 7 to check for NULL values using condition('field', NULL), that usage is deprecated and should not be used. Use the methods above instead. See #813540: Comparisons involving NULL must never return true for more information.

Subselects

condition() also supports subselects as the $value. To use a subselect, first construct a SelectQuery object created by db_select(). Then, instead of executing the Select query pass it into the value parameter of the condition() method of another query. It will automatically get integrated into the main query when it is executed.

Subselects are generally useful only in two cases: Where the subselect results in only a single row and value returned and the operator is =, <, >, <=, or >=; or when the subselect returns a single column of information and the operator is IN. Most other combination would result in a syntax error.

Note: Currently it is only possible to use subselect conditions with the IN operator because with the other operators the sub-query is not wrapped in parentheses and so results in a syntax error. See #1267508: Subselects don't work in DBTNG conditions, except when used as value for IN.

Note that on some databases, particularly MySQL, subselects in a conditional clause are not particularly fast. If possible, use joins, subselects in the FROM clause, or multiple flat conditional fragments instead of a subselect.

Examples

The following examples should hopefully make the use of conditionals clearer. For clarity, the equivalent query string is shown even though in practice placeholders and prepared statements would be used instead.

<?php
db_delete
('sessions')
  ->
condition('timestamp', REQUEST_TIME - $lifetime, '<')
  ->
execute();
// DELETE FROM {sessions} WHERE (timestamp < 1228713473)
?>

<?php
db_update
('sessions')
  ->
fields(array(
   
'sid' => session_id()
  ))
  ->
condition('sid', $old_session_id)
  ->
execute();
// UPDATE {sessions} SET sid = 'abcde' WHERE (sid = 'fghij');
?>

<?php
// From taxonomy_term_save():
$or = db_or()->condition('tid1', 5)->condition('tid2', 6);
db_delete('term_relation')->condition($or)->execute();
// DELETE FROM {term_relation} WHERE ((tid1 = 5 OR tid2 = 6))
?>

Comments

Wildcards for LIKE conditions

To do LIKE conditions using wildcards you would do something like the following:

<?php
$query
->condition('my_field', '%superstring%', 'LIKE');
?>

Is the equivalent of searching for
*superstring*
and will find results like
mysuperstringrocks

You may also use the underscore as a wildcard for a single character.

<?php
$query
->condition('my_field', 'super_tring', 'LIKE');
?>

will find results like
superstring
But not
superduperstring

To use a LIKE condition that uses the "real" percent and underscore characters you need to escape them.

<?php
$query
->condition('my_field', '\_foo\%bar\_', 'LIKE');
?>

will find results like
_foo%bar_
But not
_foo_bar_

________________________
dave hansen-lange
Technical Lead
Advomatic.com
Great White North office
Canada

Use db_like() for escaping

You may use db_like() instead of manually escaping the value:

<?php
$query
->condition('my_field', db_like('_foo%bar_'), 'LIKE');
?>

NOT IN also supported

Just for completeness, NOT IN is also supported.

Using the <> operator will result in an error:

<?php
$query
->condition('myfield', array(1, 2, 3), '<>');
// myfield <>
?>

Instead, use the NOT IN operator:

<?php
$query
->condition('myfield', array(1, 2, 3), 'NOT IN');
// myfield NOT IN (:db_placeholder_1, :db_placeholder_2, :db_placeholder_3)
?>

-Wes

Deletes with subquery and not in

How would I go about doing something like:
delete from {sessions} s where s.uid not in (select ur.uid from (users_roles) ur where ur.rid not in (3,4))

Correct

There are two main methods that apply for all conditional objects:

$query->condition($field, $value = NULL, $operator = '=')

I think it must be replaced by:

$query->condition($field, $value = NULL, $operator = NULL)

Please, see api condition

NOT conditions

Is there a way to add condition like "NOT isLogo>0" using condition() method of SelectQuery?

There are equivalent

There are equivalent operators for negative comparisons. If you're doing >, you can switch to < for the opposite. The example you gave above therefore becomes "isLogo <= 0" If you're doing equation, '!=' or '<>' should do the trick.

Dave Ingram - Gainesville, FL
www.ingraminnovation.com

Support for LEFT(), RIGHT(), SUBSTR()

I'm digging through the D7 DB API documentation as well as the dbtng module documentation, and I'm not seeing any support mentioned for portable LEFT(), RIGHT(), SUBSTR() implementations. These are parts of standard SQL.

I do see we can use:

<?php
$query
->where($snippet, $args = array())
?>

Is the assumption that we should be falling back from the API to regular SQL query language here in order to be able to use these features?

If so, here's how I ended up working through this. The goal was to only use one LIKE clause in my query, since LIKE is slow. One of the options was to match at the left on the input string (a typeahead query). Of course you can use LIKE% here, but since LIKE doesn't use an index, it's slow. Faster is LEFT(column, $length_of_string) = $string.

So this is how the query ended up looking. I wanted to see if the db_or() object would let me mix a condition() with where() and it did (in case any of you were wondering).

<?php
$string 
= What the user is typing into the typeahead....

$query = db_select('node', 'n')
  ->
fields('n', array('nid', 'title', 'status'))
  ->
condition('n.type', 'video')
  ->
condition('n.status', 1)
  ->
condition(db_or()->condition('n.title', "%$string%", 'LIKE')->where('LEFT(n.nid, :len) = :str', array(':len' => strlen($string), ':str' => $string)))
  ->
range(0, 10);
  echo
$query->__toString() . '\n';
 
$matches = $query->execute();
?>

The condition creates an or clause with a normal middle of string LIKE (for title), or left() of string match (for match by nid). Left() is faster than LIKE since it can use the index on the nid column.

I didn't see any documentation on how to create the necessary placeholders in the ->where() condition, so I experimented and finally ended up using the colon prefix for the placeholders rather than %s or !placeholder, etc etc.:

<?php
->where('LEFT(n.nid, :len) = :str', array(':len' => strlen($string), ':str' => $string)
?>

The SQL query generated by this looks like this:

<?php

echo $query->_toString() . "\n";
?>

SELECT n.nid AS nid, n.title AS title, n.status AS status
FROM
{node} n
WHERE  (n.type = :db_condition_placeholder_0) AND (n.status = :db_condition_placeholder_1) AND( (n.title LIKE :db_condition_placeholder_2 ESCAPE '\\') OR (LEFT(n.nid, :len) = :str) )
LIMIT 10 OFFSET 0

And the list of arguments (my $string in this example is "24"):

<?php
var_dump
($query->getArguments());
?>

array(5) {
  [":db_condition_placeholder_0"]=>
  string(5) "video"
  [":db_condition_placeholder_1"]=>
  int(1)
  [":db_condition_placeholder_2"]=>
  string(4) "%24%"
  [":len"]=>
  int(2)
  [":str"]=>
  string(2) "24"
}

Which means the final result sent to the database server is this:

SELECT n.nid AS nid, n.title AS title, n.status AS status
FROM
{node} n
WHERE  (n.type = 'video') AND (n.status = 1) AND ( (n.title LIKE '%24%' ESCAPE '\\') OR (LEFT(n.nid, 2) = 24) )
LIMIT 10 OFFSET 0

Which gets us exactly what we wanted.

db_and() and LIKE (multiple conditions)

<?php
$query
=db_select('node','n')
           ->
condition(db_and()->condition('n.uid','1')->condition('n.type','YOUR TYPE',LIKE))
           ->
fields('n',array('nid','type'))
           ->
execute();
?>

<?php

<?php
$query
->condition("date_begin","DATE_SUB(NOW(), INTERVAL 1 YEAR)", ">=");
?>

is not working for me because SQL function DATE_SUB is in double quotes

how can i escape '' in db_place_holder in this case because SQL function don't need double quotes

Thank you for you help

We want:
Either
under 25 AND beautiful AND posessing a character trait we like

OR
over 25 AND rich AND doesn't possess a character trait we hate

$traits_we_like = array(
   "bad at chess",
   "can open beer with tongue",
   "laughs at stupid jokes"
);
$traits_we_hate = array(
   "good at chess",
   "can open beer with empty eye socket",
   "only laughs at good jokes"
);
$query->condition(
   db_or()
      ->condition(
         db_and()
            ->condition('my_table.age', 25, '<')
            ->condition('my_table.is_beautiful', 1)
            ->condition('my_table.character_trait', $traits_we_like, 'IN')
)
      ->condition(
         db_and()
            ->condition('my_table.age', 25, '>=')
            ->condition('my_table.bottlecaps', 25000, '>=')
            ->condition('my_table.character_trait', $traits_we_hate, 'NOT IN')
      )
);

About this page

Drupal version
Drupal 7.x
Audience
Programmers
Level
Intermediate, Advanced
Drupal’s online documentation is © 2000-2013 by the individual contributors and can be used in accordance with the Creative Commons License, Attribution-ShareAlike 2.0. PHP code is distributed under the GNU General Public License. Comments on documentation pages are used to improve content and then deleted.