Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PostgreSQL create index on cast from string to date

I'm trying to create an index on the cast of a varchar column to date. I'm doing something like this:

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

I'm getting the error: functions in index expression must be marked IMMUTABLE But I don't get why, the cast to date doesn't depends on the timezone or something like that (which makes a cast to timestamp with time zone give this error).

Any help?

like image 347
Topo Avatar asked May 06 '13 19:05

Topo


2 Answers

Your first error was to store a date as a varchar column. You should not do that.

The proper fix for your problem is to convert the column to a real date column.

Now I'm pretty sure the answer to that statement is "I didn't design the database and I cannot change it", so here is a workaround:

CAST and to_char() are not immutable because they can return different values for the same input value depending on the current session's settings.

If you know you have a consistent format of all values in the table (which - if you had - would mean you can convert the column to a real date column) then you can create your own function that converts a varchar to a date and is marked as immutable.

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
/

With that definition you can create an index on the expression:

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

But you have to use exactly that function call in your query so that Postgres uses it:

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

Note that this approach will fail badly if you have just one "illegal" value in your varchar column. The only sensible solution is to store dates as dates,

like image 152
a_horse_with_no_name Avatar answered Sep 26 '22 02:09

a_horse_with_no_name


Please provide the database version, table ddl, and some example data.

Would making your own immutable function do what you want, like this? Also look into creating a new cast in the docs and see if that does anything for you.

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));
like image 35
Kuberchaun Avatar answered Sep 22 '22 02:09

Kuberchaun