簡體   English   中英

基於檢查約束的分區修剪未按預期工作

[英]Partition pruning based on check constraint not working as expected

為什么下面的查詢計划中包含表“events_201504”? 根據我的查詢和對該表的檢查約束,我希望查詢規划器能夠完全修剪它:

database=# \d events_201504
                                   Table "public.events_201504"
    Column     |            Type             |                           Modifiers
---------------+-----------------------------+---------------------------------------------------------------
 id            | bigint                      | not null default nextval('events_id_seq'::regclass)
 created_at    | timestamp without time zone |
Indexes:
    "events_201504_pkey" PRIMARY KEY, btree (id)
    "events_201504_created_at" btree (created_at)
Check constraints:
    "events_201504_created_at_check" CHECK (created_at >= '2015-04-01 00:00:00'::timestamp without time zone AND created_at <= '2015-04-30 23:59:59.999999'::timestamp without time zone)
Inherits: events

時間和配置:

database=# select now();
              now
-------------------------------
 2015-05-25 16:49:20.037815-05

database=# show constraint_exclusion;
 constraint_exclusion
----------------------
 on

查詢計划:

database=# explain select count(1) from events where created_at > now() - '1 hour'::interval;
                                                                QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=3479.86..3479.87 rows=1 width=0)
   ->  Append  (cost=0.00..3327.90 rows=60784 width=0)
         ->  Seq Scan on events  (cost=0.00..0.00 rows=1 width=0)
               Filter: (created_at > (now() - '01:00:00'::interval))
         ->  Index Only Scan using events_201504_created_at on events_201504  (cost=0.57..4.59 rows=1 width=0)
               Index Cond: (created_at > (now() - '01:00:00'::interval))
         ->  Index Only Scan using events_201505_created_at on events_201505  (cost=0.57..3245.29 rows=60765 width=0)
               Index Cond: (created_at > (now() - '01:00:00'::interval))

您的列created_at是類型timestamp without time zone

但是now()返回timestamp with time zone 表達式now() - '1 hour'::interval被強制轉換為timestamp [without time zone] ,這有兩個問題

1.)你沒有要求這個,但表達不可靠。 其結果取決於正在執行查詢的 session 的當前時區設置。此處的詳細信息:

為了使表達式清晰,您可以使用:

now() AT TIME ZONE 'Europe/London' -- your time zone here

或者只是(在這里閱讀手冊)

LOCALTIMESTAMP  -- explicitly take the local time

我會考慮使用timestamptz代替。
兩者都不能解決您的第二個問題:

2.) 回答你的問題。 約束排除不起作用。 手冊:

以下注意事項適用於約束排除:

  • [...]
  • 約束排除僅在查詢的WHERE子句包含常量(或外部提供的參數)時才有效。 例如,無法優化與非不變 function(如CURRENT_TIMESTAMP )的比較,因為規划器無法知道 function 值可能在運行時落入哪個分區。

大膽強調我的。

now()CURRENT_TIMESTAMP的 Postgres 實現。 正如您在系統目錄中看到的,它只是STABLE ,而不是IMMUTABLE

SELECT proname, provolatile FROM pg_proc WHERE proname = 'now';

proname | provolatile
--------+------------
now     | s              -- meaning: STABLE

解決方案

1.)您可以通過在WHERE條件中提供一個常量來克服限制(它始終是“不可變的”):

SELECT count(*) FROM events
WHERE created_at > '2015-05-25 15:49:20.037815'::timestamp;  -- from your example

2.)或者通過“偽造”一個不可變的 function:

CREATE FUNCTION f_now_immutable()
  RETURNS timestamp
  LANGUAGE sql IMMUTABLE AS
$func$
SELECT now() AT TIME ZONE 'UTC';  -- your time zone here
$func$;

接着:

SELECT count(*) FROM events
WHERE created_at > f_now_immutable() - interval '1 hour';

但是請注意如何使用它:雖然now()STABLE (在事務期間不會更改),但它確實會在事務之間更改,因此請注意不要在准備好的語句(作為參數值除外)或索引中使用它或任何可能咬你的東西。

3.)或者您可以將看似多余的常量WHERE子句添加到當前查詢中,以匹配分區上的約束:

SELECT count(*)
FROM   events
WHERE  created_at > now() - '1 hour'::interval
AND    created_at >= '2015-04-01 00:00:00'::timestamp
AND    created_at <= '2015-04-30 23:59:59.999999'::timestamp;

只需確保now() - '1 hour'::interval落入正確的分區,否則顯然不會得到任何結果。

旁白:我寧願在CHECK約束和查詢中使用這個表達式。 更容易處理並且做同樣的事情:

       created_at >= '2015-04-01 0:0'::timestamp
AND    created_at <  '2015-05-01 0:0'::timestamp

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM