如何根据两个条件过滤列时写Postgres查询?

时间:2012-10-15 18:15:48

标签: sql postgresql nested-queries

我在Postgres中有以下widgets表:

enter image description here

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

  1. 代表具有当前最早加载时间的窗口小部件的widgets记录;或
  2. 如果今天没有加载widgets,那么加载时间最早的那个(整个表格)
  3. 所以,使用上面的图片:

    • 查询将首先尝试返回今天首先加载的小部件(如果存在这样的小部件)。在这种情况下,今天只加载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 个答案:

答案 0 :(得分:3)

共计一行

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

..首先有效地排序今天的时间戳(+不存在的未来),因为boolean表达式已排序FALSE - &gt; TRUE - &gt; 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进行测试,是否使用它。应该是,我没有测试。如果没有,就没有意义了。

答案 1 :(得分:1)

使用union来处理逻辑,将查询分成两个查询并not exists()查询:

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

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

答案 2 :(得分:1)

SQL Fiddle

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

答案 3 :(得分:0)

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

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