如何在没有集合的情况下使用 Eloquent Builder 进行 INTERSECT

Posted

技术标签:

【中文标题】如何在没有集合的情况下使用 Eloquent Builder 进行 INTERSECT【英文标题】:How do I make an INTERSECT using Eloquent Builder without having a collection 【发布时间】:2021-01-21 15:30:33 【问题描述】:

这就是我得到的。我们使用Illuminate\Database\Eloquent\Builder 在 Postgres 中构建物化视图

本质上是三个表,我们通过加入订阅表和电子邮件表来构建电子邮件列表,然后我们得到订阅者电子邮件的电子邮件列表。

此时,我们有了第三张表,里面有三样东西。

Variable_ID | Email | Value

预期的行为是用户选择一个变量(例如地址),然后输入一个值,然后我们根据该值匹配值。因此,我们使用该订阅/电子邮件连接表,并比较变量/电子邮件/值表并找到匹配项。

问题在于说我想这样做:

$subscriptions->leftJoin('faker_email_var_table', 'faker_email_var_table'.'.email', '=', 'emails.email') ->select('faker_email_var_table'.'.email') ->where('variable_id', '1002015')->where('value', '=', '1234 Anywhere street') ->where('variable_id', '1002707')->where('value', '=', 'Smith')

这行不通。 因为我需要匹配 variable_id: 1002015 AND value: 1234 Anywhere street 然后我需要匹配 variable_id: 1002707 AND value: Smith

在 SQL 编辑器中起作用的是 INTERSECT。

SELECT var_Email_Val_Table.email
   FROM backend.subscriptions
     JOIN backend.emails ON subscriptions.email_id = emails.id
     LEFT JOIN backend.var_Email_Val_Table ON var_Email_Val_Table.email::text = emails.email
  WHERE var_Email_Val_Table.variable_id = 1002015 AND var_Email_Val_Table.value::text = '1234 Anywhere street'::text

INTERSECT

SELECT var_Email_Val_Table.email from backend.var_Email_Val_Table 
  WHERE var_Email_Val_Table.variable_id = 1002707 AND 
var_Email_Val_Table.value::text = 'Rhodes'::text

INTERSECT

 SELECT var_Email_Val_Table.email from backend.var_Email_Val_Table 
  WHERE var_Email_Val_Table.variable_id = 1002706 AND var_Email_Val_Table.value::text = 'Engineer'::text

但是,当我尝试这样的事情时:

$subscriptions->leftJoin(var_Email_Val_Table, var_Email_Val_Table.'.email', '=', 'emails.email')
    ->select(var_Email_Val_Table.'.email')
    ->where('variable_id', '1002015')->where('value', '=', '1234 Anywhere street')
    ->intersect(DB::table(var_Email_Val_Table)->select(var_Email_Val_Table.'.email')
    ->where('variable_id', '1002707')->where('value', '=', 'Smith'));

我收到此错误: Call to undefined method Illuminate\\Database\\Eloquent\\Builder::intersect()

如果这是 php,我可以执行一系列 IF 条件来获得我的结果,但这不是 SQL 中的事情。

因此,如果有人可以帮助我了解如何在不使用集合的情况下 Eloquent INTERSECT,那就太好了!

感谢 Stack Fam!

【问题讨论】:

【参考方案1】:

我最终做的是相当具体的,我希望有更好的方法。但它有效。

我提到了这是如何构建物化视图的,其中大部分是使用 Eloquent 构建的。在某些时候,这个 Eloquent 构建器模型使用 ->toSql() 转换为原始 SQL。 所以我只是在 ToSql 之后为添加到视图中的每个附加变量连接 INTERSECT's on。

$rawQuery = $subscriptions->leftJoin('variable_relational_tbl', 'variable_relational_tbl'.'email', '=', 'emails.email')
            ->select('variable_relational_tbl'.'.email')
            ->join('emails', 'subscriptions.email_id', 'emails.id')
            ->select('emails.email', 'emails.md5 as email_md5')->toSql();

$intersect = $this->buildIntersects($query['variables']);

$query = $this->formatQuery($subscriptions);
if (! empty($intersect)) 
    $query = $query.' '.$intersect;


// INTERSECT QUERY BUILDER
protected function buildIntersects($variables)

    $interstr = ' INTERSECT ';
    $subquery = '';
    $cntr = 0;
    foreach ($variables as $var) 
        if ($cntr > 0) 
            $subquery = $interstr." SELECT `variable_relational_tbl`.email FROM`variable_relational_tbl` 
                WHERE `variable_relational_tbl`.variable_id = '$var['variable_id']' AND `variable_relational_tbl`.value = '$var['variable_value']' ";
        
    
    return $subquery;


我用循环构建 intersects 字符串 我们的变量是variable_idvariable_value。 为了清楚起见,我已经对此进行了简化以显示我所做的事情。

但我只是在 toSql() 方法之后添加了 INTERSECTS。 它不漂亮,除非你打算从中获得一个视图,否则它是行不通的。

但这就是我所做的。 相反,我找不到另一种方法来获取我想要的匹配行,除了或相交。这有点令人失望。

我需要从顶部查询中删除行并匹配 variable_relational_tbl 中的多个列

就像我需要 var_id = 123 的所有行,其中值为“1234 Example Street” AND var_id = 321 的所有行,其中值为 '1234 Example Road' 但是由于一行不能有两个不同的 var_id 的 INTERSECTS 是我能做到这一点的唯一方法。

我不知道这是否会帮助任何人,但 Eloquent 没有INTERSECT。这基本上就是这个问题的答案。

【讨论】:

【参考方案2】:

我的一个控制器中有这个,它使用EXCEPT,但您可以将其更改为相交。

我预先构建了 2 个单独的查询。比我做的:

$query = Thread::query() 
    ->fromRaw( 
        '(SELECT * FROM ((' . $unioned->toSql() . ') EXCEPT ' . $excludeExplicit->toSql() . ') AS threads) AS threads', 
        array_merge($unioned->getBindings(), $excludeExplicit->getBindings()) 
    );

我很想知道这是否是最好的方法。至少它对我有用。

【讨论】:

这就像 eloquent 不适合任何复杂的查询,但我会把它扔掉,看看它是否坚持!谢谢! 嗯,这样做的美妙之处在于使用查询构建器创建了两个单独的查询,最后这个组合查询返回给我雄辩的模型。所以它就像使用 eloquent,只是使用了大量的原始 SQL。

以上是关于如何在没有集合的情况下使用 Eloquent Builder 进行 INTERSECT的主要内容,如果未能解决你的问题,请参考以下文章

如何在没有 Laravel 尝试将其保存到数据库的情况下在 Eloquent 模型中使用受保护的属性

如何在没有 N+1 问题和加载整个模型的情况下计算 eloquent 关系?

如何在 Laravel 4 中手动创建一个新的空 Eloquent 集合

Laravel:在没有 Eloquent 的情况下在视图中显示数据

如何在给定键和值数组的情况下有效地在 Eloquent 中进行大规模更新

如何在没有任何查询过滤器的情况下使用 ReactiveMongoTemplate 计算 mongodb 集合?