How to set default date on a field of a Postgres table? - postgresql

How to add default date on postgresql?
I tried getdate() but the command gives a error:
ERROR: function getdate() does not exist
SQL state: 42883
Hint: No function matches the given name and argument types. You might need to add explicit type casts.
The command issued was:
Create TABLE CREATE_ORDERS (
Plot_ID VARCHAR (50) ,
USER_ID VARCHAR(50),
ORDER_ID VARCHAR(50),
TOTAL_AMOUNT float NOT NULL DEFAULT 0.000,
DOWNPAYMENT float NOT NULL DEFAULT 25000,
BALANCE_INSTALLMENT_AMOUNT float NOT NULL DEFAULT 0.000,
NO_OF_INSTALLMENTS float NOT NULL DEFAULT 12,
PURCHASE_DATE TIMESTAMP NOT NULL DEFAULT GETDATE(),
DUE_DATE TIMESTAMP NOT NULL DEFAULT GETDATE(),
CONSTRAINT AK_TransactionID1 UNIQUE(PLOT_ID),
CONSTRAINT AK_TransactionID2 UNIQUE(ORDER_ID),
PRIMARY KEY (PLOT_ID, ORDER_ID));

Use keyword CURRENT_TIMESTAMP instead of function GETDATE().
--Current date and time
SELECT CURRENT_TIMESTAMP;
--Current date
SELECT CURRENT_DATE;
--Current date and time
SELECT NOW();
More info here.

current_date function returns current date.
Check https://www.postgresql.org/docs/current/static/functions-datetime.html and https://www.postgresql.org/docs/current/static/functions-datetime.html#FUNCTIONS-DATETIME-CURRENT for more details.

Related

How to detect if default value is constant or expression in Postgresql

In Postgresql, how can I know default value is constant or expression?
Example:
CREATE TABLE IF NOT EXISTS default_test (
id serial primary key,
name varchar(255) NOT NULL,
father_name varchar(255) DEFAULT 'NULL', -- CONSTANT
nn2 varchar(255) DEFAULT ('NULL'), -- CONSTANT
nn3 varchar(255) DEFAULT (null), -- CONSTANT
nn4 varchar(255) DEFAULT 'the default value', -- CONSTANT
last_name varchar(255) NULL, -- CONSTANT
dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, -- EXPRESSION
dt2 timestamp NOT NULL DEFAULT '2011-11-11 00:00:00', -- CONSTANT
ts timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP /*ON UPDATE CURRENT_TIMESTAMP*/, -- EXPRESSION
ts2 timestamp NOT NULL DEFAULT '2011-11-11 00:00:00', -- CONSTANT
date_col date DEFAULT (CURRENT_DATE + INTERVAL '2 YEAR'), -- EXPRESSION
i INT DEFAULT -1, -- CONSTANT
c VARCHAR(10) DEFAULT '', -- CONSTANT
price DOUBLE precision DEFAULT 0.00, -- CONSTANT
-- literal defaults
i2 INT DEFAULT 0, -- CONSTANT
i3 INT DEFAULT 3, -- CONSTANT
pi_val FLOAT DEFAULT 3.14, -- CONSTANT
--
f FLOAT DEFAULT 3.14, -- CONSTANT
d DATE DEFAULT (CURRENT_DATE + INTERVAL '1 YEAR'), -- EXPRESSION
p POINT DEFAULT (Point(0,0)), -- EXPRESSION
j JSON DEFAULT '{"a":"b"}' -- CONSTANT
)
Similar part in MySQL
See https://dev.mysql.com/doc/refman/8.0/en/data-type-defaults.html
Column with default expression have DEFAULT_GENERATED in Extra column in Information Schema
https://dev.mysql.com/doc/refman/8.0/en/information-schema-columns-table.html
Yo can get that info in adbin field from attdef in the pg_catalog
If you have for example:
create table table1(
w_def_expr text default current_user,
w_def_cons text default 'hi',
wo_def text
);
You can get that info with this query:
select adrelid, tab.relname, attname, pg_get_expr(adbin, 0) expr,
split_part(substr(adbin,2),' ',1) expr_type
from pg_attribute col
inner join pg_class tab on col.attrelid = tab.oid
inner join pg_attrdef def on col.attrelid = def.adrelid and col.attnum = def.adnum
where tab.relname='table1';
Getting something like:
The way of getting info in adbin is not standar and can be change in the future.
I didn't find a way to do it better.
Here you can se an answer from 2017 saying that is not possible

