sql unique constraint on a 2 columns combination - sql

How can you create a unique constraint on a combination of two values in two columns.
meaning
column1 column2
2 1
looking for the constraint to disallow
column1 column2
1 2

If your database allows expressions in an index you can do something like this (ANSI SQL):
CREATE UNIQUE INDEX on your_table (least(column1, column2)
, greatest(column1, column2));
Note this is a unique index not a unique constraint. The only difference for most DBMS is that you can't have a unique index as the target of a foreign key, but otherwise they serve the same purpose.
If your DBMS does not have least() or greatest() you can replace that using a CASE expression:
create unique index on your_table
(case column1 < column2 then column1 else column2 end,
case column2 > column1 then column2 else column1 end));

Looking at the documentation, found this for the ORACLE SGBD :
CREATE TABLE b(
b1 INT,
b2 INT,
CONSTRAINT bu1 UNIQUE (b1, b2)
USING INDEX (create unique index bi on b(b1, b2)),
CONSTRAINT bu2 UNIQUE (b2, b1) USING INDEX bi);
Chapter "Specifying the Index Associated with a Constraint" on the page ORACLE documentation.
Hop this help.

A unique constraint on 2 columns only prevents those exact 2 values being inserted (switching them is allowed):
So you need A TRIGGER like this (ORACLE):
CREATE TRIGGER trig1
BEFORE INSERT ON TAB
REFERENCING NEW AS NEW
FOR EACH ROW
DECLARE
FOUND NUMBER;
BEGIN
SELECT COUNT(1) into FOUND FROM TAB WHERE
(COLUMN1=:NEW.column2 AND COLUMN2=:NEW.column1)
OR (COLUMN1=:NEW.column1 AND COLUMN2=:NEW.column2);
IF FOUND>0 THEN
raise_application_error (-20001,'INSERT not allowed');
END IF;
END trig1;
Warning: syntax not checked.

Related

Check for uniqueness of column in postgres table

I need to ensure that the values in a column from a table are unique as part of a larger process.
I'm aware of the UNIQUE constraint, but I'm wondering if there is a better way to do the check.
I'm running the queries using psycopg2 so adding that tag on the off chance there's something in there that can help with this.
If the column is unique I can add a constraint. If the column is not unique adding the constraint will return an error.
If there is already a constraint of the same name a useful error is returned. in this case would prefer to just check for the existing constraint.
If the column is the primary key, the unique constraint can be added without error but in this case it would be preferable to just recognize that the column must be unique based on the primary key.
Code examples of this below.
DROP TABLE IF EXISTS unique_test;
CREATE TABLE unique_test (
pkey INT PRIMARY KEY,
unique_yes CHAR(1),
unique_no CHAR(1)
);
INSERT INTO unique_test (pkey, unique_yes, unique_no)
VALUES(1, 'a', 'a'),
(2, 'b', 'a');
CREATE UNIQUE INDEX CONCURRENTLY u_test_1 ON unique_test (unique_yes);
ALTER TABLE unique_test
ADD CONSTRAINT unique_target_1
UNIQUE USING INDEX u_test_1;
-- the above runs no problem
-- check what happens when column is not unique
CREATE UNIQUE INDEX CONCURRENTLY u_test_2 ON unique_test (unique_no);
ALTER TABLE unique_test
ADD CONSTRAINT unique_target_2
UNIQUE USING INDEX u_test_2;
-- returns:
-- SQL Error [23505]: ERROR: could not create unique index "u_test_2"
-- Detail: Key (unique_no)=(a) is duplicated.
CREATE UNIQUE INDEX CONCURRENTLY u_test_1 ON unique_test (unique_yes);
ALTER TABLE unique_test
ADD CONSTRAINT unique_target_1
UNIQUE USING INDEX u_test_1;
-- returns
-- SQL Error [42P07]: ERROR: relation "unique_target_1" already exists
-- test what happens if adding constrint to primary key column
CREATE UNIQUE INDEX CONCURRENTLY u_test_pkey ON unique_test (pkey);
ALTER TABLE unique_test
ADD CONSTRAINT unique_target_pkey
UNIQUE USING INDEX u_test_pkey;
-- this runs no problem but is inefficient.
If all you want to do is verify that values are unique, then use a query:
select unique_no, count(*)
from unique_test
group by unique_no
having count(*) > 1;
If it needs to be boolean output:
select not exists (
select unique_no, count(*)
from unique_test
group by unique_no
having count(*) > 1
);
If you just want a flag, you can use:
select count(*) <> count(distinct uniq_no) as duplicate_flag
from unique_test;
DELETE FROM
zoo x
USING zoo y
WHERE
x.animal_id < y.animal_id
AND x.animal = y.animal;
I think this is simpler, https://kb.objectrocket.com/postgresql/delete-duplicate-rows-in-postgresql-762 for reference

