2

widgets在 Postgres 中有下表:

在此处输入图像描述

(该屏幕截图是它的粗略 Excel 表示。)我正在尝试编写一个将返回我的 SQL 查询:

  1. widgets表示具有今天最早加载时间的小部件的记录;或者
  2. 如果widgets今天没有加载,则加载时间最早的那个(对于整个表)

因此,使用上图:

  • 查询将首先尝试返回今天首先加载的小部件(如果存在这样的小部件)。在这种情况下,今天仅加载了 id 为 3 和 5003094(分别)的小部件。在这两个中,widget_id = 3比另一个更早加载,因此这是查询将返回的记录。
  • 但是,如果我们假设这两个小部件不在表中,而且今天没有加载任何小部件,那么查询将返回widget_id = 1,因为它是在 2010 年加载的。

这是我对查询的初步尝试:

SELECT
    MIN(w.loaded_date_time)
FROM
    widgets w
WHERE
    w.loaded_date_time >= now()
    OR
    1=1

但是,我马上就知道这在语法上是不正确的。有任何想法吗?提前致谢!

4

4 回答 4

3

共一排

SELECT *
FROM   widgets
ORDER  BY loaded_date_time < now()::date, loaded_date_time
LIMIT  1;

..首先有效地对今天的时间戳(+不存在的未来)进行排序,因为boolean表达式是排序的FALSE-> TRUE-> NULL

如果未来的日期是可能的:

ORDER  BY
       (loaded_date_time::date = now()::date) DESC NULLS LAST
      ,loaded_date_time

NULLS LAST仅当可以为 NULL 时才相关loaded_date_time,这应该是不允许的。在这种情况下删除该子句。

每个小部件一行

SELECT DISTINCT ON (widget_id)
       widget_id, loaded_date_time
FROM   widgets
ORDER  BY
       widget_id
      ,(loaded_date_time::date = now()::date) DESC
      ,loaded_date_time;

为什么以及如何工作?

  • 关于DISTINCT ON
  • ORDER BY子句中......
    首先widget_id,显然 - 必须匹配DISTINCT子句。
    ...然后首先对“今天”的记录进行排序。如果没有,其他记录会自动上移。
    ...最后,较早的记录首先排序。

    因此,所需的行首先出现并由 挑选DISTINCT。一口气全部搞定。

  • 如果将表达式重写(loaded_date_time::date = now()::date)

    (loaded_date_time >= now()::date AND
     loaded_date_time < (now()::date + 1))   -- note: < not: <=
    

.. 在 . 上使用普通索引可能会更快loaded_date_time。因为,如果等号两边都有表达式,那么你肯定根本不能使用普通索引。你应该有一个关于 widget_id 的索引(显然),可能还有一个索引loaded_date_time

CREATE INDEX foo_idx ON widgets (loaded_date_time)

多列索引可能会快一点:

CREATE INDEX foo_idx ON widgets (widget_id, loaded_date_time);

用 测试EXPLAIN ANLYZE,是否被使用。应该是的,我没有测试。如果没有,那就没有意义了。

于 2012-10-15T18:38:10.507 回答
1

将您的查询分成两个查询和union它们,使用 anot exists()来处理逻辑:

select * from widget
where <primary criteria>
union
select * from widget
where <secondary criteria>
and not exists (
    select * from widget
    where <primary criteria>
)

像这样编码也很容易阅读和维护,特别是如果您稍后添加另一种选择类别的方式。

于 2012-10-15T18:20:16.250 回答
1

这是一个排名功能的应用程序。关键是顺序:

select w.*
from (select w.*,
             row_number() over (partition by widget_id
                                order by isToday, datetime desc) as seqnum
      from (select w.*,
                   (case when cast(widget_loaded_date_time as date) = cast(now() as date)
                         then 0
                         else 1
                    end) as isToday
            from widgets w
           ) w
     ) w
where seqnum = 1
于 2012-10-15T18:30:36.967 回答
1

SQL小提琴

select w.id, w.name, w.loaded_date_time
from (
    select min(loaded_date_time) loaded_date_time
    from widgets
    where loaded_date_time::date = current_date
    union
    select min(loaded_date_time) loaded_date_time
    from widgets
    order by loaded_date_time desc
    limit 1
) s inner join widgets w on w.loaded_date_time = s.loaded_date_time
于 2012-10-15T18:32:20.337 回答