如何在没有集合的情况下使用 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_id
和variable_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 的情况下在视图中显示数据