【问题标题】:How to query database objects within a date period?如何查询一个日期内的数据库对象?
【发布时间】:2014-09-16 13:13:15
【问题描述】:

我想从数据库中返回所有对象,其中对象的两个 Date 字段必须在特定时间段内。

我能比用BETWEEN 为两个日期字段编写查询做得更好吗?

@Entity
public class Book {
    Date from;
    Date to;
}

//the period to search for
Date fromDate;
Date toDate;

//return all Book objects having from+to paramter laying within the period
b.from BETWEEN :fromDate AND :toDate AND b.to BETWEEN :fromDate AND :toDate

【问题讨论】:

    标签: java sql hibernate postgresql


    【解决方案1】:

    我不明白你真正需要什么,但如果我理解正确,你只需要检查日期边界。

     select * from books where b.from >= :fromDate AND b.to <= :toDate
    

    您可以假设 fromDate 小于 toDate。

    【讨论】:

    • 不,我希望 b.from 在 from+toDate 内,以及 b.to 在 from+toDate 内。
    【解决方案2】:

    我假设您的意思是严格控制,而不是两个时间段的重叠(因为您有 OVERLAPS 运算符)。

    我们先生成测试数据:

    CREATE UNLOGGED TABLE books ("from", "to") AS
    SELECT g.date::date, (g.date + random() * 100 * INTERVAL '1 day')::date
    FROM generate_series('1980-01-01', '2016-12-31', INTERVAL '1 minute') g ("date");
    SELECT 19460161
    Time: 24783.529 ms
    

    现在我看到了三个选择,但首先让我们创建我将使用的三个索引:

    CREATE INDEX point_gist
    ON books
    USING gist (point(EXTRACT(EPOCH FROM "from"), EXTRACT(EPOCH FROM "to")));
    CREATE INDEX
    Time: 242062.079 ms
    
    CREATE INDEX from_to_btree ON books USING btree ("from", "to");
    CREATE INDEX
    Time: 26107.162 ms
    
    CREATE INDEX daterange_gist ON books USING gist (daterange("from", "to", '[]'));
    CREATE INDEX
    Time: 791420.184 ms
    
    VACUUM ANALYZE books;
    VACUUM
    Time: 3000.284 ms
    

    请不要依赖这些时间;每个查询只执行一次,因为它们的性能不是我主要关心的问题。 YMMV。

    1。通常的 BETWEEN

    EXPLAIN ANALYZE
    SELECT *
    FROM books
    WHERE
        "from" BETWEEN '2000-01-01' AND '2001-01-01'
        AND
        "to" BETWEEN '2000-01-01' AND '2001-01-01';
                                                                         QUERY PLAN
    -----------------------------------------------------------------------------------------------------------------------------------------------------
     Index Only Scan using from_to_btree on books  (cost=0.44..14849.78 rows=16578 width=8) (actual time=0.033..79.268 rows=456512 loops=1)
       Index Cond: (("from" >= '2000-01-01'::date) AND ("from" <= '2001-01-01'::date) AND ("to" >= '2000-01-01'::date) AND ("to" <= '2001-01-01'::date))
       Heap Fetches: 0
     Total runtime: 93.792 ms
    (4 rows)
    

    2。 9.2+ 类型,日期范围

    EXPLAIN ANALYZE
    SELECT *
    FROM books
    WHERE daterange("from", "to", '[]') <@ daterange(date '2000-01-01', date '2001-01-01', '[]');
                                                                   QUERY PLAN
    -----------------------------------------------------------------------------------------------------------------------------------------
     Bitmap Heap Scan on books  (cost=16350.89..109027.83 rows=437996 width=8) (actual time=175.644..212.508 rows=456512 loops=1)
       Recheck Cond: (daterange("from", "to", '[]'::text) <@ '[2000-01-01,2001-01-02)'::daterange)
       ->  Bitmap Index Scan on daterange_gist  (cost=0.00..16241.39 rows=437996 width=0) (actual time=175.277..175.277 rows=456512 loops=1)
             Index Cond: (daterange("from", "to", '[]'::text) <@ '[2000-01-01,2001-01-02)'::daterange)
     Total runtime: 226.568 ms
    (5 rows)
    

    3。几何类型和运算符滥用

    EXPLAIN ANALYZE
    SELECT *
    FROM books
    WHERE
        point(EXTRACT(EPOCH FROM "from"), EXTRACT(EPOCH FROM "to"))
        <@
        box(
            point(EXTRACT(EPOCH FROM date '2000-01-01'), EXTRACT(EPOCH FROM date '2000-01-01')),
            point(EXTRACT(EPOCH FROM date '2001-01-01'), EXTRACT(EPOCH FROM date '2001-01-01'))
        );
                                                                                                        QUERY PLAN
    -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
     Bitmap Heap Scan on books  (cost=959.25..47748.30 rows=19460 width=8) (actual time=97.305..147.931 rows=456512 loops=1)
       Recheck Cond: (point(date_part('epoch'::text, ("from")::timestamp without time zone), date_part('epoch'::text, ("to")::timestamp without time zone)) <@ '(978307200,978307200),(946684800,946684800)'::box)
       ->  Bitmap Index Scan on point_gist  (cost=0.00..954.38 rows=19460 width=0) (actual time=96.926..96.926 rows=456512 loops=1)
             Index Cond: (point(date_part('epoch'::text, ("from")::timestamp without time zone), date_part('epoch'::text, ("to")::timestamp without time zone)) <@ '(978307200,978307200),(946684800,946684800)'::box)
     Total runtime: 161.947 ms
    (5 rows)
    

    这需要一点解释。因此,您通过提供 (x,y) 坐标来创建一个点,而通过提供两个点(对角)来创建一个框:((x1,y1),(x2,y2))。您可以看到该点包含在框(&lt;@)中的要求意味着该点的 x 必须位于 x1 和 x2(含)之间,而该点的 y 必须位于 y1 和 y2(含)之间。

    【讨论】:

      【解决方案3】:

      我能比使用 BETWEEN 为两个日期字段编写查询做得更好吗?

      不,这正是这样做的方式。

      【讨论】:

        【解决方案4】:

        这是一个首选的替代方案,但不太简洁。 如this thread 中所述,BETWEEN 是一个“封闭间隔,可能是日期问题”。

         SELECT from, to 
         FROM Books b
         WHERE 
         (b.from >= :fromDate AND b.from =< :toDate)
         AND
         (b.to >= :fromDate AND b.to <= :toDate); 
        

        【讨论】:

          猜你喜欢
          • 2016-01-11
          • 2018-11-18
          • 1970-01-01
          • 1970-01-01
          • 2011-09-26
          • 1970-01-01
          • 1970-01-01
          • 2012-11-17
          • 2021-10-18
          相关资源
          最近更新 更多