Drupal 7.x 数据库API

论坛: 

在Drupal中最通用的查询形式是静态查询(Static queries),静态查询是一种将原始查询代码发送到数据库所进行的查询,只有选择查询(Select)可以使用静态查询。

只有很简单的查询才应该使用静态查询机制,如果你需要更复查的查询,请使用动态查询来提供更灵活的生成机制。

静态查询在内部的语法如下:

<?php
$result = $conn->query("SELECT nid, title FROM {node}");
?>

通常直接使用内隐的包装:

<?php
$result = db_query("SELECT nid, title FROM {node}");
?>

如上调用db_query()等于下面的语句:

<?php
$result = Database::getConnection()->query("SELECT nid, title FROM {node}");
?>

db_query()有三个参数,第一个是查询字符串,需要使用适当的占位符(placeholders),并且把所有表名用大括号作为括起来。第二个参数是一组占位符构成的数组。第三个参数是可选的,一个包含一些配置指令的数组,用来指示查询如何运行。

前缀(prefix)

在静态查询中,所有的表名必须被包含在大括号{}中,这个标记可以让数据库系统在适当的时候为表名加上的前缀。前缀允许在一个数据库里同时运行多个网站,或者有些情况下,可以在多个Drupal网站之间共享使用同一个数据表。

占位符(placeholder)

占位符标记了一些可以可以被插入到查询语句中并被执行的符号,通过使用与查询语句不同的形式,允许数据库系统把SQL语法和用户提供的数据区分开来,以避免SQL注入攻击。

<?php
$result = db_query("SELECT nid, title FROM {node} WHERE created > :created", array(
  ':created' => REQUEST_TIME - 3600,
));
?>

上面的查询代码会选择所有一个小时即3600秒内创建的节点(node),当查询运行的时候,占位符:
created会自动的被替代为REQUEST_TIME –
3600的当前值。一个查询可以有任意多数量的占位符,但是必须有唯一不同的名称,即使它们的值是一样的。占位符数组可以像上面的例子那样直接在语句中构
造,也可以事先构造成数组变量再传入。数组中占位符的顺序是无关紧要的。

以“db_“开头的占位符名称由内部系统保留,所以不要为占位符设定这样的名称。

注意占位符不需要被转义,也不需要使用引号,不管它指代的类型是字符串还是数值。因为它们是分别传递给数据库服务器的,服务器能够区分传入的数据是字符串还是数值型的。

<?php
// 错误:
$result = db_query("SELECT nid, title FROM {node} WHERE type = ':type'", array(
  ':type' => 'page',
));

// 正确:
$result = db_query("SELECT nid, title FROM {node} WHERE type = :type", array(
  ':type' => 'page',
));
?>

占位符数组(placeholder arrays)

Drupal的数据层包含一些占位符的额外特性,如果传递给一个占位符的值是一个数组,那这个占位符会自动扩展并用逗号分隔开来,这意味着开发者就不需要担心如何统计需要的占位符数量这个问题了。

下面的例子可以更清楚的展示这一点:

<?php
// 下面的代码:
db_query("SELECT * FROM {node} WHERE nid IN (:nids)", array(':nids' => array(13, 42, 144));

// 会自动转成如下分隔好的语句:
db_query("SELECT * FROM {node} WHERE nid IN (:nids_1, :nids_2, :nids_3)", array(
  ':nids_1' => 13,
  ':nids_2' => 42,
  ':nids_3' => 144,
));

// 等于如下的查询语句:
db_query("SELECT * FROM {node} WHERE nid IN (13, 42, 144)");
?>

查询选项(Query Options)

db_query()(或者连接对象的query方法)的第三个参数是一个指示查询如何进行的选项数组。一般来说,大多数的查询里面只会用到两个选项,其它的值主要是提供内部使用的。

“target”键指定了查询的目标服务器,缺省值是“default”。目前唯一的可选值是“slave”,表面这个查询将优先在一个从服务器(slave server)上进行。

“fetch”键指定了应该怎么接收从查询中返回的记录。可用的值包括 PDO::FETCH_OBJ, PDO::FETCH_ASSOC,
PDO::FETCH_NUM,
PDO::FETCH_BOTH,或者一个代表了类名的字符串。如果指定的是一个字符串类名,那么每条记录都将填充到那个类的一个新对象中去。其它的值都
是通过PDO(PHP Data
Object)定义的,会将所有的记录返回成标准对象、关联数组、数值数组、或者分别用关连方式和数值方式索引的一个数组。参见:http://php.net/manual/en/pdostatement.fetch.php。缺省的值是PDO::FETCH_OBJ,除非有特别的需要,否则尽量统一使用这一方式。

下面的例子展示了从一个在从服务器上执行的查询,并且以关联数组的形式返回所取得的记录。

<?php
$result = db_query("SELECT nid, title FROM {node}", array(), array(
  'target' => 'slave',
  'fetch' => PDO::FETCH_ASSOC,
));
?>

...........................

动态查询(Dynamic
queries)指的是通过Drupal动态构建出来的查询方式,而不是通过直接提供查询字符串。所有的插入(Insert)、更新(Update)、删
除(Delete)以及合并(Merge)查询都必须是动态的,选择(Select)查询可以是静态的也可以是动态的。所以,“动态查询”一般指的就是动
态选择查询。

所有的动态查询都是通过调用适当的连接对象,使用查询对象来构建的。和静态查询一样,在绝大多数情况下,可以通过内隐的包装取得这个对象,接下来所进行的查询指令,都是通过调用这个查询对象的方法的形式来实现的。

动态选择查询通过db_select()函数开始,例子如下:

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

在这个例子中,“node”是查询的主表,也就是在FROM语句后的第一个数据表。注意这里不再需要用括号包含,查询构造器会自动的处理。第二个参数是这
个数据表的别名,如果不指定则使用完整表名。$options数组是可选的,其意义和静态查询中的$options参数相同。

动态选择查询可以非常简单也可以非常复杂,我们在这里将概要的介绍下它工作的基本原理,详细的用法恐怕得要写一本书才能尽述了。

概览

这是一个关于用户数据表的相对简单的查询,下面我们将看看构成这个查询的各个部分,并且介绍更多技术比如连接(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″);

这是在用户管理页面中使用的查询的一个简化形式,用来给下面的学习提供参考。

连接查询(Joins)

要连接到其它数据表,可以使用join()、innerJoin()、leftJoin()、或者rightJoin() 方法,如:

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

上面的语句将会加入一个INNER JOIN(缺省的连接形式)到“user”数据表后,“user”数据表会使用别名“u”。连接的条件是“n.uid
= u.uid AND u.uid =
:uid”,以及:uid的值等于5的时候。请注意占位符在这里的使用方法,这可以更安全的添加更多的连接语句。永远不要直接把值或者变量放到查询语句中
去,就像静态查询语句中不能直接放入变量和数值一样(可能会导致SQL注入攻击漏洞)。innerJoin()、leftJoin()、
rightJoin()方法分别用于添加各自对应的连接查询。

join()方法返回的值是指定连接的数据表的别名,如果显式的制定了别名则会直接使用,除非某些特殊情况下这个别名已经被其它表使用了。在这种情况下,系统会赋予表另一个别名。

注意,除了使用一个名称比如“user”作为表名之外,所有的连接方法都可以用一个选择查询作为第一个参数,比如:

<?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”。

注意,如果你自己创建和维护查询,并且没有指定别名,而缺省的别名又不可用,这几乎肯定就是你程序中的一个bug。如果你打算实现hook_query_alter()钩子的话,你不可能确切的知道那些别名已经被使用了,所以你只能一直使用生成的别名。

要选择多个字段,只需要按照需要的顺序多次调用addFiled()方法就可以了。注意大多数情况下字段的顺序是无关紧要的,但如果这样的话可能会是一个模块中业务逻辑的缺陷。

作为一个替代的快捷方式,你可以使用fields()方法同时添加多个字段。

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

上面的代码等效于调用addFields方法四次,每次添加一个字段。不过,fields()方法不支持为字段指定别名,也不会返回产生的别名,而返回的
是查询对象自身,这样可以链式的调用查询方法。如果你希望知道生成的别名,可以使用addField()方法或者getFields()方法来访问原始的
内部字段结构。

调用fields()方法而不指定任何字段将会导致一个全选择查询“SELECT *”。如:

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

将会导致一个“n.*”字段被包含在查询列表里。注意这里不会生成任何别名,如果一个表使用SELECT
*包含了一个已经从另一个表里指定的字段,那么字段名在结果集中很可能就会发生冲突,在这种情况下,结果集将只会包含一个字段的值。因此,不推荐使用
SELECT * 的用法。

唯一查询(Distinct)

有些SQL查询可能会产生重复的结果,这种情况下,可以在静态查询中使用“DISTINCT”关键词过滤掉这些重复的行。动态查询中,使用distinct()方法:

<?php
// 强制过滤掉结果集中的重复记录。
$query->distinct()
?>

注意DISCTINCT可能影响性能,如果除非没有别的方法来约束结果集避免重复结果,不要使用它。

表达式(Expressions)

选择查询构造器支持在字段列表中使用表达式,比如“年龄字段的一倍”、“所有名字字段的总数”,以及标题字段的子字符串等。显然很多的表达式会使用SQL
函数,并且不是所有的SQL函数都是跨数据库通用的,这就要求模块开发者必须保证只使用跨数据库兼容表达式。(参考这个列表:http://drupal.org/node/773090
添加表达式到查询中,使用addExpression()方法。

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

上面的第一行语句添加了一段“COUNT(uid) AS
uid_count”到查询中,第二个参数是这个字段的别名。极少情况下这个别名已经被使用了,则会自动生成新的别名并且通过
addExpression()的返回值返回。如果没有指定别名,缺省的别名将会是“expression”开头的字符串(比如
expression_2、expression_3等等)。
可选的第三个参数是一个为占位符提供值的关联数组。
注意有些表达式只有和“Group By”字句一起使用时才会工作,这就要求开发者必须确保生成的查询能够切实有效的工作。

排序(Ordering)

要在动态查询中添加排序字句,使用orderBy()方法。

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

上面的代码指定查询要按照title字段降序排序。第二个参数可以是“ASC”或者“DESC”表示升序或降序排序,缺省为“ASC”。注意这里的字段名
必须是通过addField()或者addEXpression()方法创建的字段别名,所以大多数情况下你应该使用上述方法的返回值以确保别名是正确
的。要排序多个字段,只要按照需要的顺序多次调用orderBy()方法就可以了。

随机排序(Random Ordering)

在不同的数据库中要对查询进行随机排序使用的语法有轻微的不同,因此最好通过动态查询来实现。

要让指定的查询按照随机顺序排序,只要调用orderRandom()方法就可以了。

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

注意oderRandom()方法是可以链式调用,并且可以和orderBy()结合使用。所以下面的语句是很安全的:

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

上面的语句首先对查询按照“term”字段排序,然后对于同样的term的记录,进行随机排序。

分组(Grouping)

要对某一给定字段进行分组,使用groupBy()方法。

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

上面的代码指定查询按照uid字段分组。注意这个的字段名应该是通过addField()或者addExpression()方法创建的别名,所以大多数
情况下你应该使用上述方法的返回值以确保别名是正确的。要分组多个字段,只要按照需要的顺序多次调用groupBy()方法就可以了。

范围和限制(Range and Limits)

查询可以限制到所有查询结果的一个确定子集,通常这叫做“范围查询”。在MySQL中,这通常是通过Limit子句实现的。要限制查询的范围,使用range()方法。

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

上面的代码表示结果集从第五个记录开始而不是第一个,并且值返回10个记录。在大多数情况下我们只要“最开始的n个记录”。所以,指定0作为第一个参数,指定n作为第二个参数即可。

调用range()方法两次将会覆盖之前的值,不带参数的调用将会移除查询中的所有范围限制。

表排序(Table sorting)

要生成一个可以对任意列进行排序的结果表,使用TableSort扩展器并且加入表格标题。注意扩展器将会返回一个新查询对象,之后也应当使用这个新的查询对象。

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

条件式(Contitionals)

条件式是一个复杂的主题,在选择、更新和删除查询中都会用到,因此需要单独解释。不像更新和删除查询,选择查询拥有两种类型的条件:WHERE子句和
HAVING子句。HAVING子句的行为等同于WHERE子句,除非使用havingCondition()和having()方法替代
condition()和where()方法的时候。

执行查询

查询构建完成之后,调用excute()方法来编译和运行这个查询。

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

执行查询将会返回结果集或者语句对象,和db_query()的返回值完全一样,并且使用同样的方式取值。

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

注意:当在多列动态查询中使用下列的方法时请小心:

fetchField()
fetchAllKyed()
fetchCol()
这些方法当前需要数字的列索引(0、1、2等)而不是表别名,但是,目前查询构造器并不能保证返回的字段有确定的顺序,所以数据列可能不会是你所期望的顺
序。详细的说,表达式总是添加到字段之后,不管你是否把它们添加到查询前面。(这个说明不针对静态查询,静态查询总是会按照你指定的顺序返回数据列。)

统计查询(Count queries)

每一个查询都可以有相应的“统计查询”,从而得到原查询的结果行数。要得到统计查询,使用countQuery()方法。

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

$count_query变量当前是一个新的动态选择查询,没有任何排序限制,执行的时候返回的结果集只包含一个数值,也就是匹配原始查询的记录条数。因为PHP支持返回对象的链式方法,下面的形式更为常用:

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

调试(Debugging)

要检查一个查询对象在它的生命周期中某一时刻的SQL查询,只需要调用__toString()方法。

<?php
  print_r($query->__toString());
?>

扩展器(Extender)

选择查询支持名为“扩展器”的概念,扩展器提供了一种在运行时为选择查询添加功能的方式,这些功能包括添加额外的方法或者修改已有方法的行为。

如果熟悉面向对象的设计模式,扩展器就是对装饰模式的一种实现。它通过提供一种灵活的替代方法进行子类化以扩展功能,从而动态的给对象添加一些额外的职责。

使用扩展器

要使用扩展器,你必须首先拥有一个查询对象。然后通过这个查询对象调用extend()方法可以得到一个新的对象以替换原有的对象。例如:

<?php
$query = $query->extend('PagerDefault');
?>

上面的代码使用了一个选择查询,创建了一个包含原始选择查询的新的“PagerDefault”查询对象,并且返回了这个新对象。$query变量现在可以像原始查询对象那样使用,同时拥有了一些额外的方法。

注意$query变量没有被替换,新的对象是extend()返回的,如果没有保存到一个变量中就会丢失。比如,下面的操作将得不到你期望的结果:

<?php
$query = db_select('node', 'n');
$query
  ->fields('n', array('nid', 'title')
  ->extend('PagerDefault')   // 这一行返回了一个新的PagerDefault对象。
  ->limit(5);               // 这一行能工作,因为调用的是PagerDefault对象。

// extend()方法的返回值没有保存到任何变量中,所以$query变量仍然是原来的选择查询变量。
$query->orderBy('title');

// 这一行执行了选择查询对象,而不是扩展器对象,扩展器对象已经不存在了。$result = $query->execute();
?>

要避免这个问题,推荐在这个选择查询第一次被定义时就进行转换。

<?php
$query = db_select('node', 'n')->extend('PagerDefault')->extend('TableSort');
$query->fields(...);
// ...
?>

这样确保了$query对象从一开始就是完全扩展了的对象。

还要注意当扩展器可以叠加(如上例所示),不是所有的扩展器都能和其它扩展器兼容,并且顺序也可能造成问题。比如,一个查询同时扩展了分页和表格排序行为的话,必须首先使用分页扩展器。

创建新扩展器

一个扩展器只是一个实现了SelectQueryInterface接口的类,并且在其构造函数中包含两个参数,一个是选择查询对象(或者另一个实现了
SelectQueryInterface接口的对象),一个是DatabaseConnection对象。然后必须实现
SelectQueryInterface的方法并传递给构造函数中指定的查询对象,最后返回对象本身。

在绝大多数情况下,所有这些都可以通过扩展SelectQueryInterface接口在内部实现。因此实际上任何实现了SelectQueryInterface接口的类都是扩展器,类的名称就是在extend()方法中指定的参数。

对于扩展器类,之后可以根据需要添加或者重载方法,任何没有重载的方法将会透明的通过包装好的查询对象传递。当重载一个方法的时候,扩展器可能会调用底层
查询对象也可能不会,但是必须返回与SelectQuery接口所期望的同样的值。多数情况下,就是查询对象本身,对于扩展器,就是扩展器对象本身。

下面的例子会更清楚的说明:

<?php
class ExampleExtender extends SelectQueryExtender {

  /**
   * 重载通常的排序行为。
   */
  public function orderBy($field, $direction = 'ASC') {
    return $this;
  }

  /**
   * 添加我们自己的新方法。
   */
  public function orderByForReal($field, $direction = 'ASC') {
    $this->query->orderBy($field, $direction);
    return $this;
  }
}
?>

上面的例子重载了orderBy()方法但是没有进行任何操作,而是添加了另一个方法orderByForReal(),通过它来执行实际的排序操作。
(当然这是一个相当没有意义的例子,不过主要是用来阐述扩展器的工作机制。)注意在两个方法中,返回的$this变量都是扩展器对象自身。这就确保了扩展
器不会在查询对象返回时“弄丢”。

任何模块都可以定义扩展器,Drupal核心定义了两个很有用的扩展器:PagerDefault和TableSort。关于如何在你的代码中利用这些类请参考API文档。

查询修改(Query alteration)

动态选择查询的一个重要的特性是能够被其它模块自由的修改,这就意味着其它模块可以插入自己的限制条件到查询中,包括修改模块的行为或者对查询进行运行时的限制,比如节点访问权限限制。查询修改有三个组件:标签、元数据和hook_query_alter();

标签(Tagging)

每个动态选择查询都可以用一个或多个字符串来标注“标签”。这些标签定义了查询的类型,以便让修改钩子(hooks)决定是否要进行操作。标签应该是一个
小写字母数字字符串,和php变量名的命名规则相同。(也即,只允许字母、数字和下划线,并且必须以字母开头)要给查询添加标签,使用addTag()方
法:

<?php
$query->addTag('node_access');
?>

要检查查询是否已经被某个标签所标记,可以使用下面三种办法:

<?php
// 如果查询对象拥有标签返回TRUE。
$query->hasTag('example');

// 如果查询对象拥有下列每个标签返回TRUE。
$query->hasAllTags('example1', 'example2');

// 如果查询对象拥有下列任一标签返回TRUE。
$query->hasAnyTag('example1', 'example2');
?>

hasAllTags()和hasAnyTag()都可以带有任意数量的参数,每个参数代表一个查询的标签,顺序是无关紧要的。

对于标签使用的方式没有强制的规定,不过通常会使用某些标准标签。下面是一个标准化标签的部分列表:

node_access
该查询可以加上节点访问权限限制。

translatable
该查询列可以被翻译。

term_access
该查询可以被加上基于分类术语的权限限制。

views
该查询是通过views模块生成的。

元数据(Meta data)

查询也可以添加上元数据以便给修改钩子提供额外的上下文信息。元数据可以包含任何PHP变量并可以用字符串作键名。

<?php
$node = node_load($nid);
// ... Create a $query object here.
$query->addMetaData('node', $node);
?>

元数据并没有确定的含义,而且自身对查询对象没有任何影响。使用它只是为了给修改钩子提供额外的信息,而且只有当查询拥有特定的标签时才需要使用。

访问一个查询所包含的元数据,使用getMetaData()方法。

<?php
$node = $query->getMetaData('node');
?>

如果指定的键名不包含元数据,则会返回NULL。

hook_query_alter()

标签和元数据本身都不会做任何事,它们只是独立的提供信息给hook_query_alter()钩子,在这里才会对选择查询进行实际的操作。

通过执行execute()方法,在查询字串编译完成之后,所有的动态选择查询对象都会立即传递给hook_query_alter()钩子。这就提供给了模块一个按照自身需要操纵查询的机会。hook_query_alter()接受单个参数:选择查询对象自身。

<?php
/**
* Implementation of hook_query_alter().
*/
function example_query_alter(QueryAlterableInterface $query) {
  // ...
}
?>

还可以使用特定于标签的修改钩子,hook_query_TAG_NAME_alter(),当基本的查询钩子调用完毕后,这个钩子会被任何打上标签的查询调用。下面的例子会被包含了“node_access”标签的查询所调用:

<?php
function example_query_node_access_alter(QueryAlterableInterface $query) {
  // ...
}
?>

这里有关于hook_query_alter()钩子的两个重要观察:

$query变量不需声明通过引用传递,因为这是一个对象,PHP5及以后版本在处理对象时不会对对象进行复制,所以声明引用传递是不必要的,修改钩子也没有返回值。
参数的类型明确指定为QueryAlterableInterface。虽然不是严格的必要,明确指定参数类型可以提供更好一些的运行时保护,以避免传递
了错误类型的变量。类型也最好指定为QueryAlterableInterface而不是SelectQuery,这样可以提供提供更好的向上兼容性。
修改钩子可以对查询对象进行任何操作,除了再执行这个查询一次,这样会导致无限循环错误。修改钩子可以使用查询关联的标签和元数据信息,以决定要进行的操
作。模块开发者也可以调用额外的方法来对查询添加额外的字段、连接、条件等等,还可以访问查询对象的内部结构来进行直接的操纵。最好首先对查询添加好新的
信息,然后再顺序从查询中移除信息或操纵结构。

<?php
$fields =& $query->getFields();
$expressions =& $query->getExpressions();
$tables =& $query->getTables();
$order =& $query->getOrderBy();
$where =& $query->conditions();
$having =& $query->havingConditions();
?>

值得注意的一点是上面的语句必须返回引用(=&),这样修改钩子访问的才是和对象同样的数据结构。所有上面的方法都会返回数组,它们的基本结构包含在在SelectQuery的内部文档中:/database/select.inc。

....................

一个选择查询总是会返回一个包含了零个或多个记录的结果集对象,根据具体的应用,有很多种方法可以从这个结果集中获取数据。

最常用的应用是使用foreach()循环来遍历结果集。

<?php
$result = db_query("SELECT nid, title FROM {node}");
foreach ($result as $record) {
  // Do something with each $record
}
?>

根据需要的结果,也有很多种其它的方式来获取记录。

显式的取得下一条记录可以使用:

<?php
$record = $result->fetch();            // 使用缺省的获取模式。
$record = $result->fetchObject();  // 获取为标准对象。
$record = $result->fetchAssoc();   // 获取为关联数组。
?>

如果没有下一条记录则会返回FALSE。最好使用fetchObject()和fetchAssoc()而不是fetch(),因为后面的操作大多是根据类型组织的。如果你需要使用其它的PDO获取模式则可以使用fetch()。

要获取结果集中的单个字段,使用:

<?php
$record = $result->fetchField($column_index);
?>

缺省$column_index的值是0,表示第一个字段。

统计总的行数可以使用:

<?php
$number_of_rows = $result->rowCount();
?>

要把所有记录一次全部保存到一个数组中,使用下面之一的方式:

<?php
// 把所有记录保存到一个标准类索引数组中。
$result->fetchAll();

// 把所有记录保存到一个以记录名为键的关联数组中。
$result->fetchAllAssoc($field);
// 用 字段1 => 字段2 的形式把两列结果保存到一个关联数组中。
$result->fetchAllKeyed();
// 也可以通过列索引来指定需要保存的两列。
$result->fetchAllKeyed(0,2); // 字段0 => 字段2
$result->fetchAllKeyed(1,0); // 字段1 => 字段0

// 把单列结果保存到一个数组中。
$result->fetchCol();
// 把指定列的单列结果保存到一个数组中。
$result->fetchCol($column_index);
?>

注意fetchAll()和fetchAllAssoc()默认会使用查询中设定(索引数组、关联数组或者对象)好的获取模式。需要修改时可以传递一个新的获取模式常量,对fetchAll()来说传递到第一个参数,对fetchAllAssoc()传递到第二个参数。

因为PHP支持返回对象上的链式方法调用,所以通常会完全省略掉$result变量,例如:

<?php
// 获取由nid和titile组成的关联数组。.
$nodes = db_query("SELECT nid, title FROM {node}")->fetchAllKeyed();

// 从数据库中获取单条记录。
$node = db_query("SELECT * FROM {node} WHERE nid = :nid", array(':nid' => $nid))->fetchObject();

// 从数据库中获取单个值。
$title = db_query("SELECT title FROM {node} WHERE nid = :nid", array(':nid' => $nid))->fetchField();
?>

..................

插入查询必须使用查询构造器对象,不同的数据库需要对LOB字段(大对象,比如MySQL的TEXT类型)和BLOB字段(二进制大对象)的特殊处理,所以需要使用抽象层来适应不同数据库驱动处理的需要。

插入查询从以下方式使用db_insert()函数开始:

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

这就创建了一个可以插入多个记录到node表里的插入查询对象。注意这里的表名外面也不需要大括号,查询构造器会自动处理。

插入查询使用了流畅API(Fluent API)设计,也就是说,所有的方法(除了execute())都会返回查询对象自身以允许使用链式调用。在大多数情况下,这意味着查询对象就完全不需要被保存成变量了。

插入查询对象支持数种使用模式以适应不同的需要。通常的工作流包括指定要插入数据的字段,指定要插入字段的数据,然后执行这个查询。下面列出了通常推荐使用的一些使用模式。

紧凑形式

对大多插入查询推荐的形式是紧凑形式:

<?php
$nid = db_insert('node')
  ->fields(array(
    'title' => 'Example',
    'uid' => 1,
    'created' => REQUEST_TIME,
  ))
  ->execute();
?>

这将产生一个等价的如下查询:

INSERT INTO {node} (title, uid, created) VALUES (‘Example’, 1, 1221717405);

<?php
db_insert('node')
?>

这一行创建了一个针对node表的新查询对象。

<?php
  ->fields(array(
    'title' => 'Example',
    'uid' => 1,
    'created' => REQUEST_TIME,
  ))
?>

fields()方法有很多种形式的参数,不过最常用的是单个关联数组的形式。数组的键是将要插入数据表的列名,数组的值是对应的要插入的值。这将会对指定表执行一个选择查询。

<?php
  ->execute();
?>

调用execute()方法会执行这个查询,在这个方法调用之前查询不会被执行。

与其它会返回插入查询对象自身的方法不同,如果插入查询中有个自增字段,那么execute()会返回一个这个字段的值。所以上面例子中,返回值直接赋给了$nid变量。如果没有自增字段,execute()的返回的将是一个未定义值,不能使用。

多数时候,这是使用插入查询最好的形式。

退化形式

<?php
$nid = db_insert('node')
  ->fields(array('title', 'uid', 'created'))
  ->values(array(
    'title' => 'Example',
    'uid' => 1,
    'created' => REQUEST_TIME,
  ))
  ->execute();
?>

这是之前查询的一种更冗长的等价形式,结果完全一样。

<?php
  ->fields(array('title', 'uid', 'created'))
?>

当fields()用一个索引数组而不是关联数组做参数被调用时,它只用于设定查询中要用到字段(数据列)而不需设定任何值,如果需要在稍后进行多个插入查询操作的时候会很有用。

<?php
  ->values(array(
    'title' => 'Example',
    'uid' => 1,
    'created' => REQUEST_TIME,
  ))
?>

这个方法指定了一个关联数组,包含要插入数据库的字段名和值。values()方法也可以使用索引数组,如果使用的是索引数组,值的顺序必须和fields()方法中设定的字段的顺序相同。如果使用的是关联数组则可以是任意顺序。通常使用关联数组会有更好的可读性。

这种查询形式很少使用,通常紧凑模式会更好。在通常情况下只适合分开fields()和values()进行多次插入查询。

多插入形式

插入查询对象也可以多次进行值的设定,也就是说,values()可以被多次调用,以便顺序执行多个插入语句。特别是有可能这种操作会带来数据库兼容性问
题。对于大多数数据库来说,多次插入语句将会在一次事务操作中一起执行以便更好的数据整合和提高速度,在MySQL中这将会使用MySQL的多值插入语
法。

<?php
$values = array(
  array(
    'title' => 'Example',
    'uid' => 1,
    'created' => REQUEST_TIME,
  ),
  array(
    'title' => 'Example 2',
    'uid' => 1,
    'created' => REQUEST_TIME,
  ),
  array(
    'title' => 'Example 3',
    'uid' => 2,
    'created' => REQUEST_TIME,
  ),
);
$query = db_insert('node')->fields(array('title', 'uid', 'created'));
foreach ($values as $record) {
  $query->values($record);
}
$query->execute();
?>

上面的例子将会同时执行三段插入语句作为一次操作,使用的是对于特定数据库驱动来说最有效率的方式。注意这里我们将查询对象保存到一个变量当中,这样我们可以循环遍历$values并且重复调用values()方法。

在原始码中,上面的例子等效于以下三个查询:

INSERT INTO {node} (title, uid, created) VALUES ('Example', 1, 1221717405);
INSERT INTO {node} (title, uid, created) VALUES ('Example2', 1, 1221717405);
INSERT INTO {node} (title, uid, created) VALUES ('Example3', 2, 1221717405);
注意在多插入查询中execute()的返回值是未定义的,不应该被使用,因为它非常依赖于使用的数据库驱动。

基于查询结果的插入

如果你希望用另一个表里查询的结果来填充一个表,你既可以在源表中使用SELECT,把数据读取到PHP中然后插入到新表,也只可以执行一个INERT INTO…SELECT FROM查询把所有选择查询中返回的记录都填充到插入查询中。

在这个例子中,我们希望建立一个叫“mytable”的表,包含系统中所有page类型的节点的节点id和用户名。

Drupal 6

<?php
db_query('INSERT INTO {mytable} (nid, name) SELECT n.nid, u.name FROM
{node} n LEFT JOIN {users} u on n.uid = u.uid WHERE n.type = "%s"',
array ('page'));
?>

Drupal 7

<?php
// 建立SELECT查询。
$query = db_select('node', 'n');
// 连接用户表。
$query->join('users', 'u', 'n.uid = u.uid');
// 添加需要的字段。
$query->addField('n','nid');
$query->addField('u','name');
// 添加只查询page节点的条件。
$query->condition('type', 'page');

// 执行查询
db_insert('mytable')
  ->from($query)
  ->execute();
?>

缺省值

在通常情况下,如果不给字段指定一个值,那么由数据表的构架(schema)定义的缺省值将会被自动插入。有些时候,比如你希望整个记录都使用缺省值的情况下,你需要明确的告诉数据库使用缺省值。要明确的告诉数据库使用缺省值,使用useDefaults()方法。

<?php
$query->useDefaults(array('field1', 'field2'));
?>

这一行表明了查询将对fields和fields2使用数据库定义的缺省值。注意如果同时对一个字段使用useDefault()和values()会出现错误抛出一个异常。

...................

插入查询必须使用查询构造器对象,并从db_insert()函数开始:

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

这就创建了一个可以node表里的记录的插入查询对象。注意这里的表名外面也不需要大括号,查询构造器会自动处理。

删除查询使用了流畅API(Fluent API)设计,也就是说,所有的方法(除了execute())都会返回查询对象自身以允许使用链式调用。在大多数情况下,这意味着查询对象就完全不需要被保存成变量了。

删除查询的概念非常简单,只生成一个WHERE子句。关于WHERE子句的完整结构的详细信息在条件子句一章中,这里只是提及一下。

下面是完整的删除查询使用形式。

<?php
$num_deleted = db_delete('node')
  ->condition('nid', 5)
  ->execute();
?>

上面查询将会删除node表中所有nid等于5的记录,等效于下面的查询:

DELETE FROM {node} WHERE nid=5;

execute()方法返回被查询删除掉的记录总数。

dashan 答复于

合并(Merge)查询是一种特殊的混合查询,虽然它的语法是由SQL
2003规范所定义的,但实际上没有任何数据库支持这个标准语法。不过大多数数据库提供了一些替代实现的语法。Drupal中的合并查询构造器把合并查询
的概念抽象到了一个结构对象之中,这样就可以向下兼容每个数据库不同的语法了。

通常认为是插入查询和更新查询的组合使用。如果满足给定的条件,比如某给定主键的行存在,则运行某个查询,否则运行其它查询。通常情况下等效于:

<?php
if (db_result(db_query("SELECT COUNT(*) FROM {example} WHERE id=:id", array(':id' => $id))->fetchField())) {
  // 当 id = $id 时执行更新
}
else {
  // 执行插入,插入$id到id字段中
}
?>

实际的实现在每种数据库中都有很大的不同。注意虽然合并查询在概念上是一个原子操作(atomic operation),但具体是否原子操作要依赖于特定的数据库实现。例如MySQL的实现是单个原子查询,但更底层(如上例)的不是。

下面是合并查询最常见的一些用法。

简单设置

<?php
db_merge('example')
  ->key(array('name' => $name))
  ->fields(array(
      'field1' => $value1,
      'field2' => $value2,
  ))
  ->execute();
?>

上面的例子中,我们构建了一个关于“example”表的查询,之后指定了一个关键字段’name’和一个值$name,然后又指定了要设置的值。

如果存在字段“name”的值为$name的行,则该行的字段field1和field2将会被设置成相应的值。反之如果该行不存在,则会新插入一行,并
设置name字段的值为$name,field1字段的值为$value1,field2字段的值为$value2。因此查询结束后不管原本是否存在该行
结果都是相同的。

条件设置

有些情况下,你希望对于由key()指定的字段记录存在和不存在的情况分别设置不同的值,则有两种方法可以实现。

<?php
db_merge('example')
  ->key(array('name' => $name))
  ->fields(array(
      'field1' => $value1,
      'field2' => $value2,
  ))
  ->updateFields(array(
    'field1' => $alternate1,
  ))
  ->execute();
?>

上面的例子和第一个是一样的,不过当记录已经存在时,它会把field1字段的值设置为$alternate1而不是$value1,field2字段则
不会设置。updateFields()方法既可以接受一个关联数组,也可以接受两个索引数组,一个放置字段名一个放置字段值,按照顺序一一对应。

<?php
db_merge('example')
  ->key(array('name' => $name))
  ->fields(array(
      'field1' => $value1,
      'field2' => $value2,
  ))
  ->expression('field1', 'field1 + :inc', array(':inc' => 1))
  ->execute();
?>

这个例子中,如果记录已经存在,则field1的当前值将会增加1。这点对于“计数查询”非常的有用,使得可以在每次有特定事件发生的时候在数据库中递增一些计数器的值。但不管记录是否存在,field2仍然会被设置为同样的值。

注意expression()方法可以被多次调用,每次设置一个需要表达式的字段。第一个参数是字段名,第二个是一段SQL片段表示将要被设置的表达式,可选的第三个参数是要插入到表达式中的占位符值。

在expression()中使用的字段也不必要在fields()中先行设置。

限制更新

<?php
db_merge('example')
  ->key(array('name' => $name))
  ->fields(array(
      'field1' => $value1,
      'field2' => $value2,
  ))
  ->updateExcept('field1')
  ->execute();
?>

updateExcept()方法既可以用一个包含多个字段名的数组作为参数,也可以直接用多个字段名作为参数。假如查询的关键字段记录存在,在
updateExcept()中指定的字段将不会受到影响。也就是说,如果某条name=$name的记录存在,那么fields2将会被设置
为$value2,而fields1则会被完全忽略保留它原来的值。不过如果该记录不存在,那么fields1的值仍将被设置为$value1。

优先级

上面给出的API有可能会定义出不符合逻辑的查询,比如一个字段可能既被设置为忽略同时又被设置了表达式。为了尽量避免潜在的错误,请应用下面的规则:

如果一个字段设置给了expression(),那么它的优先级高于update()和updateExcept()。
如果在updateField()中指定了值,则updateExcept()会被忽略。
如果在updateField()中指定了值,只有当记录存在时这些字段才会被修改,在updateField()中没有设置的字段不会受到影响。
注意即便这样仍有可能定义出不合逻辑的查询来,这就需要开发者来确保无意义的查询不会起到实际的作用。

dashan 答复于

合并(Merge)查询是一种特殊的混合查询,虽然它的语法是由SQL
2003规范所定义的,但实际上没有任何数据库支持这个标准语法。不过大多数数据库提供了一些替代实现的语法。Drupal中的合并查询构造器把合并查询
的概念抽象到了一个结构对象之中,这样就可以向下兼容每个数据库不同的语法了。

通常认为是插入查询和更新查询的组合使用。如果满足给定的条件,比如某给定主键的行存在,则运行某个查询,否则运行其它查询。通常情况下等效于:

<?php
if (db_result(db_query("SELECT COUNT(*) FROM {example} WHERE id=:id", array(':id' => $id))->fetchField())) {
  // 当 id = $id 时执行更新
}
else {
  // 执行插入,插入$id到id字段中
}
?>

实际的实现在每种数据库中都有很大的不同。注意虽然合并查询在概念上是一个原子操作(atomic operation),但具体是否原子操作要依赖于特定的数据库实现。例如MySQL的实现是单个原子查询,但更底层(如上例)的不是。

下面是合并查询最常见的一些用法。

简单设置

<?php
db_merge('example')
  ->key(array('name' => $name))
  ->fields(array(
      'field1' => $value1,
      'field2' => $value2,
  ))
  ->execute();
?>

上面的例子中,我们构建了一个关于“example”表的查询,之后指定了一个关键字段’name’和一个值$name,然后又指定了要设置的值。

如果存在字段“name”的值为$name的行,则该行的字段field1和field2将会被设置成相应的值。反之如果该行不存在,则会新插入一行,并
设置name字段的值为$name,field1字段的值为$value1,field2字段的值为$value2。因此查询结束后不管原本是否存在该行
结果都是相同的。

条件设置

有些情况下,你希望对于由key()指定的字段记录存在和不存在的情况分别设置不同的值,则有两种方法可以实现。

<?php
db_merge('example')
  ->key(array('name' => $name))
  ->fields(array(
      'field1' => $value1,
      'field2' => $value2,
  ))
  ->updateFields(array(
    'field1' => $alternate1,
  ))
  ->execute();
?>

上面的例子和第一个是一样的,不过当记录已经存在时,它会把field1字段的值设置为$alternate1而不是$value1,field2字段则
不会设置。updateFields()方法既可以接受一个关联数组,也可以接受两个索引数组,一个放置字段名一个放置字段值,按照顺序一一对应。

<?php
db_merge('example')
  ->key(array('name' => $name))
  ->fields(array(
      'field1' => $value1,
      'field2' => $value2,
  ))
  ->expression('field1', 'field1 + :inc', array(':inc' => 1))
  ->execute();
?>

这个例子中,如果记录已经存在,则field1的当前值将会增加1。这点对于“计数查询”非常的有用,使得可以在每次有特定事件发生的时候在数据库中递增一些计数器的值。但不管记录是否存在,field2仍然会被设置为同样的值。

注意expression()方法可以被多次调用,每次设置一个需要表达式的字段。第一个参数是字段名,第二个是一段SQL片段表示将要被设置的表达式,可选的第三个参数是要插入到表达式中的占位符值。

在expression()中使用的字段也不必要在fields()中先行设置。

限制更新

<?php
db_merge('example')
  ->key(array('name' => $name))
  ->fields(array(
      'field1' => $value1,
      'field2' => $value2,
  ))
  ->updateExcept('field1')
  ->execute();
?>

updateExcept()方法既可以用一个包含多个字段名的数组作为参数,也可以直接用多个字段名作为参数。假如查询的关键字段记录存在,在
updateExcept()中指定的字段将不会受到影响。也就是说,如果某条name=$name的记录存在,那么fields2将会被设置
为$value2,而fields1则会被完全忽略保留它原来的值。不过如果该记录不存在,那么fields1的值仍将被设置为$value1。

优先级

上面给出的API有可能会定义出不符合逻辑的查询,比如一个字段可能既被设置为忽略同时又被设置了表达式。为了尽量避免潜在的错误,请应用下面的规则:

如果一个字段设置给了expression(),那么它的优先级高于update()和updateExcept()。
如果在updateField()中指定了值,则updateExcept()会被忽略。
如果在updateField()中指定了值,只有当记录存在时这些字段才会被修改,在updateField()中没有设置的字段不会受到影响。
注意即便这样仍有可能定义出不合逻辑的查询来,这就需要开发者来确保无意义的查询不会起到实际的作用。

dashan 答复于

“条件子句(conditional
clause)”是查询的一个部分,用于限制某些行只匹配特定的条件。在SQL中即是在SELECT、UPDATE、DELETE查询中的WHERE和
HAVING部分。Drupal的动态查询中这些都是用相同的机制来实现的,下面的语法都可以应用到上述三种查询类型中,除了特别提到的以外。

概念

条件段
条件段是独立的条件子句片段。

操作符
每个条件子句都由操作符连接的数条条件段构成。操作符是一种类似于AND和OR的术语,用于把两个条件段连接起来。

条件对象
Drupal把每个条件段表示为一个QueryConditional类的实例,条件对象即是这个类的实例。

下面的例子展示了这些结构:

查询:
SELECT FROM {mytable} WHERE (a = 1 AND b = ‘foo’ OR (c = ‘bar’))

条件子句:
WHERE (a = 1 AND b = ‘foo’ OR (c = ‘bar’))

条件段:
(a = 1 AND b = ‘foo’ OR (c = ‘bar’))
(c = ‘bar’)

操作符
AND, OR

选择、更新和删除查询对象都实现了同样的QueryConditionalInterface接口,并在内部都包装了一个QueryContional对象。但QueryConditional类也可以直接被实例化。

在条件语句中每个条件段都通过操作符来连接,一个条件对象由数个通过特定操作符连接好的条件段构成,缺省的操作符是AND。每个条件对象中可以用不同的操作符来连接条件段,所以条件语句允许嵌套条件段到另一个条件段中。通过这种方式,可以构建出任意复杂的条件语句。

API

在条件对象上使用的主要有两种方法。

$query->condition($field, $value = NULL, $operator = ‘=’)
condition()方法用来添加标准的$field $value $opertor结构的条件段,包括所有对条件进行二进制比较的情形,例如=、
<、 >=等等。 如果没有指定操作符,默认为等于。所以最常用的条件是contition(‘myfield’,
$value),将生成一个条件段 myfield = :value,:value将在查询运行时被替换为$value的值。

$query->where($snippet, $args = array())
where()方法允许任意的SQL语句作为条件段,$snippet可以是任何合法的SQL语句,但如果包含有变量内容必须要使用命名转义
符。$args数组是对应的转义符值对。开发者必须确保$snippet是有效的SQL,系统不会对这段语句进行任何数据库特异的修改。

在多数情况下最好使用condition()方法,除非$field $value $operator格式不能适用,比如你使用了更复杂的东西像是表达式,或者在两个字段间进行条件比对。每个方法都会返回相应的条件对象,所以可以无限期的链式调用下去。

contidion()方法也有一些特殊的应用。

数组操作符

有些操作符需要使用数组作为值的参数,包括IN和BETWEEN。如果是操作符IN,则$value表示字段值需要等于提供的数组中的任一值。下面的调用将展示这一点:

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

如果操作符是BETWEEN,$value必须是一个有两个值的数组,字段值则需要在这两个值之间。例如:

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

嵌套条件

condition()的第一个参数也可以是另一个条件对象,这个内部的条件对象将会通过圆括号的形式合并到外部条件对象中。内部条件对象也可以使用和外部条件对象不同的操作符连接。这样就可以通过自底向上的方式创建条件对象来构建一个复杂的嵌套条件结构了。

db_condition()辅助函数会返回一个新的条件对象,它只有一个参数即对象要使用的操作符。通常使用辅助方法db_and()、db_or()和db_xor()就囊括了几乎所有的情形,这使得条件语句可以用非常紧凑的语法插入到查询中去。

举例说明如下:

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

空值

有些时候,我们希望判断某条数据库字段中的值是否为NULL。虽然这也可以通过使用contition()方法来完成,不过下面提供的工具方法看起来更明晰会更适合些。

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

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

两个方法都可以根据需要使用contition()和where()来连接使用。

子选择

condition()也支持子选择来作为$value。要使用子选择,首先通过db_select()创建一个选择查询对象,然后不是执行这个选择查询
而是把它传递到另一个查询的contidion()方法的value参数中去。当查询被执行的时候这个子查询会自动集成到主查询中。

子选择通常只有在下面两种情况下有用:子选择结果只返回单行的单个值并且操作符是=、<、>、<= 或 >=;子选择返回单一列的信息并且操作符是IN。大多数其它的组合都会导致语法错误。

注意:目前只能在子选择条件语句中使用IN操作符,因为使用其它操作符时子查询不会被圆括号包裹,所以会导致语法错误。参见 #1267508:
Subselects don't work in DBTNG conditions, except when used as value for
IN.

注意在某些数据库中,特别是MySQL,条件子句中的子查询并不是特别的快。如果可能的话使用连接JOIN、在FROM子句中使用子选择、或者多次使用普通条件语句来替代子选择会更好。

例子

下面的例子应该对更清晰的使用条件子句有帮助。

<?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))
?>

dashan 答复于

更新(Update)查询必须使用查询构造器对象,不同的数据库需要对LOB字段(大对象,比如MySQL的TEXT类型)和BLOB字段(二进制大对象)的特殊处理,所以需要使用抽象层来适应不同数据库驱动处理的需要。

更新查询从dp_update()函数开始,如下:

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

这个创建的更新查询对象用来修改node表中的一个或多个记录。注意这里的表名外面也不需要大括号,查询构造器会自动处理。

更新查询对象使用了流畅API(Fluent API)设计,也就是说,所有的方法(除了execute())都会返回查询对象自身以允许使用链式调用。在大多数情况下,这意味着查询对象就完全不需要被保存成变量了。

更新查询的概念很简单,就是用使用一组键/值对来设置WHERE子句。关于WHERE子句的完整结构的详细信息在条件子句一章中,这里只是提及一下。

典型的更新查询如下:

<?php
/* This is a horrible example as node.status is pulled from
node_revision.status table as well, updating it here will do nothing. */
$num_updated = db_update('node')
  ->fields(array(
    'uid' => 5,
    'status' => 1,
  ))
  ->condition('created', REQUEST_TIME - 3600, '>=')
  ->execute();
?>

上面的查询将会更新node表中所有最近一小时内创建的节点,设置uid字段的值为5,status字段的值为1。fields()方法使用关联数组来指
定要更新的字段和它的值。注意不像选择查询,更新查询的fields()方法只接受关联数组参数。还有,数组中字段的顺序以及调用方法的顺序也是无关紧要
的。

上面的例子等效于下面的查询:

UPDATE {node} SET uid=5, status=1 WHERE created >= 1221717405;

execute()方法将会返回受到查询影响的行数,注意这里的影响不等于匹配。在上面的查询中,uid的值本就是5和status的值本就是1记录会被
匹配,但是这个数值没有改变的记录不会被影响,因此不会被统计在返回值里。作为一种边缘效应,这使得我们无法用更新查询来判断某条给定的记录是否存在。

dashan 答复于