Uniqe constrain on condition psql - sql

I required to implement unique constrain on psql table
i have columns,
1) date
2) employee
3) client_id
4) start_time
trying to add two constrain like,
1) check unique rule for date, employee, start_time, client_id this will simply work with unique constrain
BUT SECOND CONSTRAIN Is for condition where we already create entry with date,employee,start_time and client_id is False
-> so if someone try to create same entry we require to check constrain like,
does any entry already exist with fields "date, employee_id,start_time" AND client_id= False
in SIMPLE words
1) check if all 4 fields exist with unique constrain > display warning record exist
2) check if record with 3 fields and client_id = null exist > display warning record exist assign client_id
if anybody have little hint
it would be helpful

I think you need partial indexes. One will cover case when client_id is provided and other will deal with NULL client_id.
create table uni(val1 int, val2 text, val3 date, client_id int);
create unique index record_exists on uni(val1, val2, val3, client_id)
where client_id is not null;
create unique index record_exists_assign_client_id on uni(val1, val2, val3)
where client_id is null;
insert into uni values (1, 'test', current_date, 43), (2, 'test2', current_date, null);
--OK
insert into uni values (1, 'test', current_date, 43);
--duplicate key value violates unique constraint "record_exists"
insert into uni values (1, 'test', current_date, null);
--OK
insert into uni values (2, 'test2', current_date, null);
--duplicate key value violates unique constraint "record_exists_assign_client_id"

Related

Unique constraint on inserting new row

I wrote a SQL statement within PostgreSQL 12 and I first created an unique constraint like:
CONSTRAINT post_comment_response_approval__tm_response__uidx UNIQUE (post_comment_response_id, team_member_id)
On a SQL query:
INSERT INTO post_comment_response_approval (post_comment_response_id, team_member_id, approved, note)
VALUES (:postCommentResponseId, :workspaceMemberId, :approved, :note)
ON CONFLICT ON CONSTRAINT post_comment_response_approval__tm_response__uidx DO
UPDATE SET approved = :approved, note = :note
Fist, I wanted to use it for the same row whenever ever some action is made, but now I just want to make sure the API shows them if multiple actions have been submitted by the same member.
An example is that someone might suggest a change, then that change is made, then that person who suggested it later approves it. That would generate multiple post_comment_response_approval rows for that approver.
Is there a way to make it happen without removing unique constraint or maybe it should be deleted? I am new with PostgreSQL.
I didn't understand your question in detail. But I think I understood what you need. You can use PostgreSQL partial indexing.
Examples for you:
CREATE TABLE table6 (
id int4 NOT NULL,
id2 int4 NOT null,
approve bool NULL
);
-- creating partial indexing
CREATE UNIQUE INDEX table6_id_idx ON table6 (id,id2) where approve is true;
insert into table6 (id, id2, approve) values (1, 1, false);
-- success insert
insert into table6 (id, id2, approve) values (1, 1, false);
-- success insert
insert into table6 (id, id2, approve) values (1, 1, false);
-- success insert
insert into table6 (id, id2, approve) values (1, 1, true);
-- success insert
insert into table6 (id, id2, approve) values (1, 1, true);
-- error: duplicate key value violates unique constraint "table6_id_idx"
So, you get unique fields by condition.

Postgresql tuple constraints about NOT NULL

