I want to use a UTC timestamp as default value for a TIMESTAMP column. We're using DB2 9.5 on Linux
I'm aware of using CURRENT TIMESTAMP, but it provides the local time of the DB2 server (e.g. CEST). In queries you can use
SELECT JOB_ID, (CURRENT TIMESTAMP - CURRENT TIMEZONE) as tsp FROM SYSTEM_JOBS;
but this does not work in the column definition
ALTER TABLE SYSTEM_JOBS ALTER COLUMN CREATED SET DEFAULT CURRENT TIMESTAMP - CURRENT TIMEZONE
[42601][-104] An unexpected token "ALTER TABLE SYSTEM_JOBS ALTER COLUMN CREAT" was found following "BEGIN-OF-STATEMENT". Expected tokens may include: "<values>".. SQLCODE=-104, SQLSTATE=42601, DRIVER=4.23.42
I also tried to define a function, that makes the calculation.
CREATE OR REPLACE FUNCTION UTCTIMESTAMP ()
RETURNS TIMESTAMP
LANGUAGE SQL
DETERMINISTIC
NO EXTERNAL ACTION
BEGIN ATOMIC
DECLARE L TIMESTAMP;
DECLARE U TIMESTAMP;
SET L = CURRENT TIMESTAMP;
SET U = L - CURRENT TIMEZONE;
RETURN U;
END
;
But it's also not accepted in column definition
ALTER TABLE SYSTEM_JOBS ALTER COLUMN CREATED SET DEFAULT UTCTIMESTAMP();
[42894][-574] DEFAULT value or IDENTITY attribute value is not valid for column "CREATED" in table "DB2INST1.SYSTEM_JOBS". Reason code: "7".. SQLCODE=-574, SQLSTATE=42894, DRIVER=4.23.42
I'm looking for a method to set the default value in neutral UTC.
You are not able to use expressions in the DEFAULT clause. See the description of default-clause of the CREATE TABLE statement.
You can use a BEFORE INSERT trigger instead, for example, to achieve the same functionality:
CREATE TABLE SYSTEM_JOBS (ID INT NOT NULL, CREATED TIMESTAMP NOT NULL) IN USERSPACE1;
CREATE TRIGGER SYSTEM_JOBS_BIR
BEFORE INSERT ON SYSTEM_JOBS
REFERENCING NEW AS N
FOR EACH ROW
WHEN (N.CREATED IS NULL)
SET CREATED=CURRENT TIMESTAMP - CURRENT TIMEZONE;
INSERT INTO SYSTEM_JOBS(ID) VALUES 1;
Related
This is basically a duplicate of this with s/mysql/postgresql/g.
I created a table that has a timestamp TIMESTAMP column and I am trying to import data from CSV files that have Unix timestamped rows.
However, when I try to COPY the file into the table, I get errors to the tune of
2:1: conversion failed: "1394755260" to timestamp
3:1: conversion failed: "1394755320" to timestamp
4:1: conversion failed: "1394755800" to timestamp
5:1: conversion failed: "1394755920" to timestamp
Obviously this works if I set the column to be INT.
In the MySQL variant, I solved with a trick like
LOAD DATA LOCAL INFILE 'file.csv'
INTO TABLE raw_data
fields terminated by ','
lines terminated by '\n'
IGNORE 1 LINES
(#timestamp, other_column)
SET timestamp = FROM_UNIXTIME(#timestamp),
third_column = 'SomeSpecialValue'
;
Note two things: I can map the #timestamp variable from the CSV file using a function to turn it into a proper DATETIME, and I can set extra columns to certain values (this is necessary because I have more columns in the database than in the CSV).
I'm switching to postgresql because mysql lacks some functions that make my life so much easier with the queries I need to write.
Is there a way of configuring the table so that the conversion happens automatically?
I think you could accomplish this by importing the data as-is, creating a second column with the converted timestamp and then using a trigger to make sure any time a row is inserted it populates the new column:
alter table raw_table
add column time_stamp timestamp;
CREATE OR REPLACE FUNCTION raw_table_insert()
RETURNS trigger AS
$BODY$
BEGIN
NEW.time_stamp = timestamp 'epoch' + NEW.unix_time_stamp * interval '1 second';
return NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
CREATE TRIGGER insert_raw_table_trigger
BEFORE INSERT
ON raw_table
FOR EACH ROW
EXECUTE PROCEDURE raw_table_insert();
If the timestamp column can be modified, then you will want to make sure the trigger applies to updates as well.
Alternatively, you can create a view that generates the timestamp on the fly, but the advantages/disadvantages depend on how often you search on the column, how large the table is going to be and how much DML you expect.
I'm looking for something similar to this SQL.
column_name my_timestamp_column NOT NULL DEFAULT CURRENT_TIMESTAMP
Note that I require that the name of the column be specifiable, so the timestamps macro is not suitable.
You can execute SQL in migration like this
def change do
execute("ALTER TABLE some_table ADD some_column timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP")
end
How can I set a default for column B to be the value in column A?
I know, it is possible in Microsoft SQL Server:
http://www.ideaexcursion.com/2010/04/19/default-column-value-to-identity-of-different-column/
Is it possible in PostgreSQL?
The linked example shows how to intialize one column with the value of the identity column of the same table.
That is possible in Postgres
create table identdefault
(
a serial not null,
b int not null default currval('identdefault_a_seq')
);
serial will create a sequence in the background that is named tablename_column_seq thus we know that the sequence for identdefault.a will be named identdefault_a_seq and we can access the last value through the currval function.
Running:
insert into identdefault default values;
insert into identdefault default values;
insert into identdefault default values;
select *
from identdefault
will output:
a | b
--+--
1 | 1
2 | 2
3 | 3
This seems to only work with Postgres 9.4, when I tried that with 9.3 (on SQLFiddle) I got an error. But in that case it is possible as well - you just can't use the "shortcut" serial but need to create the sequence explicitly:
create sequence identdefault_a_seq;
create table identdefault
(
a int not null default nextval('identdefault_a_seq'),
b int not null default currval('identdefault_a_seq')
);
insert into identdefault default values;
insert into identdefault default values;
insert into identdefault default values;
If you want to have an identical definition as with the serial column, you just need to make the sequence belong to the column:
alter sequence identdefault_a_seq owned by identdefault.a;
SQLFiddle: http://sqlfiddle.com/#!15/0aa34/1
The answer to the much broader question "How can I set a default for column B to be the value in column A?" is unfortunately: no, you can't (see klin's comment)
You can't do it with an actual DEFAULT, but it's trivial with a BEFORE trigger.
CREATE OR REPLACE FUNCTION whatever() RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
NEW.onecol := NEW.othercol;
RETURN NEW;
END;
$$;
CREATE TRIGGER whatever_tg
BEFORE INSERT ON mytable
FOR EACH ROW EXECUTE PROCEDURE whatever();
"You cannot do that" and postgres don't go well together. There's almost always a way you can do that (whatever "that" turns out to be).
The question is more like: How do you want to do it?
One way, that is nice to DB-Admins would be: Create a before-Trigger, manipulate the new row before it is written.
If your rules to create that new column are very fancy: Turn to one of the embedded languages (like perl).
So: Is it possible? Of course it is.
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?
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,
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));
I am having trouble converting a table column TYPE from time without time zone to integer.
Is that possible?
Where integer will be equal to seconds.
I am using ALTER table exampleTable ALTER COLUMN time TYPE integer.
This should do it:
ALTER TABLE your_table
ALTER COLUMN time TYPE integer USING 0;
ALTER COLUMN is usually only used when you want to preserve the data in that column. In your case the above should work, but doesn't buy you anything. Dropping the column and then adding a new one with the type integer and a default value of 0 would be just as efficient.
(Btw: you should not use a reserved word like time as a column name)
Edit
If you do want to convert the existing data then you can use this:
ALTER TABLE your_table
ALTER COLUMN time_column TYPE integer USING extract(epoch from time_column);
Following up on the horse's answer, you can also add an expression in the using part, e.g.:
ALTER TABLE your_table
ALTER COLUMN time TYPE integer USING (
extract('hours' from time) * 3600 +
extract('minutes' from time) * 60 +
extract('seconds' from time)
);
http://www.postgresql.org/docs/current/static/sql-altertable.html