首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >有没有办法将表名自动添加到能言善辩的查询方法中?

有没有办法将表名自动添加到能言善辩的查询方法中?
EN

Stack Overflow用户
提问于 2018-01-14 03:52:42
回答 3查看 3.2K关注 0票数 23

我正在Laravel 5.5上开发一个应用程序,我遇到了一个具有特定查询范围的问题。我有以下表结构(省略了一些字段):

orders
---------
id
parent_id
status

parent_id列引用同一个表中的id。我有这个查询范围来过滤没有任何子项的记录:

public function scopeNoChildren(Builder $query): Builder
{
    return $query->select('orders.*')
        ->leftJoin('orders AS children', function ($join) {
            $join->on('orders.id', '=', 'children.parent_id')
                ->where('children.status', self::STATUS_COMPLETED);
        })
        ->where('children.id', null);
}

这个作用域单独使用时工作得很好。但是,如果我尝试将它与任何其他条件组合在一起,它会抛出SQL异常:

Order::where('status', Order::STATUS_COMPLETED)
    ->noChildren()
    ->get();

导致这样的结果:

SQLSTATE23000: where子句中的完整性约束冲突: 1052列'status‘不明确

我找到了两种方法来避免这个错误:

解决方案#1:在所有其他条件前加上表名

这样做是可行的:

Order::where('orders.status', Order::STATUS_COMPLETED)
    ->noChildren()
    ->get();

但我不认为这是一个好的方法,因为它不清楚表名是必需的,以防其他开发人员甚至我自己将来再次尝试使用该作用域。他们可能最终会发现这一点,但这似乎不是一个好的做法。

解决方案#2:使用子查询

我可以在子查询中将不明确的列分开。不过,在这种情况下,随着表的增长,性能将会下降。

不过,这是我正在使用的策略。因为它不需要对其他作用域和条件进行任何更改。至少不是我现在应用它的方式。

public function scopeNoChildren(Builder $query): Builder
{
    $subQueryChildren = self::select('id', 'parent_id')
        ->completed();
    $sqlChildren = DB::raw(sprintf(
        '(%s) AS children',
        $subQueryChildren->toSql()
    ));

    return $query->select('orders.*')
        ->leftJoin($sqlChildren, function ($join) use ($subQueryChildren) {
            $join->on('orders.id', '=', 'children.parent_id')
                ->addBinding($subQueryChildren->getBindings());
         })->where('children.id', null);
}

完美的解决方案

我认为,能够在不使用表名作为前缀的情况下使用查询,而不依赖子查询,这将是一个完美的解决方案。

这就是为什么我要问:有没有办法自动将表名添加到雄辩的查询方法中?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2018-06-04 22:57:48

我会使用一个关系:

public function children()
{
    return $this->hasMany(self::class, 'parent_id')
        ->where('status', self::STATUS_COMPLETED);
}

Order::where('status', Order::STATUS_COMPLETED)
    ->whereDoesntHave('children')
    ->get();

这将执行以下查询:

select *
from `orders`
where `status` = ?
  and not exists
    (select *
     from `orders` as `laravel_reserved_0`
     where `orders`.`id` = `laravel_reserved_0`.`parent_id`
       and `status` = ?)

它使用子查询,但它很简短,简单,并且不会引起任何歧义问题。

我不认为性能会是一个相关的问题,除非你有数百万行(我假设你没有)。如果将来子查询性能会有问题,您仍然可以返回到连接解决方案。在此之前,我将重点关注代码的可读性和灵活性。

重用关系的一种方法(如OP所指出的):

public function children()
{
    return $this->hasMany(self::class, 'parent_id');
}

Order::where('status', Order::STATUS_COMPLETED)
    ->whereDoesntHave('children', function ($query) {
        $query->where('status', self::STATUS_COMPLETED);
    })->get();

或者一种有两种关系的方式:

public function completedChildren()
{
    return $this->children()
        ->where('status', self::STATUS_COMPLETED);
}

Order::where('status', Order::STATUS_COMPLETED)
    ->whereDoesntHave('completedChildren')
    ->get();
票数 17
EN

Stack Overflow用户

发布于 2018-06-04 20:51:37

在MySQL中,有两种很好的方法来查找邻接表中的叶节点(行)。一个是LEFT-JOIN-WHERE-NULL方法(antijoin),这就是您所做的。另一个是NOT EXISTS子查询。这两种方法应该具有可比较的性能(理论上它们做的是完全相同的)。但是,子查询解决方案不会在结果中引入新列。

return $query->select('orders.*')
    ->whereRaw("not exists (
        select *
        from orders as children
        where children.parent_id = orders.id
          and children.status = ?
    )", [self::STATUS_COMPLETED]);
票数 3
EN

Stack Overflow用户

发布于 2018-06-03 03:56:26

您必须创建一个扩展原始Illuminate\Database\Query\BuilderSomeDatabaseBuilder,以及一个扩展Illuminate\Database\Eloquent\BuilderSomeEloquentBuilder,最后创建一个扩展Illuminate\Database\Eloquent\ModelBaseModel,并覆盖这些方法:

/**
 * @return SomeDatabaseBuilder
 */
protected function newBaseQueryBuilder()
{
    $connection = $this->getConnection();

    return new SomeDatabaseBuilder(
        $connection, $connection->getQueryGrammar(), $connection->getPostProcessor()
    );
}

/**
 * @param \Illuminate\Database\Query\Builder $query
 * @return SameEloquentBulder
 */
public function newEloquentBuilder($query)
{
    return new SameEloquentBulder($query);
}

然后,在SomeDatabaseBuilderSameEloquentBulder上,将方法更改为默认情况下限定列(或将其设为可选)。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/48243734

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档