I am building a database in POSTGRESQL, and I would like to create NOT NULL constraints for my columns, where one and only one column would be NOT NULL.
I have two columns in my table, site_id and buffer_result_id. Only one of these columns will have values.
alter table dt.analysis_result
add constraint ar_check check (site_id NOT NULL OR buffer_result_id NOT NULL);
The above code is just some pseudo-code to show my idea. How can I achieve this function?
You could use XOR expressed as:
alter table dt.analysis_result
add constraint ar_check check (
(site_id IS NOT NULL OR buffer_result_id IS NOT NULL)
AND NOT(site_id IS NOT NULL AND buffer_result_id IS NOT NULL)
);
db<>fiddle demo
More info: Exclusive OR - Equivalences
Demo:
CREATE TABLE analysis_result(site_id INT, buffer_result_id INT);
INSERT INTO analysis_result VALUES (NULL, NULL);
-- ERROR: new row for relation "analysis_result" violates check constraint "ar_check"
INSERT INTO analysis_result VALUES (1, 2);
-- ERROR: new row for relation "analysis_result" violates check constraint "ar_check"
INSERT INTO analysis_result VALUES (NULL, 2);
INSERT INTO analysis_result VALUES (1, NULL);
SELECT * FROM analysis_result
In Postgres, you can do this with a check constraint. I think the simplest method is to count the number of not null values:
alter table dt.analysis_result add constraint ar_check
check ( (site_id is not null)::int + (buffer_result_id is not null)::int = 1
);

Double values in a table

Is it possible to define a ID column to be unique but every value have to be occur twice?
For example:
table TRANSLATION:
id | name_id | translation
____________|_________|____________
1 | 1 | apple
____________|_________|____________
2 | 1 | apfel
____________|_________|____________
3 | 2 | pear
____________|_________|____________
4 | 2 | birne
I want name_id values to always occur twice, not once and not three times. name_id is a FK from table with my objects that needs to be translated.
No, this is impossible to enforce, though you can attempt it using triggers this is normally a pretty messy solution.
I'd change your table structure to be something like the following:
ID
NAME_ID
LANGUAGE_ID
TRANSLATION
You could then create a unique index on NAME_ID and LANGUAGE_ID. Theoretically, you'd also have a table LANGUAGES, and the LANGUAGE_ID column would have a foreign key back into LANGUAGES.ID - you could then restrict the number of times each NAME_ID appears by not having the data in LANGUAGES.
Ultimately this means that your schema would look something like this:
create table languages (
id number
, description varchar2(4000)
, constraint pk_languages primary key (id)
);
insert into languages values (1, 'English');
insert into languages values (2, 'German');
create table names (
id number
, description varchar(4000)
, constraint pk_names primary key (id)
);
insert into names values (1, 'apple');
insert into names values (2, 'pear');
create table translations (
id number
, name_id number
, language_id number
, translation varchar2(4000)
, constraint pk_translations primary key (id)
, constraint fk_translations_names foreign key (name_id) references names (id)
, constraint fk_translations_langs foreign key (language_id) references languages (id)
, constraint uk_translations unique (name_id, language_id)
);
insert into translations values (1, 1, 1, 'apple');
insert into translations values (2, 1, 2, 'apfel');
insert into translations values (3, 2, 1, 'pear');
insert into translations values (4, 2, 2, 'birne');
and you should be unable to break the constraints:
SQL> insert into translations values (5, 1, 3, 'pomme');
insert into translations values (5, 1, 3, 'pomme')
*
ERROR at line 1:
ORA-02291: integrity constraint (FK_TRANSLATIONS_LANGS) violated - parent
key not found
SQL> insert into translations values (5, 1, 2, 'pomme');
insert into translations values (5, 1, 2, 'pomme')
*
ERROR at line 1:
ORA-00001: unique constraint (UK_TRANSLATIONS) violated
See this SQL Fiddle
Do you mean a maximum of twice? or do you mean they have to occur twice (i.e., once only is not ok)
If the former, Once only IS ok) then you could Add a bit field and make the Primary Key composite on the actual id and the bit field.
If the latter (They have to occur twice), then put two id fields in the same row and make then each a single field unique key.

how to create a Foreign-Key constraint to a subset of the rows of a table?

