3

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

  1. 附近的活动 - 预定日期在未来 3 天内或过去 1 小时内的活动。
  2. 正常事件 - 没有计划日期或计划日期超过未来 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

4 回答 4

2

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

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

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

于 2012-10-04T19:36:14.477 回答
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 演示

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

于 2012-10-04T20:23:18.720 回答
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. 所以使用COALESCE, orFALSENULL会以不同的方式排序。

  • 首先,nearby在 CTE 中计算order by 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 为每一行计算一个值,并且无法使用普通索引。最近密切相关的答案中的更多信息。

简单版

短一点,快一点。

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

于 2012-10-04T21:55:38.650 回答
1

我会通过一个字段来扩展您的 SELECT 子句,以确定它是否在“附近”(及时,而不是地点)

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

于 2012-10-04T19:40:43.900 回答