Postgers: No unique or exclusion constraint matching ON CONFLICT specification when using a primary key and a UNIQUE column - sql

Here is the schema
CREATE TABLE instagram_posts (
post_id serial PRIMARY KEY,
user_id int REFERENCES instagram_users ON DELETE RESTRICT,
link TEXT UNIQUE,
caption TEXT,
num_likes int,
num_comments int,
exsits BOOLEAN NOT NULL DEFAULT TRUE);
And I would try to call the command:
INSERT INTO instagram_posts (link,num_likes,user_id) VALUES (%s,%s,(SELECT user_id from instagram_users WHERE user_account=%s)) ON CONFLICT (post_id, link) DO UPDATE SET num_likes=EXCLUDED.num_likes,user_id=EXCLUDED.user_id;
Which gives me an Error: there is no unique or exclusion constraint matching the ON CONFLICT specification
The query works if I only include post_id as a constraint or only include the link as a constraint (ie ON CONFLICT (post_id) or ON CONFLICT(link)) but not both. I cannot think of a reason why that is. Does anyone know why?

For exactly the reason it tells you — you don't have a unique constraint on (post_id, link) to power the ON CONFLICT clause. You could create such a constraint, but that seems rather pointless when the two keys making up the composite key are already individually unique. Probably ON CONFLICT (link) is exactly what you want, but it's not completely clear what you're trying to accomplish.

Related

SQL How to not insert duplicated values

I'm trying to create a procedure that inserts data into a table of registers but i don't want to repeat the second parameter, this is the table
CREATE TABLE Inscription
(
idClass INT references tb_class,
idStudent INT references tb_student,
)
The idea is that a student (idStudent) can register in various classes but not in the same class (idClass), I tried to add a unique constraint in the idStudent column but that only allows a student to register in one single class.
I always suggest that all tables have a numeric primary key. In addition, your foreign key references are not correct. And what you want to do is add a unique constraint.
The exact syntax depends on the database. The following is for SQL Server:
CREATE TABLE Inscriptions (
idInscription int identity(1, 1) primary key
idClass int references tb_classes(idClass),
idStudent int references tb_students(idStudnt)
unique (idClass, idStudent)
);
Notice that I name the tables as the plural of the entity, but the id using the singular.
The Inscriptions table probably wants other columns as well, such as the date/time of the inscription, the method, and other related information.
You are looking to create a constraint on your table that includes both columns idClass and idStudent.
Once that constraint is created, an attempt to insert duplicate class/student will result in an error being raised.
As your table does not seem to include a primary key, you would better make that constraint your primary key.
NB : you did not tell which RDBMS you are using hence cannot give you the exact syntax to use...
Your unique key needs to encompass both idClass and idStudent, so any particular combination cannot repeat itself.

column deleted, now want to add constraint back

I deleted a column in a table, using ALTER TABLE. I didn't realize that there was a constraint attached to it. Now the constraint also got deleted. How do I add the constraint back? I don't know the exact wording of that constraint. Also, the table was empty, so in that moment I thought the column was never going to be populated and removed it.
Here is the table CREATE query in pgadmin3:
CREATE TABLE employee_grade
(
id uuid NOT NULL DEFAULT uuid_generate_v4(),
graded_to uuid,
grade character varying(10),
graded_by uuid,
grade_date timestamp with time zone,
grade_month timestamp with time zone,
CONSTRAINT employee_grade_pkey PRIMARY KEY (id),
CONSTRAINT employee_grade_by_fk FOREIGN KEY (graded_by)
REFERENCES employee (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT employee_grade_to_fk FOREIGN KEY (graded_to)
REFERENCES employee (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
I don't know what the constraint was, but I do know that it involved the graded_by and graded_to foreign keys, and the grade_month column. It was also unique.
It probably meant that in that month, graded_by graded graded_to, and there should not be two gradings in the same month for the same two employees.
Also, if it helps, the corresponding Java entity does not have a corresponding gradeMonth column.
Turns out the answer indeed was a simple UNIQUE constraint:
CONSTRAINT grade_month_uniq UNIQUE (graded_to, graded_by, grade_month)
I used this and it works fine for the intended purpose.
Thanks #NickBarnes.

Why can't I add this foreign key?

I'll post only the main part. I have two tables, each one has to have the PK of the other as a FK.
CREATE TABLE apartment
(
cod_apartment INT NOT NULL PRIMARY KEY,
cod_offer INT NOT NULL
);
CREATE TABLE offer
(
cod_offer INT NOT NULL PRIMARY KEY,
cod_apartment INT NOT NULL
);
First I inserted the values on both tables and it was working, I could even search using "select * from...". But then I tried to add the foreign key:
This worked.
ALTER TABLE offer
ADD FOREIGN KEY (cod_apartment ) REFERENCES apartment;
And this not.
ALTER TABLE apartment
ADD FOREIGN KEY (cod_offer) REFERENCES offer;
This is the error message:
The ALTER TABLE statement conflicted with the FOREIGN KEY constraint "FK__apartment__cod_offer__6383C8BA". The conflict occurred in database "kleber_apartment", table "dbo.offer", column 'cod_offer'.
The problem is, every time I try to execute, the FK name changes. And this FK actually doesn't exist. I already dropped both tables and tried to insert the values again, but the same happens.
What could be?
That means you're trying to add a foreign key when existing data doesn't obey that constraint. So you have a record in your apartment table where the cod_offer column does not match any value in the cod_apartment table.
Adding a foreign key not only constrains future data, but it requires that any existing data must also follow the rule.
And regarding the 6383C8BA, whenever you add a constraint without giving it a name, SQL Server picks one for you. Personally, I'd recommend something like:
alter table dbo.apartment
add constraint FK_apartment__cod_offer
foreign key (cod_offer) references dbo.offer (cod_offer);
This lets you define names the way you want, and is a little more clear about what you're actually building.

PostgreSQL NULLable foreign key index optimization

PostgreSQL 9.2. Two variations of records can be stored in the following table. alarmConfigurations entries where ac_p_id is NOT NULL can exist more than once, but those where ac_p_id is NULL must only exist once (hence the ac_unique_row_no_port constraint).
As you can see, regular indexes are created on all other foreign keys, these get scanned a lot. But for ac_p_id...
Is there any particular advantage to creating the non-unique constraint ac_index_p_id with IS NOT NULL (as opposed to not specifying, like the others)?
In fact, if I only create ac_unique_row_no_port, will that also be used for scans where ac_p_id is NOT NULL?
Thanks in advance.
CREATE TABLE alarmConfigurations (
ac_id SERIAL PRIMARY KEY,
ac_ad_code TEXT NOT NULL,
ac_ad_et_code TEXT NOT NULL,
ac_e_id INTEGER NOT NULL,
ac_as_code TEXT,
ac_p_id INTEGER,
ac_details HSTORE, -- extra configuration
CONSTRAINT ac_ad_fkey FOREIGN KEY (ac_ad_code, ac_ad_et_code)
REFERENCES alarmDefinitions (ad_code, ad_et_code)
ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT ac_as_code_fkey FOREIGN KEY (ac_as_code)
REFERENCES alarmSeverities (as_code)
ON UPDATE CASCADE ON DELETE RESTRICT,
CONSTRAINT ac_e_id_fkey FOREIGN KEY (ac_e_id)
REFERENCES entities (e_id) ON DELETE CASCADE,
CONSTRAINT ac_p_id_fkey FOREIGN KEY (ac_p_id)
REFERENCES ports (p_id) ON DELETE CASCADE
);
CREATE INDEX ac_index_e_id ON alarmConfigurations(ac_e_id);
CREATE INDEX ac_index_ad_code ON alarmConfigurations(ac_ad_code);
CREATE INDEX ac_index_ad_et_code ON alarmConfigurations(ac_ad_et_code);
CREATE INDEX ac_index_p_id ON alarmConfigurations(ac_p_id)
WHERE ac_p_id IS NOT NULL;
CREATE UNIQUE INDEX ac_unique_row_no_port
ON alarmConfigurations(ac_ad_code, ac_ad_et_code, ac_e_id)
WHERE ac_p_id IS NULL;
Is there any particular advantage to creating the non-unique constraint ac_index_p_id with IS NOT NULL (as opposed to not specifying, like the others)?
Personally, I'd leave the where clause out on that one, in case you ever want to run a query with an arbitrary where ac_p_id is null or ac_p_id = ? or some other convoluted expression.
In fact, if I only create ac_unique_row_no_port, will that also be used for scans where ac_p_id is NOT NULL?
No. It'll only get used if you explicitly specify ac_p_id is null in your clauses. The entire where clause used in the index must be around in the query for it to be considered. (Give or take minor rewrites that PG deals with, but don't count on them without testing.)

Correct way to create a table that references variables from another table

I have these relationships:
User(uid:integer,uname:varchar), key is uid
Recipe(rid:integer,content:text), key is rid
Rating(rid:integer, uid:integer, rating:integer) , key is (uid,rid).
I built the table in the following way:
CREATE TABLE User(
uid INTEGER PRIMARY KEY ,
uname VARCHAR NOT NULL
);
CREATE TABLE Recipes(
rid INTEGER PRIMARY KEY,
content VARCHAR NOT NULL
);
Now for the Rating table: I want it to be impossible to insert a uid\rid that does not exist in User\Recipe.
My question is: which of the following is the correct way to do it? Or please suggest the correct way if none of them are correct. Moreover, I would really appreciate if someone could explain to me what is the difference between the two.
First:
CREATE TABLE Rating(
rid INTEGER,
uid INTEGER,
rating INTEGER CHECK (0<=rating and rating<=5) NOT NULL,
PRIMARY KEY(rid,uid),
FOREIGN KEY (rid) REFERENCES Recipes,
FOREIGN KEY (uid) REFERENCES User
);
Second:
CREATE TABLE Rating(
rid INTEGER REFERENCES Recipes,
uid INTEGER REFERENCES User,
rating INTEGER CHECK (0<=rating and rating<=5) NOT NULL,
PRIMARY KEY(rid,uid)
);
EDIT:
I think User is problematic as a name for a table so ignore the name.
Technically both versions are the same in Postgres. The docs for CREATE TABLE say so quite clearly:
There are two ways to define constraints: table constraints and column constraints. A column constraint is defined as part of a column definition. A table constraint definition is not tied to a particular column, and it can encompass more than one column. Every column constraint can also be written as a table constraint; a column constraint is only a notational convenience for use when the constraint only affects one column.
So when you have to reference a compound key a table constraint is the only way to go.
But for every other case I prefer the shortest and most concise form where I don't need to give names to stuff I'm not really interested in. So my version would be like this:
CREATE TABLE usr(
uid SERIAL PRIMARY KEY ,
uname TEXT NOT NULL
);
CREATE TABLE recipes(
rid SERIAL PRIMARY KEY,
content TEXT NOT NULL
);
CREATE TABLE rating(
rid INTEGER REFERENCES recipes,
uid INTEGER REFERENCES usr,
rating INTEGER NOT NULL CHECK (rating between 0 and 5),
PRIMARY KEY(rid,uid)
);
This is a SQL Server based solution, but the concept applies to most any RDBMS.
Like so:
CREATE TABLE Rating (
rid int NOT NULL,
uid int NOT NULL,
CONSTRAINT PK_Rating PRIMARY KEY (rid, uid)
);
ALTER TABLE Rating ADD CONSTRAINT FK_Rating_Recipies FOREIGN KEY(rid)
REFERENCES Recipies (rid);
ALTER TABLE Rating ADD CONSTRAINT FK_Rating_User FOREIGN KEY(uid)
REFERENCES User (uid);
This ensures that the values inside of Rating are only valid values inside of both the Users table and the Recipes table. Please note, in the Rating table I didn't include the other fields you had, just add those.
Assume in the users table you have 3 users: Joe, Bob and Bill respective ID's 1,2,3. And in the recipes table you had cookies, chicken pot pie, and pumpkin pie respective ID's are 1,2,3. Then inserting into Rating table will only allow for these values, the minute you enter 4 for a RID or a UID SQL throws an error and does not commit the transaction.
Try it yourself, its a good learning experience.
In Postgresql a correct way to implement these tables are:
CREATE SEQUENCE uid_seq;
CREATE SEQUENCE rid_seq;
CREATE TABLE User(
uid INTEGER PRIMARY KEY DEFAULT nextval('uid_seq'),
uname VARCHAR NOT NULL
);
CREATE TABLE Recipes(
rid INTEGER PRIMARY KEY DEFAULT nextval('rid_seq'),
content VARCHAR NOT NULL
);
CREATE TABLE Rating(
rid INTEGER NOT NULL REFERENCES Recipes(rid),
uid INTEGER NOT NULL REFERENCES User(uid),
rating INTEGER CHECK (0<=rating and rating<=5) NOT NULL,
PRIMARY KEY(rid,uid)
);
There is no real difference between the two options that you have written.
A simple (i.e. single-column) foreign key may be declared in-line with the column declaration or not. It's merely a question of style. A third way should be to omit foreign key declarations from the CREATE TABLE entirely and later add them using ALTER TABLE statements; done in a transaction (presumable along with all the other tables, constraints, etc) the table would never exist without its required constraints. Choose whichever you think is easiest fora human coder to read and understand i.e. is easiest to maintain.
EDIT: I overlooked the REFERENCES clause in the second version when I wrote my original answer. The two versions are identical in terms of referential integrity, there are just two ways of syntax to do this.