How do I create this FOREIGN KEY? - Postgres - postgresql

I have 2 tables. I am trying to create a FORIEGN KEY. Here is my first table:
CREATE TABLE bills(
id serial,
name varchar(100),
payment decimal(12, 2),
payoff decimal(12, 2),
type varchar(25)
)
When I try to create a second table:
CREATE TABLE pay_dates(
id serial,
bill_id integer REFERENCES bills(id),
due_date date,
pay_date date,
paid boolean
)
I get this error:
ERROR: there is no unique constraint matching given keys for referenced table "bills".
What am I doing wrong?

The referenced column has to be unique. You should declare id as primary key:
CREATE TABLE bills(
id serial primary key,
...
Primary key (or unique) constraint must be declared explicit. Type serial ensures only that the default value is the next value from a sequence. Example:
create table test (val1 serial, val2 serial);
insert into test (val2) values (1), (2), (default), (3), (4), (default);
select * from test;
val1 | val2
------+------
1 | 1
2 | 2
3 | 1
4 | 3
5 | 4
6 | 2
(6 rows)

The parent table bills (id) must have either unique or primary key constraint.
What is causing ERROR: there is no unique constraint matching given keys for referenced table?

Related

How can i create auto increment column to my existing column?

I am working with Oracle 12c in which I have below table structure:-
CREATE TABLE patients (
patient_id Integer NOT NULL,
customer_id Integer NOT NULL,
title varchar(5) NOT NULL,
fname varchar(125) NOT NULL,
lname varchar(125) NOT NULL,
dob date NOT NULL,
is_medical_card NUMBER(1) NOT NULL CHECK (is_medical_card IN (0,1)),
scheme_number Integer NOT NULL,
status varchar(50) NOT NULL,
created_on date NOT NULL,
last_update_date date NOT NULL,
consent_flag NUMBER(1) NOT NULL CHECK (consent_flag IN (0,1)),
relationship varchar(50) NOT NULL
);
Where patient_id is my primary key so now I want to make it auto increment as well so please let me how can I do this so make it auto increment.
Thanks!
Need to create auto increment to existing column.
You might want to use Identities - Creating a table with an Identity gives you the chance to omit the ID values and let Oracle use a sequence on your desired column:
1. Let's Create the Table:
CREATE TABLE identities (
id NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY,
description varchar2(100) NOT NULL
);
Table created.
2. You'll want to create a primary key to ensure uniqueness:
alter table identities add constraint id_pk primary key (ID);
Table altered.
3. Let's insert some data in different ways:
INSERT INTO identities (description)
VALUES('Insert Description omitting ID');
1 row created.
INSERT INTO identities (id,description)
VALUES(NULL,'Insert with explicit NULL value');
1 row created.
4. Save the work done
commit;
Commit complete.
5. Check the results
select * from identities;
ID DESCRIPTION
---------- ---------------------------------------------
1 Insert Description omitting ID
2 Insert with explicit NULL value
As you can see we dind't specify any number for the ID, but the Identity on the ID column did for us
Note: Mind that you can manually insert an ID, but this will mess up with the Identity as it'll normally do with a standard Sequence:
INSERT INTO identities (id,description)
VALUES(3,'Manually insert an ID value');
1 row created.
INSERT INTO identities (description)
VALUES('Test Nextval');
INSERT INTO identities (description)
*
ERROR at line 1:
ORA-00001: unique constraint (XXX.ID_PK) violated
This error, because it tries to insert a '3' into the ID that was manually inserted with the statement before.
Check the table:
select * from identities;
ID DESCRIPTION
---------- ---------------------------------------------
1 Insert Description omitting ID
2 Insert with explicit NULL value
3 Manually insert an ID value
Re-Run the "NEXTVAL" insert:
INSERT INTO identities (description)
VALUES('Test Nextval');
1 row created.
Re-Check the table:
select * from identities;
ID DESCRIPTION
---------- ---------------------------------------------
1 Insert Description omitting ID
2 Insert with explicit NULL value
3 Manually insert an ID value
4 Test Nextval
Hope this Helps.

How to leave a row in the table with value of foreign key in, if a row with a foreign key is deleted?

There is table1:
CREATE TABLE table1
(
id serial NOT NULL,
CONSTRAINT pk_table1_id PRIMARY KEY (id),
)
There is table2:
CREATE TABLE table2
(
id serial NOT NULL,
fk_1 integer,
CONSTRAINT fk_to_table1 FOREIGN KEY (fk_1)
REFERENCES table1 (id) MATCH SIMPLE
ON UPDATE CASCADE ON DELETE NO ACTION
NOT VALID
)
When I try to delete some row from table1 I got exception:
UPDATE or DELETE in the table "table1" violates the foreign key constraint "fk_to_table1" of the table "table2"
Is there some way to leave(save) a row in the table2 with same value in fk_1 (id from table1) if a row with a foreign key from table1 is deleted?
Example:
Table1:
id
1
2
3
Teble2:
id fk_1
1 | 1
2 | 1
3 | 3
4 | 2
What I want to see after delete first row from table1:
Table1:
id
2
3
Teble2:
id fk_1
1 | 1
2 | 1
3 | 3
4 | 2
You can do that using ON DELETE SET NULL:
CREATE TABLE table2
(
id int NOT NULL,
fk_1 integer,
CONSTRAINT fk_to_table1 FOREIGN KEY (fk_1)
REFERENCES table1 (id)
ON UPDATE CASCADE
ON DELETE SET NULL
);
insert into table1 values (1), (2);
insert into table2 values (1,1), (2,1), (3,2), (4,2);
Then if you run:
delete from table1
where id = 1;
Table2 will look like this:
id | fk_1
---+-----
1 |
2 |
3 | 2
4 | 2
Online example: http://rextester.com/NPUG18060
The short answer is: you cannot.
the purpose of a foreign key is to verify that whenever that attribute has a value, the value is in the referenced table (except when the value is NULL---NULL does not need to exist in the table).
Two suggestions:
Solution 1. Keep the tuple in Table2, do not delete it. Instead, add a boolean attribute that determines is the tuple has been deleted.
Specifically for your problem. Add an attribute deleted to the table.
Set if to default to false. And when the tuple is deleted
create a trigger to set this attribute to true instead. This is cumbersome
and error prone.
Solution 2 (this is my preferred solution). You can use a third table that is managed via triggers.
Every time a tuple in the table2 is added, a tuple to the table3 is
added. Table3 will only have one attribute (the one you want to have
the foreign key referenced to). Table3 will be the set of all values of the attribute that have been part of Table2. The trigger to add to Table3 should use upsert, in case the attribute being added has been seen before.
Table1 will then have the foreign key constraint pointing to Table3.

Can i set values to only be allowed in a single "group"

I have a table similar to that looks something like this:
code | group
------+------
1 | a
1 | a
2 | a
9 | b
9 | b
8 | b
Is there a way to set a constraint so that the same code can appear only in one group?
So that for example adding a row code=3; group=a would not violate the constraint, but adding a row code=1; group=b would be a violation of the constraint.
I don't believe you can do it if these fields are in the same table. It looks to me that you have a hierarchy in which group is the master level and code is detail. I see this can be solved with 3 tables:
CREATE TABLE "group" (
id "char" CONSTRAINT "group_pk" PRIMARY KEY
)
CREATE TABLE "code" (
id INTEGER CONSTRAINT "code_pk" PRIMARY KEY,
group_id "char",
FOREIGN KEY group_id REFERENCES "group" (id)
)
CREATE TABLE "data" (
id INTEGER CONSTRAINT "data_pk" PRIMARY KEY,
code_id "char",
...
FOREIGN KEY code_id REFERENCES "code" (id)
)
where data table represents the table you are talking about.

Inserting self-referential records in Postgresql

Given the following table in PostgreSQL, how do I insert a record which refers to itself?
CREATE TABLE refers (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
parent_id INTEGER NOT NULL,
FOREIGN KEY (parent_id) REFERENCES refers(id)
);
The examples I'm finding on the Web have been allowed the parent_id to be NULL and then use a trigger to update it. I'd rather update in one shot, if possible.
You can select last_value from the sequence, that is automatically created when you use type serial:
create table test (
id serial primary key,
parent integer not null,
foreign key (parent) references test(id)
);
insert into test values(default, (select last_value from test_id_seq));
insert into test values(default, (select last_value from test_id_seq));
insert into test values(default, (select last_value from test_id_seq));
select * from test;
id | parent
----+--------
1 | 1
2 | 2
3 | 3
(3 rows)
And the following even simpler seems to work as well:
insert into test values(default, lastval());
Though I don't know how this would work when using multiple sequences... I looked it up; lastval() returns the last value returned or set with the last nextval or setval call to any sequence, so the following would get you in trouble:
create table test (
id serial primary key,
foo serial not null,
parent integer not null,
foreign key (parent) references test(id)
);
select setval('test_foo_seq', 100);
insert into test values(default, default, lastval());
ERROR: insert or update on table "test" violates foreign key constraint "test_parent_fkey"
DETAIL: Key (parent)=(101) is not present in table "test".
However the following would be okay:
insert into test values(default, default, currval('test_id_seq'));
select * from test;
id | foo | parent
----+-----+--------
2 | 102 | 2
(1 row)
The main question is - why would you want to insert record which relates to itself?
Schema looks like standard adjacency list - one of methods to implement trees in relational database.
The thing is that in most cases you simply have parent_id NULL for top-level element. This is actually much simpler to handle.

What's the PostgreSQL datatype equivalent to MySQL AUTO INCREMENT?

I'm switching from MySQL to PostgreSQL and I was wondering how can I have an INT column with AUTO INCREMENT. I saw in the PostgreSQL docs a datatype called SERIAL, but I get syntax errors when using it.
Yes, SERIAL is the equivalent function.
CREATE TABLE foo (
id SERIAL,
bar varchar
);
INSERT INTO foo (bar) VALUES ('blah');
INSERT INTO foo (bar) VALUES ('blah');
SELECT * FROM foo;
+----------+
| 1 | blah |
+----------+
| 2 | blah |
+----------+
SERIAL is just a create table time macro around sequences. You can not alter SERIAL onto an existing column.
You can use any other integer data type, such as smallint.
Example :
CREATE SEQUENCE user_id_seq;
CREATE TABLE user (
user_id smallint NOT NULL DEFAULT nextval('user_id_seq')
);
ALTER SEQUENCE user_id_seq OWNED BY user.user_id;
Better to use your own data type, rather than user serial data type.
If you want to add sequence to id in the table which already exist you can use:
CREATE SEQUENCE user_id_seq;
ALTER TABLE user ALTER user_id SET DEFAULT NEXTVAL('user_id_seq');
Starting with Postgres 10, identity columns as defined by the SQL standard are also supported:
create table foo
(
id integer generated always as identity
);
creates an identity column that can't be overridden unless explicitly asked for. The following insert will fail with a column defined as generated always:
insert into foo (id)
values (1);
This can however be overruled:
insert into foo (id) overriding system value
values (1);
When using the option generated by default this is essentially the same behaviour as the existing serial implementation:
create table foo
(
id integer generated by default as identity
);
When a value is supplied manually, the underlying sequence needs to be adjusted manually as well - the same as with a serial column.
An identity column is not a primary key by default (just like a serial column). If it should be one, a primary key constraint needs to be defined manually.
Whilst it looks like sequences are the equivalent to MySQL auto_increment, there are some subtle but important differences:
1. Failed Queries Increment The Sequence/Serial
The serial column gets incremented on failed queries. This leads to fragmentation from failed queries, not just row deletions. For example, run the following queries on your PostgreSQL database:
CREATE TABLE table1 (
uid serial NOT NULL PRIMARY KEY,
col_b integer NOT NULL,
CHECK (col_b>=0)
);
INSERT INTO table1 (col_b) VALUES(1);
INSERT INTO table1 (col_b) VALUES(-1);
INSERT INTO table1 (col_b) VALUES(2);
SELECT * FROM table1;
You should get the following output:
uid | col_b
-----+-------
1 | 1
3 | 2
(2 rows)
Notice how uid goes from 1 to 3 instead of 1 to 2.
This still occurs if you were to manually create your own sequence with:
CREATE SEQUENCE table1_seq;
CREATE TABLE table1 (
col_a smallint NOT NULL DEFAULT nextval('table1_seq'),
col_b integer NOT NULL,
CHECK (col_b>=0)
);
ALTER SEQUENCE table1_seq OWNED BY table1.col_a;
If you wish to test how MySQL is different, run the following on a MySQL database:
CREATE TABLE table1 (
uid int unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
col_b int unsigned NOT NULL
);
INSERT INTO table1 (col_b) VALUES(1);
INSERT INTO table1 (col_b) VALUES(-1);
INSERT INTO table1 (col_b) VALUES(2);
You should get the following with no fragementation:
+-----+-------+
| uid | col_b |
+-----+-------+
| 1 | 1 |
| 2 | 2 |
+-----+-------+
2 rows in set (0.00 sec)
2. Manually Setting the Serial Column Value Can Cause Future Queries to Fail.
This was pointed out by #trev in a previous answer.
To simulate this manually set the uid to 4 which will "clash" later.
INSERT INTO table1 (uid, col_b) VALUES(5, 5);
Table data:
uid | col_b
-----+-------
1 | 1
3 | 2
5 | 5
(3 rows)
Run another insert:
INSERT INTO table1 (col_b) VALUES(6);
Table data:
uid | col_b
-----+-------
1 | 1
3 | 2
5 | 5
4 | 6
Now if you run another insert:
INSERT INTO table1 (col_b) VALUES(7);
It will fail with the following error message:
ERROR: duplicate key value violates unique constraint "table1_pkey"
DETAIL: Key (uid)=(5) already exists.
In contrast, MySQL will handle this gracefully as shown below:
INSERT INTO table1 (uid, col_b) VALUES(4, 4);
Now insert another row without setting uid
INSERT INTO table1 (col_b) VALUES(3);
The query doesn't fail, uid just jumps to 5:
+-----+-------+
| uid | col_b |
+-----+-------+
| 1 | 1 |
| 2 | 2 |
| 4 | 4 |
| 5 | 3 |
+-----+-------+
Testing was performed on MySQL 5.6.33, for Linux (x86_64) and PostgreSQL 9.4.9
Sorry, to rehash an old question, but this was the first Stack Overflow question/answer that popped up on Google.
This post (which came up first on Google) talks about using the more updated syntax for PostgreSQL 10:
https://blog.2ndquadrant.com/postgresql-10-identity-columns/
which happens to be:
CREATE TABLE test_new (
id int GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
);
Hope that helps :)
You have to be careful not to insert directly into your SERIAL or sequence field, otherwise your write will fail when the sequence reaches the inserted value:
-- Table: "test"
-- DROP TABLE test;
CREATE TABLE test
(
"ID" SERIAL,
"Rank" integer NOT NULL,
"GermanHeadword" "text" [] NOT NULL,
"PartOfSpeech" "text" NOT NULL,
"ExampleSentence" "text" NOT NULL,
"EnglishGloss" "text"[] NOT NULL,
CONSTRAINT "PKey" PRIMARY KEY ("ID", "Rank")
)
WITH (
OIDS=FALSE
);
-- ALTER TABLE test OWNER TO postgres;
INSERT INTO test("Rank", "GermanHeadword", "PartOfSpeech", "ExampleSentence", "EnglishGloss")
VALUES (1, '{"der", "die", "das", "den", "dem", "des"}', 'art', 'Der Mann küsst die Frau und das Kind schaut zu', '{"the", "of the" }');
INSERT INTO test("ID", "Rank", "GermanHeadword", "PartOfSpeech", "ExampleSentence", "EnglishGloss")
VALUES (2, 1, '{"der", "die", "das"}', 'pron', 'Das ist mein Fahrrad', '{"that", "those"}');
INSERT INTO test("Rank", "GermanHeadword", "PartOfSpeech", "ExampleSentence", "EnglishGloss")
VALUES (1, '{"der", "die", "das"}', 'pron', 'Die Frau, die nebenen wohnt, heißt Renate', '{"that", "who"}');
SELECT * from test;
In the context of the asked question and in reply to the comment by #sereja1c, creating SERIAL implicitly creates sequences, so for the above example-
CREATE TABLE foo (id SERIAL,bar varchar);
CREATE TABLE would implicitly create sequence foo_id_seq for serial column foo.id. Hence, SERIAL [4 Bytes] is good for its ease of use unless you need a specific datatype for your id.
Since PostgreSQL 10
CREATE TABLE test_new (
id int GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
payload text
);
This way will work for sure, I hope it helps:
CREATE TABLE fruits(
id SERIAL PRIMARY KEY,
name VARCHAR NOT NULL
);
INSERT INTO fruits(id,name) VALUES(DEFAULT,'apple');
or
INSERT INTO fruits VALUES(DEFAULT,'apple');
You can check this the details in the next link:
http://www.postgresqltutorial.com/postgresql-serial/
Create Sequence.
CREATE SEQUENCE user_role_id_seq
INCREMENT 1
MINVALUE 1
MAXVALUE 9223372036854775807
START 3
CACHE 1;
ALTER TABLE user_role_id_seq
OWNER TO postgres;
and alter table
ALTER TABLE user_roles ALTER COLUMN user_role_id SET DEFAULT nextval('user_role_id_seq'::regclass);