Flyway migration in postgresql? - postgresql

I'm trying to add an entry to Postgresql using such a request
insert into customer (id, email, name, number_telephone) VALUES (public.hibernate_sequence_customer.nextval, 'abc#jar.ru' , 'Henry', '89132547898');
, but flyway throws an error
Error: table "hibernate_sequence_customer" is missing in the FROM clause
In the project structure
enter image description here

The next value of your sequence is accessed via nextval('public.hibernate_sequence_customer'), not dot notation.
insert into customer (
id,
email,
name,
number_telephone)
VALUES (
nextval('public.hibernate_sequence_customer'),
'abc#jar.ru' ,
'Henry',
'89132547898');
but if you define id column as serial, you don't need to call the sequence at all.
create table customer (
id serial primary key,
email text,
name text,
number_telephone text);
Just skip it in your insert:
insert into customer (
email,
name,
number_telephone)
VALUES (
'abc#jar.ru' ,
'Henry',
'89132547898');
If you later need to refer to the sequence responsible for the id column - to get its current value, for example - you can use currval(pg_get_serial_sequence('customer','id')).

Related

PostrgreSQL ForeignKeyViolation

I am attempting to insert some data into my database via a lambda function. I am getting the following error ForeignKeyViolation: insert or update on table "address" violates foreign key constraint "address_id_fkey"
I understand that this is because my address table has a foreign key linking it to the clients table, and the keys are not matching.
Is there a way to format my tables so that I can input my client data and address data together? Or will I need to input the client data first, then retrieve the id and use it to input the address data.
Currently I am running the following two functions.
postgres_insert_query = "INSERT INTO clients (name, phone, contact) VALUES ('{0}','{1}','{2}')".format(data['name'], data['phone'], data['contact'])
postgres_insert_query = "INSERT INTO address (line1, city, state, zip) VALUES ('{0}','{1}','{2}', {3})".format(address['line1'], address['city'], address['state'], address['zip'])
Even if no address data is present I would still like to create a row for it (with the correct foreign key).
use DEFERRABLE foreign key constraint. Then wrap you function into a transaction.
CREATE temp TABLE pktable (
id INT4 PRIMARY KEY,
other INT4
);
CREATE temp TABLE fktable (
id INT4 PRIMARY KEY,
fk INT4 REFERENCES pktable DEFERRABLE INITIALLY DEFERRED
);
BEGIN;
INSERT INTO fktable VALUES (100, 200);
INSERT INTO pktable VALUES (200, 500);
COMMIT;
Postgres allows DML operations within a CTE. Doing so will allow you to insert into both tables in a single statement while allowing auto-generation of both ids. The following is a Postgres implementation. See demo.
with thedata(name, phone, contact, line1, city, state, zip) as
( values ('client 1', 'ev4 4213', 'andy','614 a', 'some city;','that state','11111'))
, theinsert (cli_id) as
( insert into clients(name, phone, contact)
select name, phone, contact
from thedata
returning cli_id
)
insert into addresses(cli_id, line1, city, state, zip)
select cli_id, line1, city, state, zip
from theinsert
cross join thedata;
Unfortunately I do not know your obscurification (Orm) language but perhaps something like:
pg_query = "with thedata( {0} name, {1} phone, {2} contact, {3} line1, {4} city, {5} state, {6} zip) as
, theinsert (cli_id) as
( insert into clients(name, phone, contact)
select name, phone, contact
from thedata
returning cli_id
)
insert into addresses(cli_id, line1, city, state, zip)
select cli_id, line1, city, state, zip
from theinsert
cross join thedata".format(data['name'], data['phone'], data['contact']
, address['line1'], address['city'], address['state'], address['zip']);

Running A FOREACH after a CTE in a PostgreSQL procedure?

I have a PLPGSQL procedure that:
A) Inserts an employee into an Employee table.
B) Also insert the generated serial eid into another Manager table.
C) Finally, the procedure also dictates an array of course_areas that I would like to insert in a specialize table.
Procedure where C and D array are the course_areas
CALL add_employee('Athena', '22222222', 'athena#outlook.com', '2012-12-12', '111', 'instructor', 300.0, NULL,
'{C,D}'::text[]);
Employees
CREATE TABLE Employees (
eid SERIAL PRIMARY KEY,
name TEXT NOT NULL,
phone TEXT NOT NULL,
email TEXT NOT NULL,
join_date DATE NOT NULL,
address TEXT NOT NULL,
depart_date DATE
);
Managers
CREATE TABLE Managers (
eid INT PRIMARY KEY,
monthly_salary DECIMAL(10,2) NOT NULL,
FOREIGN KEY (eid) REFERENCES Employees(eid)
ON DELETE CASCADE
);
Specializes
CREATE TABLE Specializes (
eid INT NOT NULL,
name TEXT NOT NULL,
PRIMARY KEY (eid, name),
FOREIGN KEY (eid) REFERENCES Employees(eid)
on DELETE CASCADE,
FOREIGN KEY (name) REFERENCES Course_areas(name)
on DELETE CASCADE
);
procedure.sql
CREATE OR REPLACE PROCEDURE add_employee (name TEXT, phone TEXT, email TEXT, joinDate DATE, address TEXT, category TEXT, monthlySalary DECIMAL(10,2) default NULL, hourlySalary DECIMAL(10,2) default NULL, courseAreas TEXT[] default NULL)
...
WITH FIRST AS (
INSERT INTO Employees(
name, phone, email, join_date, address
)
VALUES
(
name, phone, email, joinDate, address
) RETURNING eid
),
SECOND AS (
INSERT INTO Full_time_instructors(eid, monthly_salary)
SELECT
eid,
monthlySalary
FROM
ROWS RETURNING eid
)
FOREACH area IN ARRAY courseAreas
LOOP
RAISE NOTICE '%', area; -- should print "C" and "D"
END LOOP;
Error
ERROR: syntax error at or near "FOREACH"
LINE 27: FOREACH area IN ARRAY courseAreas
I can get A) and B) to work.
How can I use FOREACH to iterate through my courseAreas that I pass to the procedure such that I can insert each course area and the eid into a Specialize table?
The FOREACH is a PLPGSQL control structure whereas the CTE is a SQL feature. You can't mix these. Instead of using CTE you can simply perform the first statement, using the RETURNING clause to retrieve the eid to a local variable. You can then use this variable with the subsequent statement and inside your FOREACH loop.
This question gives an overview of getting the new serial value using returning: Get default serial value after INSERT inside PL/pgSQL.

