Function to insert data into different tables - postgresql

I have three tables in PostgreSQL:
CREATE TABLE organization (id int, name text, parent_id int);
CREATE TABLE staff (id int, name text, family text, organization_id int);
CREATE TABLE clock(id int, staff_id int, Date date, Time time);
I need a function that gets all the fields of these tables as inputs (8 on total) and then inserts these inputs into appropriate fields of the tables
Here is my code:
CREATE FUNCTION insert_into_tables(org_name character varying(50), org_PID int, person_name character varying(50),_family character varying(50), org_id int, staff_id int,_date date, _time time without time zone)
RETURNS void AS $$
BEGIN
INSERT INTO "Org".organisation("Name", "PID")
VALUES ($1, $2);
INSERT INTO "Org".staff("Name", "Family", "Organization_id")
VALUES ($3, $4, $5);
INSERT INTO "Org"."Clock"("Staff_Id", "Date", "Time")
VALUES ($6, $7, $8);
END;
$$ LANGUAGE plpgsql;
select * from insert_into_tables('SASAD',9,'mamad','Imani',2,2,1397-10-22,'08:26:47')
But no data is inserted. I get the error:
ERROR: function insert_into_tables(unknown, integer, unknown, unknown, integer, integer, integer, unknown) does not exist
LINE 17: select * from insert_into_tables('SASAD',9,'mamad','Imani',2... ^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
Where did i go wrong?

That's because the 2nd last parameter is declared as date, not int. You forgot the single quotes:
select * from insert_into_tables('SASAD',9,'mamad','Imani',2,2,'1397-10-22','08:26:47');
Without single quotes, this is interpreted as subtraction between 3 integer constants, resulting in an integer: 1397-10-22 = 1365.
Also fix your identifiers: double-quoting preserves upper-case letters, so "Name" is distinct from name etc. See:
Are PostgreSQL column names case-sensitive?

Related

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.

Returned type bigint does not match expected type integer in column 3

Below is my table structure for sold_quantity (Migration File)
alter table public.invoice_item add column sold_quantity int4 default 1;
Below is the function for execution
CREATE OR REPLACE FUNCTION sold_quantity()
RETURNS TABLE(
invoiceid BIGINT,
itemid BIGINT,
sum_sold_quantity INT)
AS $$
BEGIN
RETURN QUERY SELECT
invoice_id as invoiceid, item_id as itemid, sum(sold_quantity) as
sum_sold_quantity
FROM
invoice_item
WHERE
status='sold'
GROUP BY
invoice_id, item_id;
END; $$
What is the wrong in my code, Please help me solve this Error
Returned type bigint does not match expected type integer in column 3
sum() returns a bigint, not necessarily the type of the column that is being summed.
If you are 100% sure your sum never exceeds the range for an integer, you can fix this using a cast in your query: sum(sold_quantity)::int as sum_sold_quantity
But it would be better to adjust the signature of the function:
CREATE OR REPLACE FUNCTION sold_quantity()
RETURNS TABLE(
invoiceid BIGINT,
itemid BIGINT,
sum_sold_quantity BIGINT)

Insert a row with NULL values from one table

I have a custom type:
CREATE TYPE public.relacion AS
(id integer,
codpadre character varying,
codhijo character varying,
canpres numeric(7,3),
cancert numeric(7,3),--this is usually NULL
posicion smallint);
After that, I need another type from relacion type:
CREATE TYPE public.borrarrelacion AS
(idborrar integer,
paso integer,
rel relacion);
Well, now into a function I need to copy some rows from a relacion type table to borrarrelacion type table:
This is a snippet of my code:
DECLARE:
r relacion%ROWTYPE;
------------
BEGIN
EXECUTE FORMAT ('CREATE TABLE IF NOT EXISTS "mytable" OF borrarrelacion (PRIMARY KEY (idborrar))');
EXECUTE FORMAT ('SELECT * FROM %I WHERE codpadre = %s AND codhijo = %s',
tablarelacion,quote_literal(codigopadre),quote_literal(codigohijo)) INTO r;
EXECUTE FORMAT ('INSERT INTO "mytable" VALUES(0,0,%s)',r);
But I get an error because the field r.cancert is NULL and it's trying to insert something like that:
INSERT INTO "mytable" VALUES(0,0,(0,'cod1','cod2',10,,0));
I can solve this problem reading every field of r and putting it values in the INSERT statement, like that: (and changing the NULL value by 0)
EXECUTE FORMAT ('INSERT INTO "mytable" VALUES(0,0,(%s,%s,%s,%s,%s,%s))',
r.id,quote_literal(r.codpadre),quote_literal(r.codhijo),r.canpres,COALESCE(r.cancert,0),r.posicion);
But I would like to know if I can avoid this way and I can insert exactly the same row from one table to other.

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, SQL state: 42601

I want to insert into a table (circuit) using a select which takes values from 2 tables (segment and wgs). My query:
INSERT INTO circuit (id_circuit, description, date_start, date_end, speed,
length, duration)
SELECT (seg.id_segment, cir.nomcircuit, seg.date_start, seg.date_end, seg.speed_average,
cir.shape_leng, (seg.date_end - seg.date_start))
FROM segment seg, wgs cir where seg.id = 13077
My Tables: circuit:
CREATE TABLE circuit
(
id serial NOT NULL,
id_circuit integer,
description character varying(50),
date_start time without time zone,
date_end time without time zone,
speed double precision,
length double precision,
duration double precision,
CONSTRAINT circuit_pkey PRIMARY KEY (id)
)
segment:
CREATE TABLE segment
(
id serial NOT NULL,
id_segment integer,
date_start timestamp without time zone,
date_end timestamp without time zone,
speed_average double precision,
mt_identity character varying,
truck_type character varying,
CONSTRAINT segment_pkey PRIMARY KEY (id)
)
wgs:
CREATE TABLE wgs
(
id serial NOT NULL,
nomcircuit character varying(50),
shape_leng numeric,
CONSTRAINT wgs_pkey PRIMARY KEY (id)
)
But when I run my query, this error comes:
ERROR: INSERT has more target columns than expressions
LINE 1: INSERT INTO circuit (id_circuit, description, dat...
^
HINT: The insertion source is a row expression containing the same number of columns
expected by the INSERT. Did you accidentally use extra parentheses?
As far I can see, I do not have extra parentheses, I double checked the columns data type and made sure they match and various tries, but I still don't get why the error comes.
PS: the 13077 is just to try it out with one value I'm sure I have.
This constructs an anonymous composite value:
select (1, 'a');
For example:
=> select (1, 'a');
row
-------
(1,a)
(1 row)
=> select row(1, 'a');
row
-------
(1,a)
(1 row)
Note that that is a single composite value, not multiple values.
From the fine manual:
8.16.2. Composite Value Input
To write a composite value as a literal constant, enclose the field values within parentheses and separate them by commas. You can put double quotes around any field value, and must do so if it contains commas or parentheses.
[...]
The ROW expression syntax can also be used to construct composite values. In most cases this is considerably simpler to use than the string-literal syntax since you don't have to worry about multiple layers of quoting. We already used this method above:
ROW('fuzzy dice', 42, 1.99)
ROW('', 42, NULL)
The ROW keyword is actually optional as long as you have more than one field in the expression, so these can simplify to:
('fuzzy dice', 42, 1.99)
('', 42, NULL)
The Row Constructors section might also be of interest.
When you say this:
INSERT INTO circuit (id_circuit, description, date_start, date_end, speed,
length, duration)
SELECT (...)
FROM segment seg, wgs cir where seg.id = 13077
your SELECT clause only has one column as the whole (...) expression represents a single value. The solution is to simply drop those parentheses:
INSERT INTO circuit (id_circuit, description, date_start, date_end, speed, length, duration)
SELECT seg.id_segment, ..., (seg.date_end - seg.date_start)
FROM segment seg, wgs cir where seg.id = 13077