Looking for suggestions on what table constraints to have to achieve uniqueness

Expecting data like below in a sql-server table:
A resource with id1 will entries for different versions and can also have different names for different versions.
But Name cannot be shared among resources. Once id1 use NameX, other resource should not be able to use same name.
Please suggest sql-table constraints I can define to achieve this:
Id Name Version
------------------
id1 Name1 1
id1 Name1 2
id1 NameA 3
id1 NameX 4
id2 Name2 1
id2 NameX 2 --invalid record, NameX is already used for id1
You can use an indexed view with a couple of unique indexes to ensure that each name only appears once per id value in the view and then to make the complete set of names unique:
create table dbo.Ix (ID varchar(20) not null, Name varchar(20) not null,
Version int not null)
go
create view dbo.DRI_Ix_Unique_Names
with schemabinding
as
select
Id,Name,COUNT_BIG(*) as Cnt
from
dbo.Ix
group by
ID,Name
go
create unique clustered index IX_DRI_IX_Unique_Names on dbo.DRI_Ix_Unique_Names (Id,Name)
go
create unique nonclustered index IX_DRI_IX_Unique_Names_Only on
dbo.DRI_Ix_Unique_Names(Name)
go
insert into dbo.Ix(ID,Name,Version) values
('id1','Name1',1)
go
insert into dbo.Ix(ID,Name,Version) values
('id1','Name1',2)
go
insert into dbo.Ix(ID,Name,Version) values
('id1','NameA',3)
go
insert into dbo.Ix(ID,Name,Version) values
('id1','NameX',4)
go
insert into dbo.Ix(ID,Name,Version) values
('id2','Name2',1)
go
insert into dbo.Ix(ID,Name,Version) values
('id2','NameX',2)
This results in five successful inserts followed by an error because the final insert violates the nonclustered unique index.
I'm not sure how the version column factors into your requirements and am not using it in any of the constraints.
create a trigger that checks the existence of the values before inserting a new record and throw an error if the record exists
like this
CREATE TRIGGER ti_CheckRecord
on YourTable before insert
begin
if exists(select 1 from inserted where exists(select 1 from yourtable where name = inserted.name and id <> inserted.id))
begin
--write your error code here
end
end

Constraint on values, oracle database

I would like to have a table in my Oracle database where the values of an attribute1 (values may change) can't be greater than the value (fixed) of attribute2.
Is it possible to enforce such a rule?
Is it possible to make a value impossible to change after the insert ?
Disallowing attribute1 from being larger than attribute2 can be done with a check constraint:
ALTER TABLE mytable
ADD CONSTRAINT attribute2_greater_check
CHECK (attribute2 >= attribute1)
Preventing update of attribute2 can be done with a trigger that raises an error:
CREATE OR REPLACE TRIGGER mytable_attribute2_update_tr
BEFORE UPDATE ON mytable
FOR EACH ROW
BEGIN
IF :NEW.attribute2 != :OLD.attribute2
THEN
RAISE_APPLICATION_ERROR(-20101, 'attribute2 cannot be updated');
END IF;
END;
/
One way to do is to use an appropriate CONSTRAINT when creating the table:
CREATE TABLE table_name
(
column1 integer,
column2 integer not null,
CONSTRAINT constraint_name CHECK (column1 <= column2)
);
INSERT INTO table_name VALUES(1,1); //ok
INSERT INTO table_name VALUES(2,1); //gives an error
Such constraints can use any fields in the table at hand, but may not access other tables via a subselect.
EDIT: I just realized you also asked another question ... that has been answered already by #Mureinik.