How to insert and then update returned Id from insert query as returning id in a single command in postgres?

I have a demo table
CREATE TABLE items (
id SERIAL primary key,
user_id integer,
name character varying,
created timestamp with time zone default now()
);
And I want a single query to run and first insert data, then return primary key using returning id and then update the same table with the returned id.
INSERT INTO items (name) values ('pen') RETURNING id as idd
update items set user_id=(select idd) where id=(select idd)
but the above command doesn't work and throws syntax error.
Any help will be appriciated.
You can do that right within the INSERT statement:
INSERT INTO items
(name, user_id)
values
('pen', currval(pg_get_serial_sequence('items','id')));
Online example
You can try this way also :
create temp table insert_item as
with insert_item_cte as (
INSERT INTO items (name)
values ('pen') returning id
)
select id from insert_item_cte;
update items set user_id = items.id
from insert_item ii
where ii.id = items.id;
Online Demo

Firebird update or insert primary key violation

I try to update or insert into a Firebird database table.
But after a new entry, the next time I try to update I get a PK violation.
Both times I use:
UPDATE OR INSERT INTO NAMES ( ID, NAME , SURENAME ) VALUES ( 123, 'Peter', 'Miller' ) matching (Name)
But each time with different surnames.
The ID is a PK in the table. And I generate the ID at the first time.

How to implicitly insert SERIAL ID via view over more than one table

I have two tables, connected in E/R by a is-relation. One representing the "mother table"
CREATE TABLE PERSONS(
id SERIAL NOT NULL,
name character varying NOT NULL,
address character varying NOT NULL,
day_of_creation timestamp NOT NULL DEFAULT current_timestamp,
PRIMARY KEY (id)
)
the other representing the "child table"
CREATE TABLE EMPLOYEES (
id integer NOT NULL,
store character varying NOT NULL,
paychecksize integer NOT NULL,
FOREIGN KEY (id)
REFERENCES PERSONS(id),
PRIMARY KEY (id)
)
Now those two tables are joined in a view
CREATE VIEW EMPLOYEES_VIEW AS
SELECT
P.id,name,address,store,paychecksize,day_of_creation
FROM
PERSONS AS P
JOIN
EMPLOYEES AS E ON P.id = E.id
I want to write either a rule or a trigger to enable a db user to make an insert on that view, sparing him the nasty details of the splitted columns into different tables.
But I also want to make it convenient, as the id is a SERIAL and the day_of_creation has a default value there is no actual need that a user has to provide those, therefore a statement like
INSERT INTO EMPLOYEES_VIEW (name, address, store, paychecksize)
VALUES ("bob", "top secret", "drugstore", 42)
should be enough to result in
PERSONS
id|name|address |day_of_creation
-------------------------------
1 |bob |top secret| 2013-08-13 15:32:42
EMPLOYEES
id| store |paychecksize
---------------------
1 |drugstore|42
A basic rule would be easy as
CREATE RULE EMPLOYEE_VIEW_INSERT AS ON INSERT TO EMPLOYEE_VIEW
DO INSTED (
INSERT INTO PERSONS
VALUES (NEW.id,NEW.name,NEW.address,NEW.day_of_creation),
INSERT INTO EMPLOYEES
VALUES (NEW.id,NEW.store,NEW.paychecksize)
)
should be sufficient. But this will not be convenient as a user will have to provide the id and timestamp, even though it actually is not necessary.
How can I rewrite/extend that code base to match my criteria of convenience?
Something like:
CREATE RULE EMPLOYEE_VIEW_INSERT AS ON INSERT TO EMPLOYEES_VIEW
DO INSTEAD
(
INSERT INTO PERSONS (id, name, address, day_of_creation)
VALUES (default,NEW.name,NEW.address,default);
INSERT INTO EMPLOYEES (id, store, paychecksize)
VALUES (currval('persons_id_seq'),NEW.store,NEW.paychecksize)
);
That way the default values for persons.id and persons.day_of_creation will be the default values. Another option would have been to simply remove those columns from the insert:
INSERT INTO PERSONS (name, address)
VALUES (NEW.name,NEW.address);
Once the rule is defined, the following insert should work:
insert into employees_view (name, address, store, paychecksize)
values ('Arthur Dent', 'Some Street', 'Some Store', 42);
Btw: with a current Postgres version an instead of trigger is the preferred way to make a view updateable.