删除 CakePHP 3 和 belongsToMany 中重复的 find() 结果

Remove duplicate find() Results in CakePHP 3 and belongsToMany

架构

`a`
|id  |
|a1  |

`b`
|id  |
|b1  |
|b2  |


`c`
|a_id|b_id|some_data|
|a1  |b1  |lorem ipsum|
|a1  |b1  |dolor|
|a1  |b2  |abc|

A belongsToMany B 通过 C

查询

$this->As->find()->contain(['Bs' => function (Query $q) {
    return $q->distinct();
}]);

在 CakePHP 3 中执行上述查询将 return A 和三个 B,因为 table 中有三行(b1 两次)。有没有办法使用 query builder 删除重复结果?我试过 distinct() 但没有用。我怀疑是因为_joinData 不同,但我不确定。

没有参数的

distinct() 将应用于所有 selected 列,即它会产生如下内容:

DISTINCT c.a_id, c.b_id, c.some_data, b.id

对所有列应用不同,这将有效地不会删除任何重复项,因为由这些列组成的所有元组都将不同。

您必须仅在特定列上应用不同,以便您的副本之间实际上存在差异,例如使用 Bs.id:

$q->distinct('Bs.id');

这将创建 DISTINCT ON(Bs.id)GROUP BY Bs.id,具体取决于您使用的 DBMS。此外,根据您使用的 DBMS 及其配置,GROUP BY 将导致错误,因为查询将 select 不在 GROUP BY 子句中的非聚合列(参见示例 MySQL and the ONLY_FULL_GROUP_BY mode).

解决这个限制需要一些技巧,我过去使用的一种方法是在联接 table 和 select [=20= 上使用显式中间关联] 数据在一个单独的查询中,中间关联的查询可以安全地应用分组。

你的例子是 As hasMany CsCs belongsTo Bs,然后包含连接 table,你可以在其中应用分组,它看起来像这样:

$this->As
    ->find()
    ->contain([
        'Cs' => [
            'queryBuilder' => function (\Cake\ORM\Query $query) {
                return $query
                    ->select(['Cs.a_id', 'Cs.b_id'])
                    ->group(['Cs.a_id', 'Cs.b_id']);
            },
            'Bs' => [
                'strategy' => \Cake\ORM\Association\BelongsTo::STRATEGY_SELECT
            ]
        ]
    ]);

结果的格式当然会有所不同,因此您可能需要重新格式化它以防万一。