我有一个表使用declarative partitioning(w00t!)按日期范围对表进行分区 - 在我的情况下为一年。
当我查询表格SELECT * FROM tbl WHERE date > date '2016-01-01'
时,它完全符合预期;只扫描包含较新数据的表格。
当我使用变量或函数(CURRENT_DATE
,NOW()
等)指定日期时,EXPLAIN
表示会扫描每个分区。
按预期工作的事情:
SELECT * FROM tbl WHERE date > date '2016-01-01'
--
SELECT * FROM tbl WHERE date > '2016-01-01'::date
不必要地扫描所有分区的事情:
SELECT * FROM tbl WHERE date > CURRENT_DATE
--
SELECT * FROM tbl WHERE date > NOW()
--
SELECT * FROM tbl WHERE date > (NOW() - 365)::date
--
SELECT * FROM tbl WHERE date > (SELECT (NOW()::date - 365)::date AS d)
-- Even CTEs are no dice:
WITH a AS (SELECT CURRENT_DATE AS d)
SELECT * FROM tbl, a WHERE date > a.d
-- Same with JOINs
SELECT w.*
FROM (CURRENT_DATE - 365 as d) a
LEFT JOIN wtf w ON w.date > a.d
...等
我与其他比较运算符=
,<
等有相同的行为。
文档说我不需要场上的idx(我不会反正)。我添加了一个以防万一,它没有帮助。
为什么会发生这种情况,我该怎么做才能阻止它(最好不要在简单的查询中添加复杂功能)?
答案 0 :(得分:2)
感谢JustMe回答这个问题 - 请参阅OP上的评论。
问题在于NOW()
和CURRENT_TIMESTAMP
与FROM
相关的评估时间;当您尝试在联接ala WHERE join_table.a > from_table.b
中过滤时,您会看到同样的问题。
假设今天是1970年1月1日,这些查询
SELECT * FROM my_stuff WHERE date > NOW()::date;
--
SELECT * FROM my_stuff WHERE date > '1970-01-01'::date;
必然会产生相同的结果集,但不一定以相同的方式进行评估。
这就是为什么会发生这种情况,不幸的是,似乎没有一种简单的方法可以阻止它。功能似乎是最好的选择:
CREATE OR REPLACE FUNCTION myfunc()
RETURNS setof tbl
LANGUAGE 'plpgsql'
AS $$
DECLARE
n date := CURRENT_DATE - 365;
BEGIN
RETURN query EXECUTE $a$
SELECT * FROM tbl
WHERE date > $1;
$a$ using n;
END $$;
您可以通过将RETURNS setof tbl
更改为RETURNS setof text
并将SELECT...
更改为EXPLAIN SELECT...