如何以某种方式订购这些SQL结果?

时间:2012-10-04 19:29:07

标签: sql postgresql sqlalchemy

我正在制作一种活动日历app。用户主页上显示了两种类型的事件:

  1. 附近活动 - 在接下来的3天内或在过去1小时内安排日期的活动。
  2. 正常事件 - 未安排日期或未来超过3天的日程安排日期。
  3. 这是事件表:id, user_id, content, occurs_at, created_at

    目前,我正在显示按创建日期排序的这些事件,以便首先显示新添加的内容。

    我想要的是首先显示所有附近的事件并按计划日期排序,然后显示正常事件并按创建日期排序。我认为它可以带来更好的用户体验,但我不知道如何去做。

    更新

    结束这个查询,这是几个答案的组合。

    ORDER BY COALESCE(occurs_at > :min_time AND occurs_at < :max_time, 0) DESC, occurs_at ASC, created_at DESC

4 个答案:

答案 0 :(得分:2)

order by子句可以包含任意逻辑,而不仅仅是字段名称,因此,在伪代码中,您可以拥有

SELECT ...
...
ORDER BY (date_diff(occurs_at, now) <= '3 days'), occurs_at DESC, created_at DESC

第一个子句强制将接下来3天内发生的所有事件发送到列表顶部。然后第二个子句按降序排序,然后其他所有内容按created_at日期排序。

答案 1 :(得分:2)

正如@MarcB所提到的,你可以通过表达式进行排序,但对于PostgreSQL,表达式看起来有些不同;

SELECT * FROM Events
ORDER BY CASE WHEN (occurs_at - '3 days'::interval) < CURRENT_TIMESTAMP 
         THEN occurs_at
         ELSE '9999-01-01'::timestamp
         END, created_at;

SQLfiddle demo

注意,'9999-01-01'在某种程度上是一个任意选择的日期,如果有人知道“maxdate”的PostgreSQL常量,我会感兴趣:)

答案 2 :(得分:2)

详细格式

更容易解释/理解。

WITH x AS (
   SELECT *
        , COALESCE(occurs_at <= (now() + interval '3 days'), FALSE) AS nearby
   FROM   events
   WHERE  occurs_at >= now() - interval '1 hour' -- exclude older events
      OR  occurs_at IS NULL
   )      -- CTE is not strictly necessary, just to make ORDER BY clearer
SELECT *
FROM   x
ORDER  BY
       nearby DESC
     , CASE WHEN nearby THEN occurs_at ELSE created_at END

在问题中没有明确定义,但提到的“无日期日期”让我假设occurs_at可以是NULL。因此,使用COALESCEFALSENULL的排序方式会有所不同。

  • 首先,按CTE nearby计算的x排序。
  • 接下来,按相应日期(occurs_atcreated_at)排序。
    • occurs_ at NULL nearby次活动无法{。}}。
    • 我假设created_at定义为NOT NULL。否则,您必须决定将NULL值放在哪里。

此处的其他答案建议在将其与常量进行比较之前根据列计算值,这可能导致非常差的性能。例如:

date_diff(d,occurs_at, now) <= 3  -- date_diff is also tSQL, not Postgres

如果可以避免,请执行此操作。它迫使Postgres计算每一行的值,使得无法使用普通索引。此recent closely related answer中有更多信息。

简易版

更短,更快一点。

SELECT *
FROM   events
WHERE  occurs_at >= now() - interval '1h'
   OR  occurs_at IS NULL
ORDER  BY
       COALESCE( occurs_at <= (now()::date + 3), FALSE) DESC
     , CASE WHEN occurs_at <= (now()::date + 3)
            THEN occurs_at ELSE created_at END;

我拿了@Joachim的sqlfiddle,简化了设置,添加了丢失的案例(未安排,过去发生过)并对其进行了查询:
-> sqlfiddle

答案 3 :(得分:1)

我会通过一个字段来扩展你的SELECT子句,确定它是否附近&#34; (及时,不是地方)

SELECT
id, user_id, content, occurs_at, created_at,
case when date_diff(d,occurs_at, now) <= 3 then 1 else 0 end as isNearby
...

但我不确定您的语法,这适用于MS SQL Server