【问题标题】:Time based priority in Active Record QueryActive Record 查询中基于时间的优先级
【发布时间】:2012-10-01 19:09:47
【问题描述】:

我有一个包含工作列表的表,显示时通常按 created_at 字段降序排列。我正在添加一个“特色”布尔标志,这将使客户能够获得更多关于他们的工作列表的可见性。如果工作不到 X 天,我希望将精选列表固定在搜索结果的顶部。我将如何通过现有查询进行修改以支持这一点?

Jobs.where("expiration_date >= ? and published = ?", Date.today, true).order("created_at DESC") 

当前查询拉回所有当前发布的作业,按 created_at 排序。

【问题讨论】:

    标签: sql ruby-on-rails postgresql activerecord


    【解决方案1】:

    与其他一些数据库(如 Oracle)不同,PostgreSQL 具有功能齐全的 boolean 类型。您可以直接ORDER BY 子句中使用它,而无需应用 CASE 语句 - 这对于更复杂的情况非常有用。

    boolean 值的排序顺序是:

    FALSE -> TRUE -> NULL
    

    如果您ORDER BY bool_expressionDESC,则将顺序颠倒为:

    NULL -> TRUE -> FALSE
    

    如果您想首先使用TRUE,最后使用NULL,请使用NULLS LAST clause of ORDER BY

    ORDER BY (featured AND created_at > now() - interval '11 days') DESC NULLS LAST  
           , created_at DESC
    

    当然,NULLS LAST 仅在 featuredcreated_at 可以NULL 时才相关。如果列被定义为NOT NULL,那么不要打扰。

    另外,FALSE 将排在NULL 之前。如果你不想区分这两者,你要么回到CASE声明,要么你可以抛出NULLIF()COALESCE()

    ORDER BY NULLIF(featured AND created_at > now() - interval '11 days'), FALSE)
                                                                    DESC NULLS LAST
           , created_at DESC
    

    性能

    注意,我是如何使用的:

    created_at > now() - interval '11 days'
    

    不是

    now() - created_at < interval '11 days'
    

    在第一个示例中,右侧的表达式是一个计算一次的常数。然后可以使用索引来查找匹配的行。效率很高。

    后者通常不能与索引一起使用。必须为每一行计算一个值,然后才能根据右侧的常量表达式检查它。如果可以避免,请不要这样做。永远!

    【讨论】:

    • 这是一个更好的方法,我总是可以指望你纠正我的 (Postgre)?SQL 脑损伤:) 你不必担心 Rails 应用程序中 created_at 中的 NULL,那不应该(哈哈)发生。
    • 我的想法被正式吹响了 - 做到了select id, featured, created_at, title from jobs where published = true and expiration_date &gt;= '10/1/2012' order by (featured and created_at &gt; now() - interval '2 days') desc nulls last, created_at desc;
    【解决方案2】:

    不确定你想在这里实现什么。

    我猜你会对结果进行分页。如果是这样,并且您希望始终在顶部显示特色作业,而不管页面如何,那么您应该分别从数据库中提取它们。如果您只想在第一页显示它们,请按published 订购,如下所示:

    Jobs.where("expiration_date >= ?", Date.today).order("published DESC, created_at DESC") 
    

    如果你想单独拉它们:

    @featured_jobs = Jobs.where("expiration_date >= ? and published = ?", Date.today, true).order("created_at DESC")
    @regular_jobs = Jobs.where("expiration_date >= ? AND published = ?", Date.today, false).order("created_at DESC") #Paginate them too ... depends on the gem you're using
    

    【讨论】:

    • 谢谢安东尼。我的目标是仅在不到 7 天的情况下将精选工作固定在顶部。之后,它们属于常规 created_at 排序。希望在一个查询而不是两个查询中做到这一点。如果没有人在单个查询中提供解决方案,我会将其标记为已接受的答案
    • 想不出一个单一的 SQL 请求可以处理这个......无论如何,如果你添加分页,我认为你需要单独获得特色工作!
    猜你喜欢
    • 2021-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多