Arel和CTE包装查询

时间:2018-02-26 19:19:00

标签: ruby-on-rails postgresql rails-activerecord postgres-ext

我必须更新与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方法由&#34; posgres_ext&#34;提供。似乎已被遗弃的宝石。因此,我正在寻找另一种方法来构建来自rel2的关系rel

Arel docs提到了一个似乎没有帮助的案例。

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

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

2 个答案:

答案 0 :(得分:1)

由于您的CTE是非递归的,因此您可以将其重写为<div id="my-grid"></div> <script> $('#my-grid').kendoGrid({ columns: [ { field: 'firstName', title: 'First Name' }, { template: '<button class="grid-text-button">A</button><button class="grid-text-button">B</button>', title: 'Actions' } ], dataSource: [ { id: 1, firstName: 'David' }, { id: 2, firstName: 'Chuck' } ], dataBound: function(e) { var $rows = e.sender.items(); $rows.find('.grid-text-button').kendoButton(); } }); </script> 子句中的子查询。唯一的变化是Postgres的计划者会将其作为主要查询的一部分而不是单独进行优化(因为a CTE is an optimization fence)。在ActiveRecord中,这适用于我(在5.1.4上测试):

FROM

我没有看到任何方法将CTE压缩到ActiveRecord而不扩展它,就像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 那样。遗憾!

答案 1 :(得分: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