Laravel 多重计数多重连接

Posted

技术标签:

【中文标题】Laravel 多重计数多重连接【英文标题】:Laravel multiple count on multiple join 【发布时间】:2021-07-29 20:06:16 【问题描述】:

我有四个表套接字,办公室,容器,项目我需要将这些表与 primey 表套接字连接,并通过 socket.id 在每个表组中添加计数。

$data['socjetsreport'] = DB::table('socket')
    ->limit(5)
    ->join('attached', 'attached.socket_id', '=', 'socket.id', 'left outer')
    ->join('office', 'office.socket_id', '=', 'socket.id', 'left outer')
    ->join('container', 'container.socket_id', '=', 'socket.id', 'left outer')
    ->join('project', 'project.socket_id', '=', 'socket.id', 'left outer')
    ->join('users', 'users.id', '=', 'socket.employee_id', 'left outer')
    ->select('socket.id as id', 'socket.name as name',  'users.name as uname', 
        DB::raw("count(attached.socket_id) as attccount"))
    ->groupBy('socket.id')
    ->get();

我尝试在 mysql 查询中应用它,它运行良好,但在 laravel 中它只是给我错误的计数。

这是我的 MySQL 查询:

SELECT
    users.name,
    COUNT(*) AS attached,
    (
        SELECT COUNT(*) FROM socket
        LEFT OUTER JOIN container ON socket.id = container.socket_id
        WHERE socket.id = @id
    ) AS container,
    (
        SELECT COUNT(*) FROM socket
        LEFT OUTER JOIN project ON socket.id = project.socket_id
        WHERE socket.id = @id
    ) AS project, 
    (
        SELECT COUNT(*) FROM socket
        LEFT OUTER JOIN office ON socket.id = office.socket_id
        WHERE socket.id = @id
    ) AS office 
FROM socket
LEFT OUTER JOIN attached ON socket.id = attached.socket_id
LEFT OUTER JOIN users ON socket.employee_id = users.id WHERE socket.id = @id
GROUP BY users.name

【问题讨论】:

【参考方案1】:

我通过使用这个查询来解决它

$socjetsreport  = DB::table('socket')
    ->select("socket.id", "socket.name", 
    DB::raw("(SELECT COUNT(id) AS attchedcount FROM attached WHERE attached.socket_id=socket.id) as attchedcount"), 
    DB::raw("(SELECT COUNT(id) AS officecount FROM office WHERE office.socket_id=socket.id) as officecount"),
    DB::raw("(SELECT COUNT(id) AS containercount FROM container WHERE container.socket_id=socket.id) as containercount"),
    DB::raw("(SELECT COUNT(id) AS projectcount FROM project WHERE project.socket_id=socket.id) as projectcount"),
    DB::raw("(SELECT users.name AS employname FROM users WHERE users.id=socket.employee_id) as employname")
    )
    ->orderBy('socket.id', 'asc')
    ->get();

【讨论】:

【参考方案2】:

由于LEFT OUTER JOIN is the same as LEFT JOIN,您可以只使用查询构建器的leftJoin() 方法。

对于SELECT 语句中的所有子查询,您应该使用查询构建器的selectSub() 方法。

    /**
     * Add a subselect expression to the query.
     *
     * @param  \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder|string  $query
     * @param  string  $as
     * @return $this
     *
     * @throws \InvalidArgumentException
     */
    public function selectSub($query, $as)
    
        [$query, $bindings] = $this->createSub($query);

        return $this->selectRaw(
            '('.$query.') as '.$this->grammar->wrap($as), $bindings
        );
    

生成的查询如下所示:

use Illuminate\Database\Query\Builder;

$data['socjetsreport'] = DB::table('socket')
    ->select('users.name')
    ->selectRaw('count(*) as attached')
    ->selectSub(
        function (Builder $query) use ($socketId) 
            return $query->selectRaw('count(*)')->from('socket')
                         ->leftJoin('container', 'socket.id', 'container.socket_id')
                         ->where('socket.id', $socketId);
        ,
        'container'
    )
    ->selectSub(
        function (Builder $query) use ($socketId) 
            return $query->selectRaw('count(*)')->from('socket')
                         ->leftJoin('project', 'socket.id', 'project.socket_id')
                         ->where('socket.id', $socketId);
        ,
        'project'
    )
    ->selectSub(
        function (Builder $query) use ($socketId) 
            return $query->selectRaw('count(*)')->from('socket')
                         ->leftJoin('office', 'socket.id', 'office.socket_id')
                         ->where('socket.id', $socketId);
        ,
        'office'
    )
    ->leftJoin('attached', 'socket.id', 'attached.socket_id')
    ->leftJoin('users', 'socket.employee_id', 'users.id')
    ->where('socket.id', $socketId)
    ->groupBy('users.name')
    ->get();

如果你挂断了使用LEFT OUTER JOIN,或者像这样。

use Illuminate\Database\Query\Builder;

$data['socjetsreport'] = DB::table('socket')
    ->select('users.name')
    ->selectRaw('count(*) as attached')
    ->selectSub(
        function (Builder $query) use ($socketId) 
            return $query->selectRaw('count(*)')->from('socket')
                         ->join('container', 'socket.id', '=', 'container.socket_id', 'left outer')
                         ->where('socket.id', $socketId);
        ,
        'container'
    )
    ->selectSub(
        function (Builder $query) use ($socketId) 
            return $query->selectRaw('count(*)')->from('socket')
                         ->join('project', 'socket.id', '=', 'project.socket_id', 'left outer')
                         ->where('socket.id', $socketId);
        ,
        'project'
    )
    ->selectSub(
        function (Builder $query) use ($socketId) 
            return $query->selectRaw('count(*)')->from('socket')
                         ->join('office', 'socket.id', '=', 'office.socket_id', 'left outer')
                         ->where('socket.id', $socketId);
        ,
        'office'
    )
    ->join('attached', 'socket.id', '=', 'attached.socket_id', 'left outer')
    ->join('users', 'socket.employee_id', '=', 'users.id', 'left outer')
    ->where('socket.id', $socketId)
    ->groupBy('users.name')
    ->get();

php 7.4 的速记闭包可以使那些 selectSub 语句看起来更小

    ->selectSub(
        fn(Builder $query) => $query->selectRaw('count(*)')->from('socket')
                                    ->leftJoin('container', 'socket.id', 'container.socket_id')
                                    ->where('socket.id', $socketId),
        'container'
    )

您可以通过将->get() 替换为toSql() 并转储结果来尝试自己检查SQL。

【讨论】:

以上是关于Laravel 多重计数多重连接的主要内容,如果未能解决你的问题,请参考以下文章

多重连接 laravel 雄辩

Laravel 自定义多重身份验证

Laravel 8 工厂的多重关系

如何在 laravel 5.2 中使用多重身份验证 [关闭]

Laravel 多重关系

Laravel 模拟多重依赖