postgresql insert with integer PK nextval

So, I have the following table definition with the id as integer PK using a nextval seq as shown below.
'''
CREATE TABLE public.fi_raisedalarms
(
id integer NOT NULL DEFAULT nextval('fi_raisedalarms_id_seq'::regclass),
equipid integer,
alid integer,
isset boolean,
tstamp timestamp without time zone,
create_uid integer,
create_date timestamp without time zone,
write_uid integer,
write_date timestamp without time zone,
CONSTRAINT fi_raisedalarms_pkey PRIMARY KEY (id),
CONSTRAINT fi_raisedalarms_alid_fkey FOREIGN KEY (alid)
REFERENCES public.fi_alarms (id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE SET NULL,
CONSTRAINT fi_raisedalarms_create_uid_fkey FOREIGN KEY (create_uid)
REFERENCES public.res_users (id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE SET NULL,
CONSTRAINT fi_raisedalarms_write_uid_fkey FOREIGN KEY (write_uid)
REFERENCES public.res_users (id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE SET NULL
)
'''
I then want to use a function as shown below to insert omitting the id since it should pull the default value but I am getting the following error. Not sure where to go from here....
'''
CREATE OR REPLACE FUNCTION public.setequipmentalarm(
equipmentid integer,
alarmid integer,
isset boolean,
tstamp timestamp without time zone)
RETURNS integer
LANGUAGE 'plpgsql'
COST 100
VOLATILE
AS $BODY$
DECLARE
var integer;
BEGIN
INSERT INTO fi_raisedalarms VALUES(equipmentid, alarmid, isset, tstamp) RETURNING equipmentid into var;
RETURN var;
END;
$BODY$;
ALTER FUNCTION public.setequipmentalarm(integer, integer, boolean, timestamp without time zone)
OWNER TO postgres;
'''
ERROR: column "alid" is of type integer but expression is of type boolean
LINE 1: ...INTO fi_raisedalarms VALUES(equipmentid, alarmid, isset, tst...
^
HINT: You will need to rewrite or cast the expression.
QUERY: INSERT INTO fi_raisedalarms VALUES(equipmentid, alarmid, isset, tstamp) RETURNING equipmentid
CONTEXT: PL/pgSQL function setequipmentalarm(integer,integer,boolean,timestamp without time zone) line 5 at SQL statement
SQL state: 42804
You need to specify column names if you are not setting a value for each table column:
INSERT INTO fi_raisedalarms(equipid, alid , isset, tstamp) VALUES ...
Alternatively, you can insert DEFAULT in place of the column to explicitly choose the default value. But specifying the columns is preferable.

How to pass a parameter and cast to a certain data type inside a function?

I'm attempting to write a plpgsql function to insert a record into a table that expects some timestamps in certain columns. Here is the function:
create or replace function insert_slot(created_by varchar
, version bigint
, bsv_id bigint
, start_date varchar) returns int as $$
declare
last_id int := (select id from appointments order by id desc limit 1) + 1;
begin
insert into appointments (
id,
created,
created_by,
version,
bsv_id,
usrn,
start_date,
end_date,
status,
request_priority_name,
reservation_expiry,
day_of_week
)values (
last_id,
now(),
created_by,
version,
bsv_id,
'UN-' || last_id,
to_timestamp(extract(epoch from timestamp #start_date)),
to_timestamp(extract(epoch from timestamp '2017-2-12 10:30:00')),
'OCCUPIED',
'ROUTINE',
to_timestamp(extract(epoch from timestamp '2017-3-19 10:30:00')),
1
);
return last_id;
end;
$$ LANGUAGE plpgsql;
select * from insert_slot('Brad', 2, 70000, '2017-2-12 10:00:00');
This works fine when I am passing a literal string for the date format, but not for a parameter:
to_timestamp(extract(epoch from timestamp #start_date)),
to_timestamp(extract(epoch from timestamp '2017-2-12 10:30:00')),
When I try to use #start_date or start_date instead of the literal string I get this error:
ERROR: column "timestamp" does not exist
LINE 21: to_timestamp(extract(epoch from timestamp #start_date)),
Can someone enlighten me as to the correct syntax to use? I've found plenty of posts online about using parameters but can't find anything that addresses why I can't pass through the function parameter to epoch from timestamp.
1.
Do not use a # character to prepend variables. That's SQL-Server or MySQL syntax (and maybe others) and illegal in Postgres SQL or PL/pgSQL code.
PL/pgSQL variable names follow the same rules as SQL identifiers. The manual:
SQL identifiers and key words must begin with a letter (a-z, but also
letters with diacritical marks and non-Latin letters) or an underscore
(_). Subsequent characters in an identifier or key word can be
letters, underscores, digits (0-9), or dollar signs ($).
So #start_date is a syntax error.
2.
In this expression:
to_timestamp(extract(epoch from timestamp '2017-2-12 10:30:00')),
timestamp is the data type of the immediately following string literal.
But this notation is not allowed for run-time type conversions. So this is a syntax error:
to_timestamp(extract(epoch from timestamp start_date))
You can use an explicit type cast instead:
to_timestamp(extract(epoch from start_date::timestamp))
The manual:
The ::, CAST(), and function-call syntaxes can also be used to specify
run-time type conversions of arbitrary expressions, as discussed in
Section 4.2.9. To avoid syntactic ambiguity, the type 'string' syntax
can only be used to specify the type of a simple literal constant.
In your particular case it would be smarter / cleaner to define the function parameter as date or timestamp to begin with - depends on what kind of data you plan to pass to the function. Your parameter name indicates a date, but your example indicates a timestamp.
Either way, you don't need to cast that later. EXTRACT() also accepts date and casts it to timestamp automatically.
I would recomend you to use serial/bigserial for id:
CREATE TABLE appointments(id bigserial PRIMARY KEY, ...
Also is better to pass start_date as timestsamp.
create or replace function insert_slot(created_by varchar, version bigint, bsv_id bigint, start_date timestsamp) returns int as $$
For access to function's argument in postgresql you just use its name. Also you can call it via <function_name>.<variable name>. It might be useful when arguments of your function has the same name as column in tables (as is in your function). To avoid this you can add something (for example v_) to names. Also you do not need too complicate construction as to_timestamp(extract(epoch from timestamp '2017-3-19 10:30:00')). Also you can use just sql function.
Improved variant of function:
create table appointments (
id serial PRIMARY KEY,
created timestamp,
created_by text,
version text,
bsv_id int8,
usrn text,
start_date timestamp,
end_date timestamp,
status text,
request_priority_name text,
reservation_expiry timestamp,
day_of_week int2);
CREATE OR REPLACE FUNCTION set_usrn() RETURNS TRIGGER AS $sql$
BEGIN
NEW.usrn := 'UN' || NEW.id;
RETURN NEW;
END;
$sql$ LANGUAGE plpgsql;
CREATE TRIGGER insert_usrn BEFORE INSERT ON appointments FOR EACH ROW EXECUTE PROCEDURE set_usrn();
create or replace function insert_slot(v_created_by varchar, v_version bigint, v_bsv_id bigint, v_start_date timestamp) returns int as $$
insert into appointments (
created,
created_by,
version,
bsv_id,
start_date,
end_date,
status,
request_priority_name,
reservation_expiry,
day_of_week
)values (
now(),
v_created_by,
v_version,
v_bsv_id,
v_start_date,
'2017-2-12 10:30:00',
'OCCUPIED',
'ROUTINE',
'2017-3-19 10:30:00',
1
) RETURNING id;
$$ LANGUAGE sql;
For checking results:
select * from insert_slot('Brad', 2, 70000, '2017-2-12 10:00:00');
SELECT * FROM appointments;
id | created | created_by | version | bsv_id | usrn | start_date | end_date | status | request_priority_name | reservation_expiry | day_of_week
----+----------------------------+------------+---------+--------+------+---------------------+---------------------+----------+-----------------------+---------------------+-------------
1 | 2017-02-05 17:30:59.800305 | Brad | 2 | 70000 | UN1 | 2017-02-12 10:00:00 | 2017-02-12 10:30:00 | OCCUPIED | ROUTINE | 2017-03-19 10:30:00 | 1

postgresql - insert result of query SELECT EXTRACT into another table

I have the following table in postgresql (table1):
Var1,
var2,
var3,
timestamp1 timestamp without time zone NOT NULL,
timestamp2 timestamp without time zone NOT NULL,
diff double precision,
The column diff is empty.
I calculate the variable diff by the following code:
SELECT EXTRACT(EPOCH FROM ((timestamp1 – timestamp2)/1800))
I want insert the result of this operation in variable diff of table 1.
I write the following code, but do not work…
CREATE TEMPORARY TABLE temptablename AS
SELECT EXTRACT(EPOCH FROM ((timestamp1 – timestamp2)/1800)) AS diff2 from table1;
INSERT INTO table1 (diff) SELECT diff2 FROM temptablename;
ERROR: null value in column "" violates not-null constraint
DETAIL: Failing row contains (null, null, null, null, null,83).
Assuming your arithmetic is right, it sounds like you just need an update statement.
update table1
set diff = extract(epoch from ((timestamp1 – timestamp2)/1800))
where diff is null;
The WHERE clause isn't strictly necessary, since you already know that column is empty. But it guards against overwriting values the second time you run that statement.

Extracting the number of days from a calculated interval

I am trying to get a query like the following one to work:
SELECT EXTRACT(DAY FROM INTERVAL to_date - from_date) FROM histories;
In the referenced table, to_date and from_date are of type timestamp without time zone. A regular query like
SELECT to_date - from_date FROM histories;
Gives me interval results such as '65 days 04:58:09.99'. But using this expression inside the first query gives me an error: invalid input syntax for type interval. I've tried various quotations and even nesting the query without luck. Can this be done?
SELECT EXTRACT(DAY FROM INTERVAL to_date - from_date) FROM histories;
This makes no sense. INTERVAL xxx is syntax for interval literals. So INTERVAL from_date is a syntax error, since from_date isn't a literal. If your code really looks more like INTERVAL '2012-02-01' then that's going to fail, because 2012-02-01 is not valid syntax for an INTERVAL.
The INTERVAL keyword here is just noise. I suspect you misunderstood an example from the documentation. Remove it and the expression will be fine.
I'm guessing you're trying to get the number of days between two dates represented as timestamp or timestamptz.
If so, either cast both to date:
SELECT to_date::date - from_date::date FROM histories;
or get the interval, then extract the day component:
SELECT extract(day from to_date - from_date) FROM histories;
This example demontrates the creation of a table with trigger which updates the difference between a stop_time and start_time in DDD HH24:MI:SS format where the DDD stands for the amount of dates ...
DROP TABLE IF EXISTS benchmarks ;
SELECT 'create the "benchmarks" table'
;
CREATE TABLE benchmarks (
guid UUID NOT NULL DEFAULT gen_random_uuid()
, id bigint UNIQUE NOT NULL DEFAULT cast (to_char(current_timestamp, 'YYMMDDHH12MISS') as bigint)
, git_hash char (8) NULL DEFAULT 'hash...'
, start_time timestamp NOT NULL DEFAULT DATE_TRUNC('second', NOW())
, stop_time timestamp NOT NULL DEFAULT DATE_TRUNC('second', NOW())
, diff_time varchar (20) NOT NULL DEFAULT 'HH:MI:SS'
, update_time timestamp DEFAULT DATE_TRUNC('second', NOW())
, CONSTRAINT pk_benchmarks_guid PRIMARY KEY (guid)
) WITH (
OIDS=FALSE
);
create unique index idx_uniq_benchmarks_id on benchmarks (id);
-- START trigger trg_benchmarks_upsrt_diff_time
-- hrt = human readable time
CREATE OR REPLACE FUNCTION fnc_benchmarks_upsrt_diff_time()
RETURNS TRIGGER
AS $$
BEGIN
-- NEW.diff_time = age(NEW.stop_time::timestamp-NEW.start_time::timestamp);
NEW.diff_time = to_char(NEW.stop_time-NEW.start_time, 'DDD HH24:MI:SS');
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trg_benchmarks_upsrt_diff_time
BEFORE INSERT OR UPDATE ON benchmarks
FOR EACH ROW EXECUTE PROCEDURE fnc_benchmarks_upsrt_diff_time();
--
-- STOP trigger trg_benchmarks_upsrt_diff_time
Just remove the keyword INTERVAL:
SELECT EXTRACT(DAY FROM to_date - from_date) FROM histories;