简体   繁体   English

Arel和CTE包装查询

[英]Arel and CTE to Wrap Query

I have to update a search builder which builds a relation with CTE. 我必须更新一个与CTE建立关系的搜索构建器。 This is necessary because a complex relation (which includes DISTINCT, JOINs etc) is first built and then it's results have to be ordered – all in one query. 这是必要的,因为首先构建复杂关系(包括DISTINCT,JOIN等),然后必须对其结果进行排序 - 所有这些都在一个查询中。

Here's a simplified look at things: 这是对事物的简化看法:

rel = User.select('DISTINCT ON (users.id) users.*').where(<lotsastuff>)
rel.to_sql

# SELECT DISTINCT ON (users.id) users.*
#   FROM "users"
#   WHERE <lotsastuff>

rel2 = User.from_cte('cte_table', rel).order(:created_at)
rel2.to_sql

# WITH "cte_table" AS (
#   SELECT DISTINCT ON (users.id) users.*
#     FROM "users"
#     WHERE <lotsastuff>
# ) SELECT "cte_table".* FROM "cte_table"
#   ORDER BY "cte_table"."created_at" ASC

The beauty of it is that rel2 responds as expected eg to count . 它的美妙之处在于rel2按预期响应,例如count

The from_cte method is provided by the "posgres_ext" gem which appears to have been abandoned. from_cte方法由“posgres_ext”gem提供,该gem似乎已被放弃。 I'm therefore looking for another way to build the relation rel2 from rel . 因此,我正在寻找另一种从rel构建关系rel2

The Arel docs mention a case which doesn't seem to help here. Arel文档提到了一个似乎没有帮助的案例。

Any hints on how to get there? 有关如何到达那里的任何提示? Thanks a bunch! 谢谢你!


PS: I know how to do this with to queries by selecting all user IDs in the first, then build a query with IN over the IDs and order there. PS:我知道如何通过在第一个中选择所有用户ID来对查询执行此操作,然后使用IN在ID上创建查询并IN那里进行排序。 However, I'm curious whether this is possible with one query (with or without CTE) as well. 但是,我很好奇是否可以使用一个查询(有或没有CTE)。

Since your CTE is non-recursive, you can rewrite it as a subquery in the FROM clause. 由于您的CTE是非递归的,因此可以将其重写为FROM子句中的子查询。 The only change is that Postgres's planner will optimize it as part of the main query instead of separately (because a CTE is an optimization fence ). 唯一的变化是Postgres的计划者将其作为主要查询的一部分而不是单独进行优化 (因为CTE是优化围栏 )。 In ActiveRecord this works for me (tested on 5.1.4): 在ActiveRecord中,这适用于我(在5.1.4上测试):

2.4.1 :001 > rel = User.select("DISTINCT ON (users.id) users.*").where("1=1")
2.4.1 :002 > puts User.from(rel, 'users').order(:created_at).to_sql
SELECT "users".* FROM (SELECT DISTINCT ON (users.id) users.* FROM "users" WHERE (1=1)) users ORDER BY "users"."created_at" ASC

I don't see any way to squeeze a CTE into ActiveRecord without extending it though, like what postgres_ext does. 我没有看到任何方法将CTE压缩到ActiveRecord而不扩展它,就像postgres_ext那样。 Sorry! 抱歉!

From what you've mentioned, I did not understand why do you need to use CTE instead of just a nested query. 从你提到的,我不明白为什么你需要使用CTE而不仅仅是嵌套查询。

rel = User.select('DISTINCT ON (users.id) users.*').where(<lotsastuff>).arel
inner_query = Arel::Table.new(:inner_query)
composed_cte = Arel::Nodes::As.new(inner_query, rel)
select_manager = Arel::SelectManager.new(composed_cte)
rel2 = select_manager.project('COUNT(*)')
rel2.to_sql
rel3 = select_manager.order('created_at ASC')
rel3.to_sql

you can then execute that sql 然后你可以执行那个sql

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM