【问题标题】:How to select only those records that have at least one associated record?如何只选择那些至少有一个关联记录的记录?
【发布时间】:2015-12-30 11:07:44
【问题描述】:

我有以下关联: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

【问题讨论】:

    标签: cakephp query-builder cakephp-3.1


    【解决方案1】:

    由于您拥有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();
    

    另见

    【讨论】:

    • 感谢 ndm。匹配是在 Users 上完成的,而不是在 Orders 上完成的,我将探索您的解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-02-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-08
    相关资源
    最近更新 更多