【问题标题】:Converting a SQL statement to rails commabd将 SQL 语句转换为 rails 命令
【发布时间】:2021-11-16 20:21:10
【问题描述】:

我有一种情况,我只需要从特定的活动记录查询响应中获取几条记录。

@annotations = Annotations.select('*', ROW_NUMBER() OVER (PARTITION BY column_a) ORDER BY column_b)

上面是@annotations 是活动记录响应的查询,我想在其上应用以下逻辑。有没有更好的方法来用rails方式编写以下逻辑?

with some_table as
(
 select *, row_number() over (partition by column_a order by column_b) rn
 from the_table
)
select * from some_table
where (column_a = 'ABC' and rn <= 10) or (column_b <> 'AAA')

【问题讨论】:

    标签: ruby-on-rails arel


    【解决方案1】:

    ActiveRecord 在其高级 API 中不提供 CTE;但是,我们可以用一点 Arel 将其作为 FROM 子句中的子查询

    annotations_table = Annotation.arel_table 
    sub_query = annotations_table.project(
      Arel.star,
      Arel.sql('row_number() over (partition by column_a order by column_b)').as(:rn)
    )
    query = Arel::Nodes::As.new(sub_query, Arel.sql(annotations_table.name))
    
    Annotation.from(query).where(
      annotations_table[:column_a].eq('ABC').and(
        annotations_table[:rn].lt(10)
      ).or(annotations_table[:column_b].not_eq('AAA'))
    )
    

    结果将是使用您的 CTE 和您描述的过滤器的 Annotation 对象的集合。

    SQL:

    select annotations.* 
    from (
     select *, row_number() over (partition by column_a order by column_b) AS rn
     from annotations
    ) AS annotations
    where (annotations.column_a = 'ABC' and annotations.rn <= 10) or (annotations.column_b <> 'AAA')
    

    注意事项:

    • 通过一些额外的工作,我们可以使它成为 CTE,但在这种情况下似乎不需要它

    • 我们也可以使用一堆技巧将这个 row_number() over (partition by column_a order by column_b) 转换为 Arel,但这似乎与问题无关。

    【讨论】:

    • 如果你有一个相当现代的 Rails 和 Ruby (2.7+) 版本,你可以使用无穷无尽的范围来创建带有哈希语法的 LT/GT/LTE/GTE 条件。 where(column_a: 'ABC', rn: ..10).or(Anotation.where('annotations.column_b &lt;&gt; 'AAA')')。在旧版本中,您可以使用10..Float::INFINITY
    • @max 我总是忘记这一点,因为它对我来说仍然感觉很尴尬而且几乎很脏。 Qualified Range 表示介于之间,无穷 Range 大于等于,无开始范围小于等于,三点去掉相等,魔力太大。我几乎更喜欢谓词解析,例如where(column_a: 'ABC', rn: {lt_eq: 10})
    • 是的,这有点尴尬,如果..... 使用LTE,我永远记不得了。我希望where 占用一个块并产生 arel 表或选择经理或其他东西,这样你就可以做where { |a| a[:rn].gt(10) }。有点像 Squeel,但元编程不那么疯狂。
    • @max 同意特别是因为sequel 提供这种类型的接口,ransack 提供像rn_lteq: 10 这样的复合查找器
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-18
    • 2013-02-15
    • 2019-07-03
    • 1970-01-01
    • 2023-03-15
    相关资源
    最近更新 更多