如何在日期期间查询数据库对象?

时间:2014-09-16 13:13:15

标签: java sql hibernate postgresql

我想从数据库中返回所有对象,其中对象的两个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

4 个答案:

答案 0 :(得分:1)

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

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

您可以假设fromDate小于toDate。

答案 1 :(得分:1)

我假设你真的是指严格控制,而不是两段时间的重叠(因为你有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))。您可以看到框中包含该点的要求(<@)意味着该点的x必须介于x1和x2之间(包括x和x2),并且该点的y必须介于y1和y2之间,包括

答案 2 :(得分:0)

这是一种首选替代方案,但不太适合。  BETWEEN是一个关闭的间隔,可能是日期问题。如this thread中所述。

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

答案 3 :(得分:0)

  

我能为两个日期字段用BETWEEN编写查询吗?

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