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 篩選滾動時間範圍

滾動時間範圍會隨目前日期移動。從 CURRENT_DATE 減去一個 INTERVAL,即可算出範圍起點,再以該界限比較日期欄位。若要查詢最近 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 的包含式界限很直接;時間戳記篩選則需要考量時間的上界。