在 CakePHP 3 中使用 IFNULL ... GROUP_CONCAT()
Using IFNULL ... GROUP_CONCAT() in CakePHP 3
我有一个正常的 MySQL 查询如下,它工作正常:
SELECT d.*, IFNULL(
(SELECT GROUP_CONCAT(value) FROM display_substances `ds`
WHERE `ds`.`display_id` = `d`.`id`
AND ds.substance_id = 2
GROUP BY `ds`.`display_id`
), 'Not Listed'
) `substances` FROM displays `d`;
背景是我有 2 个 table,displays
和 display_substances
。我想为给定物质 ID(在上面的查询中由 ds.substance_id = 2
表示)呈现 displays
中每一行的 table 和 display_substances
中的相应值。
并非 displays
中的每一行都有对应的 display_substances
值。如果是这种情况,它应该输出单词 "Not Listed"。这实际上意味着如果 display_substances
中没有相应的行 - 那么 display.id
就没有数据 - 因此数据库中该特定行的数据是 "Not Listed" 。上面的查询正是这样做的。
我希望能够使用 CakePHP 的 ORM 语法编写我的查询。
table 的结构如下。
mysql> DESCRIBE displays;
+----------+----------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+----------------------+------+-----+---------+----------------+
| id | smallint(5) unsigned | NO | PRI | NULL | auto_increment |
| label | varchar(255) | NO | | NULL | |
+----------+----------------------+------+-----+---------+----------------+
mysql> DESCRIBE display_substances;
+--------------+-----------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+-----------------------+------+-----+---------+----------------+
| id | mediumint(8) unsigned | NO | PRI | NULL | auto_increment |
| display_id | smallint(5) unsigned | NO | MUL | NULL | |
| substance_id | mediumint(8) unsigned | NO | MUL | NULL | |
| value | text | NO | | NULL | |
+--------------+-----------------------+------+-----+---------+----------------+
Table 类 存在并定义了适当的关系。
// src/Model/Table/DisplaysTable.php
$this->hasMany('DisplaySubstances', [
'foreignKey' => 'display_id'
]);
// src/Model/Table/DisplaysSubstancesTable.php
$this->belongsTo('Displays', [
'foreignKey' => 'display_id',
'joinType' => 'INNER'
]);
到目前为止我有这个:
$substance_id = 2;
$data =
$DisplaySubstances->find()
->contain(['Displays'])
->where(['DisplaySubstances.substance_id' => $substance_id)
->select(['Displays.id', 'Displays.label', 'Displays.anchor', 'DisplaySubstances.value'])
->enableHydration(false)->toArray();
这将使我得到 displays
中的行,这些行在 display_substances
中具有物质 ID 2 的相应值。这实际上是我们拥有数据的所有内容,但不包括任何行"Not Listed".
我不知道如何使用 Cake 的 ORM 语法编写普通 SQL 查询的 IFNULL...GROUP_CONCAT
部分。
我读过 How to use group_contact in cakephp query? 所以可以看到可以在 ->select()
条件下使用 GROUP_CONCAT
。但是链接示例要简单得多,因为它不使用 IFNULL
条件和相应的操作(返回 "Not Listed")。
如果可能的话,有人可以建议如何用 Cake 的 ORM 语法编写吗?
CakePHP 版本为 3.5.18
IFNULL
和 GROUP_CONCAT
都是 SQL 函数,所以您可以使用函数生成器,它可以用来创建您想要的任何函数调用,它的神奇调用处理程序如果调用的方法没有具体实现,将创建一个通用函数调用,即 $functionsBuilder->IFNULL()
和 $functionsBuilder->GROUP_CONCAT()
将正常工作。
函数构建器也接受表达式,因此您可以将另一个查询对象传递给您的 IFNULL()
调用,即示例中的子查询 SQL.
$subquery = $DisplaySubstances->find()
->select(function (\Cake\ORM\Query $query) {
return [
$query->func()->GROUP_CONCAT(['value' => 'identifier'])
];
})
->where(function (\Cake\Database\Expression\QueryExpression $exp) use ($substance_id) {
return [
$exp->equalFields('DisplaySubstances.display_id', 'Displays.id'),
'DisplaySubstances.substance_id' => $substance_id
];
})
->group('DisplaySubstances.display_id');
$query = $Displays->find()
->select(function (\Cake\ORM\Query $query) use ($subquery) {
// Before CakePHP 3.8.3 queries need to be wrapped in an additional expression
// in order for the query builder to generate them wrapped in parentheses in SQL
$subquery = $query->newExpr($subquery);
return [
'substances' => $query->func()->IFNULL([$subquery, 'Not Listed'])
];
})
->select($Displays);
另见
我有一个正常的 MySQL 查询如下,它工作正常:
SELECT d.*, IFNULL(
(SELECT GROUP_CONCAT(value) FROM display_substances `ds`
WHERE `ds`.`display_id` = `d`.`id`
AND ds.substance_id = 2
GROUP BY `ds`.`display_id`
), 'Not Listed'
) `substances` FROM displays `d`;
背景是我有 2 个 table,displays
和 display_substances
。我想为给定物质 ID(在上面的查询中由 ds.substance_id = 2
表示)呈现 displays
中每一行的 table 和 display_substances
中的相应值。
并非 displays
中的每一行都有对应的 display_substances
值。如果是这种情况,它应该输出单词 "Not Listed"。这实际上意味着如果 display_substances
中没有相应的行 - 那么 display.id
就没有数据 - 因此数据库中该特定行的数据是 "Not Listed" 。上面的查询正是这样做的。
我希望能够使用 CakePHP 的 ORM 语法编写我的查询。
table 的结构如下。
mysql> DESCRIBE displays;
+----------+----------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+----------------------+------+-----+---------+----------------+
| id | smallint(5) unsigned | NO | PRI | NULL | auto_increment |
| label | varchar(255) | NO | | NULL | |
+----------+----------------------+------+-----+---------+----------------+
mysql> DESCRIBE display_substances;
+--------------+-----------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+-----------------------+------+-----+---------+----------------+
| id | mediumint(8) unsigned | NO | PRI | NULL | auto_increment |
| display_id | smallint(5) unsigned | NO | MUL | NULL | |
| substance_id | mediumint(8) unsigned | NO | MUL | NULL | |
| value | text | NO | | NULL | |
+--------------+-----------------------+------+-----+---------+----------------+
Table 类 存在并定义了适当的关系。
// src/Model/Table/DisplaysTable.php
$this->hasMany('DisplaySubstances', [
'foreignKey' => 'display_id'
]);
// src/Model/Table/DisplaysSubstancesTable.php
$this->belongsTo('Displays', [
'foreignKey' => 'display_id',
'joinType' => 'INNER'
]);
到目前为止我有这个:
$substance_id = 2;
$data =
$DisplaySubstances->find()
->contain(['Displays'])
->where(['DisplaySubstances.substance_id' => $substance_id)
->select(['Displays.id', 'Displays.label', 'Displays.anchor', 'DisplaySubstances.value'])
->enableHydration(false)->toArray();
这将使我得到 displays
中的行,这些行在 display_substances
中具有物质 ID 2 的相应值。这实际上是我们拥有数据的所有内容,但不包括任何行"Not Listed".
我不知道如何使用 Cake 的 ORM 语法编写普通 SQL 查询的 IFNULL...GROUP_CONCAT
部分。
我读过 How to use group_contact in cakephp query? 所以可以看到可以在 ->select()
条件下使用 GROUP_CONCAT
。但是链接示例要简单得多,因为它不使用 IFNULL
条件和相应的操作(返回 "Not Listed")。
如果可能的话,有人可以建议如何用 Cake 的 ORM 语法编写吗?
CakePHP 版本为 3.5.18
IFNULL
和 GROUP_CONCAT
都是 SQL 函数,所以您可以使用函数生成器,它可以用来创建您想要的任何函数调用,它的神奇调用处理程序如果调用的方法没有具体实现,将创建一个通用函数调用,即 $functionsBuilder->IFNULL()
和 $functionsBuilder->GROUP_CONCAT()
将正常工作。
函数构建器也接受表达式,因此您可以将另一个查询对象传递给您的 IFNULL()
调用,即示例中的子查询 SQL.
$subquery = $DisplaySubstances->find()
->select(function (\Cake\ORM\Query $query) {
return [
$query->func()->GROUP_CONCAT(['value' => 'identifier'])
];
})
->where(function (\Cake\Database\Expression\QueryExpression $exp) use ($substance_id) {
return [
$exp->equalFields('DisplaySubstances.display_id', 'Displays.id'),
'DisplaySubstances.substance_id' => $substance_id
];
})
->group('DisplaySubstances.display_id');
$query = $Displays->find()
->select(function (\Cake\ORM\Query $query) use ($subquery) {
// Before CakePHP 3.8.3 queries need to be wrapped in an additional expression
// in order for the query builder to generate them wrapped in parentheses in SQL
$subquery = $query->newExpr($subquery);
return [
'substances' => $query->func()->IFNULL([$subquery, 'Not Listed'])
];
})
->select($Displays);
另见