0

What i want to do, is to change my date column type from varchar to timestamp w/o timezone, and migrate all my data. This is what im doing:

ALTER TABLE mytable ALTER COLUMN "datecol"
    TYPE timestamp without time zone USING(to_timestamp("datecol", 'YYYY-MM-DD')::timestamp without time zone);

This works fine if data in datecol is in date format. But if i have some not valid data, like random strings (e.g "abc") how can i validate and check if date format is good? I want to set default value for those invalid fields.

EDIT: Thanks Ludvig, i solved my problem:

create or replace function is_date(s varchar) returns boolean as $$
begin
perform s::date;
return true;
exception when others then
return false;
end;
$$ language plpgsql;

ALTER TABLE mytable ALTER COLUMN "datecol"
TYPE timestamp without time zone USING(
CASE WHEN is_date("datecol") = true
THEN to_timestamp("datecol", 'YYYY-MM-DD')::timestamp without time zone
ELSE '1970-01-01 00:00:00.000'
END
);
3
  • This answer might be of help to you. Commented May 3, 2017 at 9:42
  • date format is good Which date formats are good? only like this: 'YYYY-MM-DD'? Commented May 3, 2017 at 9:50
  • No, to_timestamp identifies formats like: YYYY, YYYY-MM, YYYY-MM-DD, YYYY/MM/DD, YYYY.MM.DD etc. For those formats it works fine Commented May 3, 2017 at 9:56

2 Answers 2

0

You can't alter type for column with try/catch logic, so you either have to regexp all possible formats or you can:

  1. add column "ts" of timestamp data type
  2. add column "exc" boolean
  3. do $$ 
      declare _r record; 
    begin 
      for _r in (select * from mytable) loop 
        update mytable set "ts" = to_timestamp("datecol", 'YYYY-MM-DD') 
        where ...PK..=_r.PK; 
      when others 
        then update mytable set "exc" = true; 
     end loop; end; $$;
    
  4. drop datecol, rename "ts" to "datecol"
  5. deal with values where "exc"

The big minus would be changed order of columns in mytable. So poor apology that you can after create table tt as select NEEDED order from mytable; drop table mytable;alter table tt rename to mytable, but then you will have to rebuild all references and dependants as well of course. Or even more exotic way - you can start adding column in needed order, dropping renaming until you get the old set...

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, but changed order is not really what i need :D
0

Instead of testing for a valid date I would create a function that tries casting using different formats and returns the default date in case none of them work:

create or replace function convert_date(s varchar) 
   returns timestamp 
as 
$$
begin
  -- test standard ISO format
  begin
    return to_timestamp(s, 'yyyy-mm-dd');
  exception when others then
      -- ignore
  end;

  begin
    return to_timestamp(s, 'yyyy.mm.dd');
  exception when others then
    -- ignore
  end;

  begin
    return to_timestamp(s, 'yyyy/mm/dd');
  exception when others then
    -- ignore
  end;

  begin
    return to_timestamp(s, 'yyyy-mm');
  exception when others then
    -- ignore
  end;


  begin
    return to_timestamp(s, 'yyyy');
  exception when others then
    -- ignore
  end;

  return timestamp '1970-01-01 00:00:00';
end
$$ language plpgsql;

Then use:

ALTER TABLE mytable 
    ALTER COLUMN "datecol" TYPE timestamp without time zone 
    USING (convert_date(datecol));

This won't be very efficient, but for a one-time job it should work

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.