3

我必须更新与 CTE 建立关系的搜索构建器。这是必要的,因为首先构建了一个复杂的关系(包括 DISTINCT、JOIN 等),然后必须对其结果进行排序——所有这些都在一个查询中。

以下是对事物的简化看法:

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

它的美妙之处在于rel2响应如预期的那样count

from_cte方法由似乎已被放弃的“posgres_ext”gem 提供。因此,我正在寻找另一种rel2rel.

Arel 文档提到了一个在这里似乎没有帮助的案例。

关于如何到达那里的任何提示?非常感谢!


PS:我知道如何通过首先选择所有用户 ID 来对查询执行此操作,然后使用IN这些 ID 构建一个查询并在那里订购。但是,我很好奇这是否也可以通过一个查询(有或没有 CTE)来实现。

4

2 回答 2

2

由于您的 CTE 是非递归的,因此您可以将其重写为FROM子句中的子查询。唯一的变化是 Postgres 的计划器会将其作为主查询的一部分而不是单独进行优化(因为CTE 是优化围栏)。在 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

我看不出有任何方法可以将 CTE 压缩到 ActiveRecord 中而不扩展它,就像它所做的postgres_ext那样。对不起!

于 2018-03-08T17:08:53.080 回答
0

从您提到的内容来看,我不明白您为什么需要使用 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

然后你可以执行那个sql

于 2018-02-26T19:34:54.477 回答