logo

额外区块类型 (EBT) - 全新的布局构建器体验❗

额外区块类型 (EBT) - 样式化、可定制的区块类型:幻灯片、标签页、卡片、手风琴等更多类型。内置背景、DOM Box、JavaScript 插件的设置。立即体验布局构建的未来。

演示 EBT 模块 下载 EBT 模块

❗额外段落类型 (EPT) - 全新的 Paragraphs 体验

额外段落类型 (EPT) - 类似的基于 Paragraph 的模块集合。

演示 EPT 模块 滚动

滚动

在 Drupal 7 中操作数据库 - 第11课 - 合并查询(MERGE)

14/10/2025, by Ivan

合并查询是一种特殊的混合类型查询。尽管这种语法在 SQL 2003 中被定义,但实际上几乎没有数据库原生支持该语法。然而,大多数数据库都提供了各自特定的替代实现。Drupal 中的合并查询构造器(Merge Query Builder)将“合并查询”的概念抽象为对象结构,这样系统可以根据不同数据库的特性分别编译对应的查询语句。

总体而言,合并查询是 INSERTUPDATE 查询的组合。如果满足某个条件(例如表中存在指定键的记录),则执行一个查询;否则执行另一个查询。大多数情况下,它等价于以下逻辑:

<?php
if (db_result(db_query("SELECT COUNT(*) FROM {example} WHERE id=:id", array(':id' => $id))->fetchField())) {
  // 如果记录存在,执行 UPDATE 查询 WHERE id = $id
}
else {
  // 如果记录不存在,执行 INSERT 查询,插入 id = $id
}
?>

在实际中,不同数据库的实现方式略有不同。请注意,虽然在概念上合并查询应是原子操作,但其原子性是否真正生效取决于数据库的实现。例如,MySQL 的实现是原子的,而上面的伪代码逻辑则不是。

合并查询的通用形式如下:

基础示例

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

在上面的示例中,我们操作表 “example”。我们定义了一个键字段 name,其值为 $name,并指定了一个字段数组用于设置数据。

如果表中已经存在字段 name = $name 的记录,则更新 field1field2 的值;如果不存在,则插入一条新记录,其中 name = $name,field1 = $value1,field2 = $value2。

设置条件更新

在某些情况下,我们希望根据记录是否存在来设置不同的值。有两种方法实现这一点:

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

上述示例与第一个类似,只是当记录已存在时,我们将 field1 更新为 $alternate1。如果记录不存在,则创建新记录,字段值为 field1 = $value1field2 = $value2

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。这种方式常用于“计数器更新”场景,例如统计事件发生次数。若记录不存在,则插入新数据,其中 field1field2 的值按定义设置。

请注意,expression() 可以被多次调用,每个字段调用一次,用于定义更新时的表达式。其第一个参数为字段名,第二个参数为 SQL 表达式片段,第三个可选参数为替换变量数组。

同时,被 expression() 使用的字段不一定要出现在 fields() 中。

限制更新

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

updateExcept() 方法接受字段数组或多个独立字段名参数。被列出的字段在记录存在时不会被更新。

例如,如果表中存在 name = $name 的记录,则仅更新 field2 的值为 $value2,而 field1 保持不变。如果记录不存在,则插入一条新记录并设置所有字段。

优先级规则

上述 API 提供了高度灵活的功能,但如果使用不当,也可能导致逻辑矛盾的查询。例如同时在同一字段上定义 ignoreexpression()。为了避免这种情况,Drupal 遵循以下规则:

  • 如果字段定义了 expression(),则其优先级高于 update()updateExcept()
  • 如果字段同时出现在 update()updateExcept() 中,则 updateExcept() 被忽略。
  • 如果定义了 update(),则只有这些字段会在记录存在时更新;未列出的字段不会被处理。

请注意,理论上仍然可能定义出无意义的查询,因此应谨慎使用。