HOWTO · Postgres
PostgreSQL 中日期范围之间的查询
本文介绍如何筛选 PostgreSQL 的 DATE 值:使用滚动时间间隔、包含两端的固定日期边界,以及 daterange 包含查询。
本页内容
按日期筛选 PostgreSQL 资料列时,滚动时间范围可用比较条件,固定且包含两端的边界可用 BETWEEN,纯日期范围则可用 daterange 包含查询。以下示例使用 DATE 列。DATE 不包含具体时间;筛选时间戳列时,边界需要考虑时间部分。
建立示例数据表
示例数据表以 login_date 保存每次登录的日期。以下代表性资料用于说明各查询会选出哪些日期;这些 SQL 片段可在 PostgreSQL 会话中运行。
使用 CREATE TABLE logger (id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, login_date DATE NOT NULL); 建立数据表。然后使用 INSERT INTO logger (id, name, login_date) VALUES (1, 'Jhon', '2020-06-06'), (2, 'Alice', '2022-06-06'), (3, 'Bon', '2021-06-06'), (4, 'Trude', '2020-02-02'), (5, 'Jene', '2022-02-22'), (6, 'Dan', '2022-01-20'); 加入资料。
使用 INTERVAL 筛选滚动时间范围
滚动时间范围会随当前日期变化。将 INTERVAL 从 CURRENT_DATE 减去,即可计算范围起点,再用该截止日期比较日期列。若要筛选最近 120 天的资料,可使用 SELECT id, name, login_date FROM logger WHERE login_date > CURRENT_DATE - INTERVAL '120 days' ORDER BY id;。严格大于号 > 不会选中日期恰好等于截止日期的资料列;若要包含该日期,请使用 >=。此条件没有未来日期的上限;若要排除未来日期的资料列,请加上 AND login_date <= CURRENT_DATE。
由于 CURRENT_DATE 会随时间变化,选出的资料列也会改变。若想使用给定示例预览固定且包含两端的日期筛选效果,同时避免依赖今天的日期,可运行以下独立的 Python 示例。它只演示日期比较,不会执行 PostgreSQL SQL。
本节中的代码以及显示的标签或输出仍为英文。
from datetime import date
rows = [
(1, "Jhon", date(2020, 6, 6)),
(2, "Alice", date(2022, 6, 6)),
(3, "Bon", date(2021, 6, 6)),
(4, "Trude", date(2020, 2, 2)),
(5, "Jene", date(2022, 2, 22)),
(6, "Dan", date(2022, 1, 20)),
]
start, end = date(2021, 1, 1), date(2022, 3, 10)
for row_id, name, login_date in rows:
if start <= login_date <= end:
print(row_id, name, login_date.isoformat())
3 Bon 2021-06-06
5 Jene 2022-02-22
6 Dan 2022-01-20
使用 BETWEEN 指定包含两端的日期边界
当两个端点固定且都应包含在结果中时,可使用 BETWEEN。以下完整查询会选出从 2021 年 1 月 1 日到 2022 年 3 月 10 日(含首尾两天)的日期:SELECT id, name, login_date FROM logger WHERE login_date BETWEEN DATE '2021-01-01' AND DATE '2022-03-10' ORDER BY id;。BETWEEN 等同于下界使用 >= 且上界使用 <= 的比较条件。
对于 DATE 列,上界会包含整个日历日,因为储存的值不含时间部分。对于时间戳列,若上界是最后一天开始时的午夜,就不会包含当天剩余时间。请使用次日开始时的排他边界,或选择符合资料精度和时区的时间戳边界。
使用 daterange 查询纯日期范围
PostgreSQL 的 daterange 类型表示一个日历日期范围。@> 运算符用于判断范围是否包含某个值。若要选出日期从 2021 年 6 月 6 日到 2022 年 3 月 10 日的资料列,可使用 SELECT id, name, login_date FROM logger WHERE daterange(DATE '2021-06-06', DATE '2022-03-11', '[)') @> login_date ORDER BY id;。该范围包含下界但不包含上界,因此将 3 月 11 日设为排他端点,就会包含 3 月 10 日。此写法明确表示端点规则,并适用于示例中的 DATE 值。
如果固定的包含两端边界能让查询更易读,请使用 BETWEEN。如果范围包含操作更贴合需求,或范围值还会用于查询的其他部分,请使用 daterange。这些示例针对日期,不代表时间戳范围的行为。
处理日期格式与会变动的端点
日期字面值应使用明确无歧义的 YYYY-MM-DD 格式,例如 DATE '2021-06-06'。明确指定 DATE 类型可以表明该值是日历日期。CURRENT_DATE 会提供数据库会话的当前日期,因此适用于需要自动随时间推进的查询,例如滚动时间范围筛选。
若要生成稳定的报告,请使用固定日期边界,这样每次查询的都是相同期间。若查询应随日历变化,请根据 CURRENT_DATE 和 INTERVAL 计算截止日期。选择端点时也要考虑资料类型:对于 DATE,使用包含两端的日期边界很直接;筛选时间戳时,则需要考虑时间的上界。