18

迄今为止,我正在尝试在 varchar 列的强制转换上创建索引。我正在做这样的事情:

CREATE INDEX date_index ON table_name (CAST(varchar_column AS DATE));

我得到了错误:functions in index expression must be marked IMMUTABLE但我不明白为什么,迄今为止的演员表不取决于时区或类似的东西(这使得演员表与时区的时间戳给出了这个错误)。

有什么帮助吗?

4

2 回答 2

18

您的第一个错误是将日期存储为 varchar 列。你不应该那样做。

解决您的问题的正确方法是将 column 转换为 real datecolumn

现在我很确定该声明的答案是“我没有设计数据库,我无法更改它”,所以这里有一个解决方法:

CAST并且to_char()不是不可变的,因为它们可以根据当前会话的设置为相同的输入值返回不同的值。

如果您知道表中所有值的格式一致(如果有,则意味着您可以将列转换为真实date 列),那么您可以创建自己的函数,将 varchar 转换为日期并标记作为不可变的。

create or replace function fix_bad_datatype(the_date varchar)
   returns date
   language sql
   immutable
as
$body$
  select to_date(the_date, 'yyyy-mm-dd');
$body$
ROWS 1
/

使用该定义,您可以在表达式上创建索引:

CREATE INDEX date_index ON table_name (fix_bad_datatype(varchar_column));

但是您必须在查询中准确地使用该函数调用,以便 Postgres 使用它:

select *
from foo
where fix_bad_datatype(varchar_column) < current_date;

请注意,如果您的 varchar 列中只有一个“非法”值,则此方法将失败。唯一明智的解决方案将日期存储为dates,

于 2013-05-06T19:37:47.720 回答
3

请提供数据库版本、表 ddl 和一些示例数据。

像这样让自己的不可变函数做你想做的事吗?还要考虑在文档中创建一个新演员表,看看这是否对你有用。

create table emp2 (emp2_id integer, hire_date VARCHAR(100));

insert into emp2(hire_date)
select now();

select cast(hire_date as DATE)
from emp2


CREATE FUNCTION my_date_cast(VARCHAR) RETURNS DATE
    AS 'select cast($1 as DATE)'
    LANGUAGE SQL
    IMMUTABLE
    RETURNS NULL ON NULL INPUT;


CREATE INDEX idx_emp2_hire_date ON emp2 (my_date_cast(hire_date));
于 2013-05-06T19:37:36.580 回答