Удалите дубликат поиска () Результаты в CakePHP 3 и принадлежит ToMany - PullRequest
0 голосов
/ 01 ноября 2019

Схема

`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 принадлежит от ToMany B до C

Запрос

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

Выполнение вышеуказанного запроса в CakePHP 3 вернет A с тремя B, потому что естьтри строки в таблице (b1 дважды). Есть ли способ удалить дубликаты результатов с помощью построителя запросов ? Я пробовал Different (), но это не сработало. Я спрашиваю, потому что _joinData отличается, но я не уверен.

1 Ответ

1 голос
/ 01 ноября 2019

distinct() без аргументов будет применяться ко всем выбранным столбцам, т. Е. Будет производиться что-то вроде:

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, в зависимости от используемой СУБД. Кроме того, в зависимости от используемой СУБД и ее конфигурации, GROUP BY вызовет ошибку, так как запрос выберет неагрегированные столбцы, которых нет в предложении GROUP BY (см., Например, * 1018). * MySQL и режим ONLY_FULL_GROUP_BY ).

Чтобы обойти это ограничение, потребуется некоторая хитрость, один из способов, который я использовал в прошлом, - это использование явной посреднической ассоциации в соединенииВ таблице и выберите данные n:m в отдельном запросе, запрос для ассоциации-посредника может затем безопасно применить группировку.

В вашем примере это будет As hasMany Cs и Cs belongsTo Bs, а затем будет содержатьтаблица соединений, где вы можете применить группировку, будет выглядеть примерно так:

$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
            ]
        ]
    ]);

Результат, конечно, будет отформатирован по-разному, поэтому вам может потребоваться переформатировать его в случае необходимости.

...