【问题标题】:Rails ActiveRecord - Order query by matching numberRails ActiveRecord - 按匹配号查询订单
【发布时间】:2020-12-12 16:23:05
【问题描述】:

我有一个名为Location 的模型。此表有 2 个字段,我想按以下顺序排序:rating(整数)和 name(字符串)。

我想按姓名字母顺序排序,但如果评分是 5,我希望该评分排在第一位。如果评分是 5 以外的任何其他值,我希望它们在 5 之后按字母顺序公正地显示。

这可能与 SQL 有关吗?我知道我显然可以使用 Ruby 的 Array#sort 来执行此操作,但我想将其作为查询来执行。

【问题讨论】:

    标签: sql ruby-on-rails ruby postgresql activerecord


    【解决方案1】:

    是的,您可以使用一些 SQL 来完成此操作。您需要在 ORDER BY 中包含 case 表达式:

    case rating when 5 then 0 else 1 end
    

    这大致相当于 Ruby 中的 rating == 5 ? 0 : 1,因此它将所有评级为 5 的位置放在首位,其他位置放在后面。然后按name排序:

    Location.order(Arel.sql('case rating when 5 then 0 else 1 end, name'))
    

    当然,您可以将其隐藏在一个范围内,以便于阅读和理解。

    【讨论】:

    • 非常感谢。当我有你时,是否可以限制首先列出多少个 5?意思是如果有 10 个实例的评分为 5,只放其中 3 个,然后将其他实例与其余实例排序?
    • 这是可能的,但是使用 ActiveRecord(或任何 ORM)开始变得很麻烦,您必须决定如何选择最先出现的三个。
    • 现在,我只想按名称的字母顺序选择前三个。任何帮助表示赞赏。
    【解决方案2】:

    您可以使用窗口函数 ROW_NUMBER 为每个等级分配一个“序列”值。然后应用 case 语句并为该“序列”添加谓词以满足您的限制。

    select rank,name 
      from ( select rank, name 
                  , row_number() over (partition by rank
                                           order by rank, name ) rn  
               from location
           ) l
     order by case when rank=5 and rn<=3 then 0 else 1 end   
            , name; 
    

    【讨论】:

    • 您可以通过说select * from location order by case when rank = 5 and row_number() over (order by rank, name) &lt;= 3 then 0 else 1 end, name 来避免派生表。 DT 对 ORM 来说通常很痛苦。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多