如何在查询生成器中有效地转换嵌套 SQL [重复]

Posted

技术标签:

【中文标题】如何在查询生成器中有效地转换嵌套 SQL [重复]【英文标题】:how to efficiently convert a nested SQL in Query builder [duplicate] 【发布时间】:2021-09-16 09:37:43 【问题描述】:

我有一个嵌套的 sql 查询

SELECT
   city_id,
   town_id,
   SUM(IF(total_visit >= 2, 1, 0)) visited_twice,
   SUM(IF(total_visit >= 3, 1, 0)) visited_thrice 
FROM
   (
      SELECT
         c.city_id,
         c.town_id,
         c.id,
         COUNT(v.id) AS total_visit 
      FROM
         VISITS v 
         LEFT JOIN
            CUSTOMERS c 
            ON v.customer_id = c.id 
      WHERE
         c.customer_type = 1 
         AND MONTH(v.visit_date) = 6 
         AND YEAR(v.visit_date) = 2021 
      GROUP BY
         c.town_id,
         c.id,
         MONTH(v.visit_date),
         YEAR(v.visit_date) 
      HAVING
         total_visit > 1
   )
GROUP BY
   town_id

如何转换为查询构建器模式以使代码可读?

我已尝试转换为普通查询的查询生成器,但正在寻找嵌套查询的建议。

编辑

$visitTable = Visit::$TABLE_NAME;
$customerTable = Customer::$TABLE_NAME;

$sub = Visit::with($with)
    ->selectRaw("$customerTable.city_id, $customerTable.town_id, 
                     $customerTable.id, COUNT($visitTable.id) total_visit")
    ->leftJoin("$customerTable", "$customerTable.id", '=', "$visitTable.customer_id")
    ->where("$customerTable.customer_type_id", 1)
    ->whereMonth("$visitTable.visit_date", $month)
    ->whereYear("$visitTable.visit_date", $year)
    ->groupBy("$customerTable.town_id, MONTH($visitTable.visit_date), YEAR($visitTable.visit_date)")
    ->havingRaw('total_visit > 1');

$query = DB::table( DB::raw("($sub->toSql()) as sub") )
    ->selectRaw("city_id, town_id,
                SUM(IF(total_visit >= 2, 1, 0)) visited_twice, SUM(IF(total_visit >= 3, 1, 0)) visited_thrice ")
    ->mergeBindings($sub->getQuery())
    ->groupBy("town_id");

但最终还是这样

"connection": ,
    "grammar": ,
    "processor": ,
    "bindings": 
        "select": [],
        "join": [],
        "where": [
            1,
            "6",
            "2021"
        ],
        "having": [],
        "order": [],
        "union": []
    ,
    "aggregate": null,
    "columns": [
        
    ],

【问题讨论】:

【参考方案1】:

您可以使用以下任何语法选项轻松地从子查询表中查询

DB::table(Closure, alias) DB::table(Builder, alias) DB::query()->from(Closure, alias) DB::query()->from(Builder, alias)

这里,我使用的是第二个选项。

// Build the subquery first (without getting the results)
$visits_sub = DB::table('VISITS', 'v')
    ->select('c.city_id', 'c.town_id', 'c.id')
    ->selectRaw('count(v.id) as total_visit')
    ->leftJoin('CUSTOMERS as c', 'v.customer_id', 'c.id')
    ->where('c.customer_type', 1)
    ->whereMonth('v.visit_date', 6)
    ->whereYear('v.visit_date', 2021)
    ->groupByRaw('c.town_id, c.id, month(v.visit_date), year(v.visit_date)')
    ->having('total_visit', '>', 1);

// Use the built subquery as the table
$query = DB::table($visits_sub, 'visits')
    ->select('city_id', 'town_id')
    ->selectRaw('sum(if(total_visits >= 2, 1, 0) as visited_twice')
    ->selectRaw('sum(if(total_visits >= 3, 1, 0) as visited_thrice')
    ->groupBy('town_id');

// you can verify the sql by dumping the query
$query->dump();

$results = $query->get();

如果你想强制结果为Visit 模型,你需要使用->query()->from(...) 语法。

$query = Visit::query()
    ->from($visits_sub, 'visits')
    // rest should be the same.

【讨论】:

感谢您的解释。但为什么我收到Object of class Illuminate\Database\Query\Builder could not be converted to string 你在哪里得到这个错误? 基本上,我在存储库模式和存储库级别使用此查询构建器。当我在邮递员上测试这个 api 时,我得到了这个。 可能取决于 laravel 版本。 @shaedrich 那么我该如何解决这个问题?我已经阅读了几篇关于 mergeBindings 的文章,但我已经完成了相应的操作。对我没用。

以上是关于如何在查询生成器中有效地转换嵌套 SQL [重复]的主要内容,如果未能解决你的问题,请参考以下文章

SQL 查询有效地选择不完美的重复项

Azure 数据工厂:如何在转换数据流中实现嵌套 sql 查询

从 Excel VBA 运行嵌套的 Access SQL 查询

使用嵌套行(类型 STRUCT)对表 SQL 进行重复数据删除

在 SQL Server 视图中有效地将行转换为列

如何有效地删除列表列表中的连续重复项?