jph*_*jph 6 sql postgresql database-partitioning postgresql-performance postgresql-9.3
为什么下面的查询计划中包含表“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
Run Code Online (Sandbox Code Playgroud)
时间及配置:
database=# select now();
now
-------------------------------
2015-05-25 16:49:20.037815-05
database=# show constraint_exclusion;
constraint_exclusion
----------------------
on
Run Code Online (Sandbox Code Playgroud)
查询计划:
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))
Run Code Online (Sandbox Code Playgroud)
Erw*_*ter 10
您的专栏created_at属于 类型timestamp without time zone。
但now()归来timestamp with time zone。该表达式now() - '1 hour'::interval被强制为timestamp [without time zone],这带来了两个问题:
1.)你没有要求这个,但这个表达不可靠。其结果取决于正在执行查询的会话的当前时区设置。详细信息如下:
为了使表达式清晰,您可以使用:
now() AT TIME ZONE 'Europe/London' -- your time zone here
Run Code Online (Sandbox Code Playgroud)
或者只是(阅读此处的手册):
LOCALTIMESTAMP -- explicitly take the local time
Run Code Online (Sandbox Code Playgroud)
我会考虑与合作timestamptz。
两者都不能解决你的第二个问题:
2.) 回答你的问题。约束排除不起作用。手册:
以下注意事项适用于约束排除:
- [...]
WHERE仅当查询的子句包含常量(或外部提供的参数)时,约束排除才起作用。例如,与非不可变函数(例如CURRENT_TIMESTAMP无法优化)的比较,因为规划器无法知道函数值在运行时可能落入哪个分区。
大胆强调我的。
now()是 Postgres 的实现CURRENT_TIMESTAMP。正如您在系统目录中看到的,它只是STABLE,而不是IMMUTABLE:
SELECT proname, provolatile FROM pg_proc WHERE proname = 'now';
proname | provolatile
--------+------------
now | s -- meaning: STABLE
Run Code Online (Sandbox Code Playgroud)
1.)您可以通过在条件中提供常量WHERE(始终“不可变”)来克服限制:
now() AT TIME ZONE 'Europe/London' -- your time zone here
Run Code Online (Sandbox Code Playgroud)
2.)或者通过“伪造”一个不可变的函数:
LOCALTIMESTAMP -- explicitly take the local time
Run Code Online (Sandbox Code Playgroud)
进而:
SELECT proname, provolatile FROM pg_proc WHERE proname = 'now';
proname | provolatile
--------+------------
now | s -- meaning: STABLE
Run Code Online (Sandbox Code Playgroud)
但要小心如何使用它: while now()is STABLE(在事务期间不会改变),它会在事务之间改变,所以请注意不要在准备好的语句(作为参数值除外)或索引或任何需要它的地方使用它。可能会咬你。
3.)或者,您可以将看似多余的常量WHERE子句添加到当前查询中,以匹配分区上的约束:
选择计数(*) 来自事件 WHERE create_at > now() - '1 小时'::间隔 AND 创建时间 >= '2015-04-01 00:00:00'::时间戳 AND 创建的_at <= '2015-04-30 23:59:59.999999'::timestamp;
只要确保自己now() - '1 hour'::interval属于正确的分区,否则显然你不会得到任何结果。
旁白:我宁愿在CHECK约束和查询中使用这个表达式。更容易处理并且执行相同的操作:
created_at >= '2015-04-01'::timestamp
AND created_at < '2015-05-01'::timestamp
Run Code Online (Sandbox Code Playgroud)