I have a reference table, say OrderType that collects different types of orders:
CREATE TABLE IF NOT EXISTS OrderType (name VARCHAR);
ALTER TABLE OrderType ADD PRIMARY KEY (name);
INSERT INTO OrderType(name) VALUES('sale-order-type-1');
INSERT INTO OrderType(name) VALUES('sale-order-type-2');
INSERT INTO OrderType(name) VALUES('buy-order-type-1');
INSERT INTO OrderType(name) VALUES('buy-order-type-2');
I wish to create a FK constraint from another table, say SaleInformation, pointing to that table (OrderType). However, I am trying to express that not all rows of OrderType are eligible for the purposes of that FK (it should only be sale-related order types).
I thought about creating a view of table OrderType with just the right kind of rows (view SaleOrderType) and adding a FK constraint to that view, but PostgreSQL balks at that with:
ERROR: referenced relation "SaleOrderType" is not a table
So it seems I am unable to create a FK constraint to a view (why?). Am I only left with the option of creating a redundant table to hold the sale-related order types? The alternative would be to simply allow the FK to point to the original table, but then I am not really expressing the constraint as strictly as I would like to.
I think your schema should be something like this
create table order_nature (
nature_id int primary key,
description text
);
insert into order_nature (nature_id, description)
values (1, 'sale'), (2, 'buy')
;
create table order_type (
type_id int primary key,
description text
);
insert into order_type (type_id, description)
values (1, 'type 1'), (2, 'type 2')
;
create table order_nature_type (
nature_id int references order_nature (nature_id),
type_id int references order_type (type_id),
primary key (nature_id, type_id)
);
insert into order_nature_type (nature_id, type_id)
values (1, 1), (1, 2), (2, 1), (2, 2)
;
create table sale_information (
nature_id int default 1 check (nature_id = 1),
type_id int,
foreign key (nature_id, type_id) references order_nature_type (nature_id, type_id)
);
If the foreign key clause would also accept an expression the sale information could omit the nature_id column
create table sale_information (
type_id int,
foreign key (1, type_id) references order_nature_type (nature_id, type_id)
);
Notice the 1 in the foreign key
You could use an FK to OrderType to ensure referential integrity and a separate CHECK constraint to limit the order types.
If your OrderType values really are that structured then a simple CHECK like this would suffice:
check (c ~ '^sale-order-type-')
where c is order type column in SaleInformation
If the types aren't structured that way in reality, then you could add some sort of type flag to OrderType (say a boolean is_sales column), write a function which uses that flag to determine if an order type is a sales order:
create or replace function is_sales_order_type(text ot) returns boolean as $$
select exists (select 1 from OrderType where name = ot and is_sales);
$$ language sql
and then use that in your CHECK:
check(is_sales_order_type(c))
You don't of course have to use a boolean is_sales flag, you could have more structure than that, is_sales is just for illustrative purposes.

Conditional composite key in MySQL?

So I have this table with a composite key, basically 'userID'-'data' must be unique (see my other question SQL table - semi-unique row?)
However, I was wondering if it was possible to make this only come into effect when userID is not zero? By that I mean, 'userID'-'data' must be unique for non-zero userIDs?
Or am I barking up the wrong tree?
Thanks
Mala
SQL constraints apply to every row in the table. You can't make them conditional based on certain data values.
However, if you could use NULL instead of zero, you can get around the unique constraint. A unique constraint allows multiple entries that have NULL. The reason is that uniqueness means no two equal values can exist. Equality means value1 = value2 must be true. But in SQL, NULL = NULL is unknown, not true.
CREATE TABLE MyTable (id SERIAL PRIMARY KEY, userid INT, data VARCHAR(64));
INSERT INTO MyTable (userid, data) VALUES ( 1, 'foo');
INSERT INTO MyTable (userid, data) VALUES ( 1, 'bar');
INSERT INTO MyTable (userid, data) VALUES (NULL, 'baz');
So far so good, now you might think the following statements would violate the unique constraint, but they don't:
INSERT INTO MyTable (userid, data) VALUES ( 1, 'baz');
INSERT INTO MyTable (userid, data) VALUES (NULL, 'foo');
INSERT INTO MyTable (userid, data) VALUES (NULL, 'baz');
INSERT INTO MyTable (userid, data) VALUES (NULL, 'baz');