In Postgresql, force unique on combination of two columns

I would like to set up a table in PostgreSQL such that two columns together must be unique. There can be multiple values of either value, so long as there are not two that share both.
For instance:
CREATE TABLE someTable (
id int PRIMARY KEY AUTOINCREMENT,
col1 int NOT NULL,
col2 int NOT NULL
)
So, col1 and col2 can repeat, but not at the same time. So, this would be allowed (Not including the id)
1 1
1 2
2 1
2 2
but not this:
1 1
1 2
1 1 -- would reject this insert for violating constraints
CREATE TABLE someTable (
id serial PRIMARY KEY,
col1 int NOT NULL,
col2 int NOT NULL,
UNIQUE (col1, col2)
)
autoincrement is not postgresql. You want a integer primary key generated always as identity (or serial if you use PG 9 or lower. serial was soft-deprecated in PG 10).
If col1 and col2 make a unique and can't be null then they make a good primary key:
CREATE TABLE someTable (
col1 int NOT NULL,
col2 int NOT NULL,
PRIMARY KEY (col1, col2)
)
Create unique constraint that two numbers together CANNOT together be repeated:
ALTER TABLE someTable
ADD UNIQUE (col1, col2)
If, like me, you landed here with:
a pre-existing table,
to which you need to add a new column, and
also need to add a new unique constraint on the new column as well as an old one, AND
be able to undo it all (i.e. write a down migration)
Here is what worked for me, utilizing one of the above answers and expanding it:
-- up
ALTER TABLE myoldtable ADD COLUMN newcolumn TEXT;
ALTER TABLE myoldtable ADD CONSTRAINT myoldtable_oldcolumn_newcolumn_key UNIQUE (oldcolumn, newcolumn);
---
ALTER TABLE myoldtable DROP CONSTRAINT myoldtable_oldcolumn_newcolumn_key;
ALTER TABLE myoldtable DROP COLUMN newcolumn;
-- down
Seems like regular UNIQUE CONSTRAINT :)
CREATE TABLE example (
a integer,
b integer,
c integer,
UNIQUE (a, c));
More here

SQL Constraints Question

In Sql Server 2005, I have a table with two integer columns, call them Id1 and Id2.
I need them to be unique with in the table (easy enough with a unique index that spans both columns). I also need them to be unique in the table if the values are transposed between the two columns.
For example, SELECT * FROM MyTable returns
Id1 Id2
---------
2 4
5 8
7 2
4 2 <--- values transposed from the first row
How do I make a constraint that would prevent the last row from being entered into the table because they are the transposed values from the first row?
Create a check constraint that is bound to a user defined function that performs a select on the table to check for the transposed value.
Create table mytable(id1 int, id2 int)
go
create Function dbo.fx_Transposed(#id1 int, #id2 int)
returns bit as
Begin
Declare #Ret bit
Set #ret = 0
if exists(Select 1 from MyTable
Where id2 = #id1 and id1 = #id2)
Set #ret = 1
Return #ret
End
GO
Alter table mytable add
CONSTRAINT [CHK_TRANSPOSE] CHECK
(([dbo].[fx_Transposed]([ID1],[ID2])=(0)))
GO
Insert into mytable (id1, id2) values (1,2)
Insert into mytable (id1, id2) values (2,1)
Does the order between Id1 and Id2 have any significance? If not and this is a large table it may be more performent to enforce Id1 < Id2 in addition to your unique index. This would impact any process inputing records so it may not be feasible.
I would create a trigger that executed on insert and update would verify using a select statement that the values were unique in the two columns when transposed and when not transposed. This would allow you to reject any changes to the table that would break your rules for uniqueness at the point of the change and you could remove the unique index because it only enforces part of the requirement.