如何只选择那些至少有一条相关记录的记录?

时间:2015-12-30 11:07:44

标签: cakephp query-builder cakephp-3.1

我有以下关联:Orders belongsTo Sites

我执行以下查询:

$sites = $this->Sites->find('all')
->select(['id', 'name'])
->contain([
        'Users' => function ($q) {
                return $q->select(['id', 'owner_id', 'firstname', 'lastname']);
        },
        'Orders' => function ($q) {
                return $q->where(['status >' => 0]);
        },
        'Orders.Agplans'
])
->matching('Users', function ($q) use($owner_id)
{
    return $q->where([
        'Users.owner_id' => $owner_id
    ]);
})
->all();

但大部分网站都没有订单,所以我得到了一些结果:

(int) 100 => object(App\Model\Entity\Site) {
    'id' => (int) 7966,
    'name' => 'Site example',
    'orders' => [],
    'users' => [
        (int) 0 => object(App\Model\Entity\User) {

    ...

是否可以在查询中指定我只希望sites不为空orders

1 个答案:

答案 0 :(得分:4)

由于您拥有hasMany关联,因此您必须另外采取措施来包含Orders

使用内部联接

您可以使用INNER联接,只会选择存在匹配订单的行。出于过滤目的,可以使用Query::innerJoinWith()轻松完成此操作,类似于Query::matching(),但不会加载任何结果。

$sites = $this->Sites
    ->find('all')
    ->select(['id', 'name'])

    // ...

    ->innerJoinWith('Orders', function ($q) {
        return $q->where(['Orders.status >' => 0]);
    })
    ->group(['Sites.id']) // may or may not be necessary
    ->all();

使用您已经做过的匹配

您已经将Query::matching()用于Users,因此我假设您知道它的作用...过滤。为Orders添加进一步匹配也是一种选择,但它会获取其他数据。

$sites = $this->Sites
    ->find('all')
    ->select(['id', 'name'])

    // ...

    ->matching('Orders', function ($q) {
        return $q->where(['Orders.status >' => 0]);
    })
    ->group(['Sites.id']) // may or may not be necessary
    ->all();

最后但并非最不重要的是,使用计数器缓存

使用正确索引的计数器缓存可能是性能最佳的选项,因为过滤将通过同一个表中的列完成。

您可以使用适当的条件在Orders模型中定义它,然后在查询中使用where(),例如

$this->addBehavior('CounterCache', [
    'Sites' => [
        'active_orders_count' => [
            'conditions' => ['Orders.status >' => 0]
        ]
    ]
]);
$sites = $this->Sites
    ->find('all')
    ->select(['id', 'name'])

    // ...

    ->where([
        'active_orders_count >' => 0
    ])
    ->all();

另见