I want to have a unique constraint on a column which I am going to populate with GUIDs. However, my data contains null values for this columns. How do I create the constraint that allows multiple null values?
Here's an example scenario. Consider this schema:
CREATE TABLE People (
Id INT CONSTRAINT PK_MyTable PRIMARY KEY IDENTITY,
Name NVARCHAR(250) NOT NULL,
LibraryCardId UNIQUEIDENTIFIER NULL,
CONSTRAINT UQ_People_LibraryCardId UNIQUE (LibraryCardId)
)
Then see this code for what I'm trying to achieve:
-- This works fine:
INSERT INTO People (Name, LibraryCardId)
VALUES ('John Doe', 'AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA');
-- This also works fine, obviously:
INSERT INTO People (Name, LibraryCardId)
VALUES ('Marie Doe', 'BBBBBBBB-BBBB-BBBB-BBBB-BBBBBBBBBBBB');
-- This would *correctly* fail:
--INSERT INTO People (Name, LibraryCardId)
--VALUES ('John Doe the Second', 'AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA');
-- This works fine this one first time:
INSERT INTO People (Name, LibraryCardId)
VALUES ('Richard Roe', NULL);
-- THE PROBLEM: This fails even though I'd like to be able to do this:
INSERT INTO People (Name, LibraryCardId)
VALUES ('Marcus Roe', NULL);
The final statement fails with a message:
Violation of UNIQUE KEY constraint 'UQ_People_LibraryCardId'. Cannot insert duplicate key in object 'dbo.People'.
How can I change my schema and/or uniqueness constraint so that it allows multiple NULL values, while still checking for uniqueness on actual data?
What you're looking for is indeed part of the ANSI standards SQL:92, SQL:1999 and SQL:2003, ie a UNIQUE constraint must disallow duplicate non-NULL values but accept multiple NULL values.
In the Microsoft world of SQL Server however, a single NULL is allowed but multiple NULLs are not...
In SQL Server 2008, you can define a unique filtered index based on a predicate that excludes NULLs:
CREATE UNIQUE NONCLUSTERED INDEX idx_yourcolumn_notnull
ON YourTable(yourcolumn)
WHERE yourcolumn IS NOT NULL;
In earlier versions, you can resort to VIEWS with a NOT NULL predicate to enforce the constraint.
SQL Server 2008 +
You can create a unique index that accept multiple NULLs with a WHERE clause. See the answer below.
Prior to SQL Server 2008
You cannot create a UNIQUE constraint and allow NULLs. You need set a default value of NEWID().
Update the existing values to NEWID() where NULL before creating the UNIQUE constraint.
SQL Server 2008 And Up
Just filter a unique index:
CREATE UNIQUE NONCLUSTERED INDEX UQ_Party_SamAccountName
ON dbo.Party(SamAccountName)
WHERE SamAccountName IS NOT NULL;
In Lower Versions, A Materialized View Is Still Not Required
For SQL Server 2005 and earlier, you can do it without a view. I just added a unique constraint like you're asking for to one of my tables. Given that I want uniqueness in column SamAccountName, but I want to allow multiple NULLs, I used a materialized column rather than a materialized view:
ALTER TABLE dbo.Party ADD SamAccountNameUnique
AS (Coalesce(SamAccountName, Convert(varchar(11), PartyID)))
ALTER TABLE dbo.Party ADD CONSTRAINT UQ_Party_SamAccountName
UNIQUE (SamAccountNameUnique)
You simply have to put something in the computed column that will be guaranteed unique across the whole table when the actual desired unique column is NULL. In this case, PartyID is an identity column and being numeric will never match any SamAccountName, so it worked for me. You can try your own method—be sure you understand the domain of your data so that there is no possibility of intersection with real data. That could be as simple as prepending a differentiator character like this:
Coalesce('n' + SamAccountName, 'p' + Convert(varchar(11), PartyID))
Even if PartyID became non-numeric someday and could coincide with a SamAccountName, now it won't matter.
Note that the presence of an index including the computed column implicitly causes each expression result to be saved to disk with the other data in the table, which DOES take additional disk space.
Note that if you don't want an index, you can still save CPU by making the expression be precalculated to disk by adding the keyword PERSISTED to the end of the column expression definition.
In SQL Server 2008 and up, definitely use the filtered solution instead if you possibly can!
Controversy
Please note that some database professionals will see this as a case of "surrogate NULLs", which definitely have problems (mostly due to issues around trying to determine when something is a real value or a surrogate value for missing data; there can also be issues with the number of non-NULL surrogate values multiplying like crazy).
However, I believe this case is different. The computed column I'm adding will never be used to determine anything. It has no meaning of itself, and encodes no information that isn't already found separately in other, properly defined columns. It should never be selected or used.
So, my story is that this is not a surrogate NULL, and I'm sticking to it! Since we don't actually want the non-NULL value for any purpose other than to trick the UNIQUE index to ignore NULLs, our use case has none of the problems that arise with normal surrogate NULL creation.
All that said, I have no problem with using an indexed view instead—but it brings some issues with it such as the requirement of using SCHEMABINDING. Have fun adding a new column to your base table (you'll at minimum have to drop the index, and then drop the view or alter the view to not be schema bound). See the full (long) list of requirements for creating an indexed view in SQL Server (2005) (also later versions), (2000).
Update
If your column is numeric, there may be the challenge of ensuring that the unique constraint using Coalesce does not result in collisions. In that case, there are some options. One might be to use a negative number, to put the "surrogate NULLs" only in the negative range, and the "real values" only in the positive range. Alternately, the following pattern could be used. In table Issue (where IssueID is the PRIMARY KEY), there may or may not be a TicketID, but if there is one, it must be unique.
ALTER TABLE dbo.Issue ADD TicketUnique
AS (CASE WHEN TicketID IS NULL THEN IssueID END);
ALTER TABLE dbo.Issue ADD CONSTRAINT UQ_Issue_Ticket_AllowNull
UNIQUE (TicketID, TicketUnique);
If IssueID 1 has ticket 123, the UNIQUE constraint will be on values (123, NULL). If IssueID 2 has no ticket, it will be on (NULL, 2). Some thought will show that this constraint cannot be duplicated for any row in the table, and still allows multiple NULLs.
For people who are using Microsoft SQL Server Manager and want to create a Unique but Nullable index you can create your unique index as you normally would then in your Index Properties for your new index, select "Filter" from the left hand panel, then enter your filter (which is your where clause). It should read something like this:
([YourColumnName] IS NOT NULL)
This works with MSSQL 2012
When I applied the unique index below:
CREATE UNIQUE NONCLUSTERED INDEX idx_badgeid_notnull
ON employee(badgeid)
WHERE badgeid IS NOT NULL;
every non null update and insert failed with the error below:
UPDATE failed because the following SET options have incorrect settings: 'ARITHABORT'.
I found this on MSDN
SET ARITHABORT must be ON when you are creating or changing indexes on computed columns or indexed views. If SET ARITHABORT is OFF, CREATE, UPDATE, INSERT, and DELETE statements on tables with indexes on computed columns or indexed views will fail.
So to get this to work correctly I did this
Right click [Database]-->Properties-->Options-->Other
Options-->Misscellaneous-->Arithmetic Abort Enabled -->true
I believe it is possible to set this option in code using
ALTER DATABASE "DBNAME" SET ARITHABORT ON
but i have not tested this
It can be done in the designer as well
Right click on the Index > Properties to get this window
Create a view that selects only non-NULL columns and create the UNIQUE INDEX on the view:
CREATE VIEW myview
AS
SELECT *
FROM mytable
WHERE mycolumn IS NOT NULL
CREATE UNIQUE INDEX ux_myview_mycolumn ON myview (mycolumn)
Note that you'll need to perform INSERT's and UPDATE's on the view instead of table.
You may do it with an INSTEAD OF trigger:
CREATE TRIGGER trg_mytable_insert ON mytable
INSTEAD OF INSERT
AS
BEGIN
INSERT
INTO myview
SELECT *
FROM inserted
END
It is possible to create a unique constraint on a Clustered Indexed View
You can create the View like this:
CREATE VIEW dbo.VIEW_OfYourTable WITH SCHEMABINDING AS
SELECT YourUniqueColumnWithNullValues FROM dbo.YourTable
WHERE YourUniqueColumnWithNullValues IS NOT NULL;
and the unique constraint like this:
CREATE UNIQUE CLUSTERED INDEX UIX_VIEW_OFYOURTABLE
ON dbo.VIEW_OfYourTable(YourUniqueColumnWithNullValues)
In my experience - if you're thinking a column needs to allow NULLs but also needs to be UNIQUE for values where they exist, you may be modelling the data incorrectly. This often suggests you're creating a separate sub-entity within the same table as a different entity. It probably makes more sense to have this entity in a second table.
In the provided example, I would put LibraryCardId in a separate LibraryCards table with a unique not-null foreign key to the People table:
CREATE TABLE People (
Id INT CONSTRAINT PK_MyTable PRIMARY KEY IDENTITY,
Name NVARCHAR(250) NOT NULL,
)
CREATE TABLE LibraryCards (
LibraryCardId UNIQUEIDENTIFIER CONSTRAINT PK_LibraryCards PRIMARY KEY,
PersonId INT NOT NULL
CONSTRAINT UQ_LibraryCardId_PersonId UNIQUE (PersonId),
FOREIGN KEY (PersonId) REFERENCES People(id)
)
This way you don't need to bother with a column being both unique and nullable. If a person doesn't have a library card, they just won't have a record in the library cards table. Also, if there are additional attributes about the library card (perhaps Expiration Date or something), you now have a logical place to put those fields.
Maybe consider an "INSTEAD OF" trigger and do the check yourself? With a non-clustered (non-unique) index on the column to enable the lookup.
As stated before, SQL Server doesn't implement the ANSI standard when it comes to UNIQUE CONSTRAINT. There is a ticket on Microsoft Connect for this since 2007. As suggested there and here the best options as of today are to use a filtered index as stated in another answer or a computed column, e.g.:
CREATE TABLE [Orders] (
[OrderId] INT IDENTITY(1,1) NOT NULL,
[TrackingId] varchar(11) NULL,
...
[ComputedUniqueTrackingId] AS (
CASE WHEN [TrackingId] IS NULL
THEN '#' + cast([OrderId] as varchar(12))
ELSE [TrackingId_Unique] END
),
CONSTRAINT [UQ_TrackingId] UNIQUE ([ComputedUniqueTrackingId])
)
You can create an INSTEAD OF trigger to check for specific conditions and error if they are met. Creating an index can be costly on larger tables.
Here's an example:
CREATE TRIGGER PONY.trg_pony_unique_name ON PONY.tbl_pony
INSTEAD OF INSERT, UPDATE
AS
BEGIN
IF EXISTS(
SELECT TOP (1) 1
FROM inserted i
GROUP BY i.pony_name
HAVING COUNT(1) > 1
)
OR EXISTS(
SELECT TOP (1) 1
FROM PONY.tbl_pony t
INNER JOIN inserted i
ON i.pony_name = t.pony_name
)
THROW 911911, 'A pony must have a name as unique as s/he is. --PAS', 16;
ELSE
INSERT INTO PONY.tbl_pony (pony_name, stable_id, pet_human_id)
SELECT pony_name, stable_id, pet_human_id
FROM inserted
END
You can't do this with a UNIQUE constraint, but you can do this in a trigger.
CREATE TRIGGER [dbo].[OnInsertMyTableTrigger]
ON [dbo].[MyTable]
INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Column1 INT;
DECLARE #Column2 INT; -- allow nulls on this column
SELECT #Column1=Column1, #Column2=Column2 FROM inserted;
-- Check if an existing record already exists, if not allow the insert.
IF NOT EXISTS(SELECT * FROM dbo.MyTable WHERE Column1=#Column1 AND Column2=#Column2 #Column2 IS NOT NULL)
BEGIN
INSERT INTO dbo.MyTable (Column1, Column2)
SELECT #Column2, #Column2;
END
ELSE
BEGIN
RAISERROR('The unique constraint applies on Column1 %d, AND Column2 %d, unless Column2 is NULL.', 16, 1, #Column1, #Column2);
ROLLBACK TRANSACTION;
END
END
CREATE UNIQUE NONCLUSTERED INDEX [UIX_COLUMN_NAME]
ON [dbo].[Employee]([Username] ASC) WHERE ([Username] IS NOT NULL)
WITH (ALLOW_PAGE_LOCKS = ON, ALLOW_ROW_LOCKS = ON, PAD_INDEX = OFF, SORT_IN_TEMPDB = OFF,
DROP_EXISTING = OFF, IGNORE_DUP_KEY = OFF, STATISTICS_NORECOMPUTE = OFF, ONLINE = OFF,
MAXDOP = 0) ON [PRIMARY];
this code if u make a register form with textBox and use insert and ur textBox is empty and u click on submit button .
CREATE UNIQUE NONCLUSTERED INDEX [IX_tableName_Column]
ON [dbo].[tableName]([columnName] ASC) WHERE [columnName] !=`''`;
I have a unique index on the table and I need to make it case-insensitive, because I started getting duplicates in that table:
TEST
Test
TeSt
To fix the issue I was trying to drop existing index and re-create it:
ALTER TABLE table1 drop constraint test_uk1;
DROP INDEX test_uk1;
CREATE UNIQUE INDEX test_uk1 ON table1(UPPER(column1));
ALTER TABLE table1 ADD CONSTRAINT test_uk1 UNIQUE (column1) USING INDEX test_uk1 ENABLE VALIDATE;
I got an error ORA-14196: Specified index cannot be used to enforce the constraint. on the last statement.
Is there a better way to accomplish this with Oracle?
You can use a virtual column to normalise the case of the string and then put the unique constraint on the virtual column:
CREATE TABLE table_name (
value VARCHAR2(20),
uvalue VARCHAR2(20)
GENERATED ALWAYS AS (UPPER(value))
CONSTRAINT table_name__uvalue__u UNIQUE
);
Then:
INSERT INTO table_name (value) VALUES ('abc');
INSERT INTO table_name (value) VALUES ('aBc');
Inserts one row and the second statement fails with:
ORA-00001: unique constraint (FIDDLE_QDGWKZFBAWPLSRVHJNHN.TABLE_NAME__UVALUE__U) violated
db<>fiddle here
You need not to ALTER TABLE table1 ADD CONSTRAINT ..., the definition of the functional based index is enough for the creation of the unique constraint.
It is also wrong, as you use UNIQUE (column1) which you do not want, so simple skip this statement.
CREATE UNIQUE INDEX test_uk1 ON table1(UPPER(column1));
insert into table1 (column1) values('x');
insert into table1 (column1) values('X');
ORA-00001: unique constraint (XXX.TEST_UK1) violated
Even independently of the DBMS (Oracle, SQL Server, DB2, Vertica, PostgreSQL, you name them), Indexes live and die with the fact that data is not modified/derived before the index or the optimisation taking advantage of the index is applied.
So your approach is doomed, as far as I can tell.
Try this:
ALTER TABLE table1 ADD column1_u DEFAULT UPPER(column1);
CREATE UNIQUE INDEX test_uk1 ON table1(column1_u);
ALTER TABLE table1 ADD CONSTRAINT test_uk1 UNIQUE(column1_u)
USING INDEX test_uk1 ENABLE VALIDATE;
I know, it's a long transaction if your table is big, as you need to shuffle half the tablespace, but it's worth it ..
I have a table with a primary key with subsequent values 1,2,3,4,... 1035
I want to insert a new value somewhere in this sequence and increment all values above the said value (actually, the order would be the other way round).
Obviously, I get an error with a simple statement like
UPDATE Table
SET primary_id=primary_id+1
WHERE primary_id > 501
because primary_id 502 is incremented to 503, and 503 already exists.
It seems a very basic question, but I don't remember how to do it.
The table is in MySQL, but I don't think this really matters.
First, take a backup of your table, like this:
CREATE TABLE backup LIKE table;
INSERT INTO backup SELECT * FROM table;
Then, you can run the following queries:
-- remove auto_increment attribute and unique constraint
ALTER TABLE table MODIFY id INT NOT NULL, DROP PRIMARY KEY;
-- your original query
UPDATE table SET id = (id+1) WHERE id > 501;
-- re-add the primary key
ALTER TABLE table MODIFY id INT NOT NULL PRIMARY KEY AUTO_INCREMENT;
If results are OK, remove the backup:
DROP TABLE backup;
How can I make a set of column to be unique key in sql server database?
for example: I have a database that have just one table by columns 1_book 2_page 3_line 4_word i want to search a word in some books and record this information .
where is the problem? if it find a words twice or more in a line it will save the same record to table.it is not important for me how many times a word is repeated in a line. i want if a word to be repeated once or more save the information.
is there any way to say every record should be unique?
searching a record in table before Inserting it to table is not reasonable .isn't it?
Just create a unique constraint for your table: (example for ms sql-server)
ALTER TABLE <yourtablename> ADD CONSTRAINT
UniqueEntries UNIQUE NONCLUSTERED
(
1_book, 2_page, 3_line, 4_word
)
If you do not want to get errors and simply ignore additional adds of the same word in the same line, you can extend the constraint with IGNORE_DUP_KEY = ON
Example:
ALTER TABLE <yourtablename> ADD CONSTRAINT
UniqueEntries UNIQUE NONCLUSTERED
(
1_book, 2_page, 3_line, 4_word
) WITH (IGNORE_DUP_KEY = ON)
Inserts with already existing records will then just be silently ignored.
You mean, you don't know how to create composite keys? Well:
alter table dbo.Words add constraint PK_Words primary key (
1_book, 2_page, 3_line, 4_word
);
And if you don't want key violations while adding data, use merge instead of insert (assuming your SQL Server version is 2008 or later).
in SQL Server Management Studio (SSMS) 2008 R2 Dev on the table (because I cannot ask without it)
--SET ANSI_NULL_DFLT_ON ON
create table B (Id int)
I create unique constraint
ALTER TABLE B
ADD CONSTRAINT IX_B
UNIQUE (ID)
WITH (IGNORE_DUP_KEY = ON)
SSMS shows that I do not have any constraint but have a key + index instead while context options (on right clicking) hint me to create (sorry, script) still constraint.
MAIN QUESTION:
What is a key here?
Why is it needed for this constraint?
Why is unique constraint called by key (and key by unique constraint)?
Sorry, again, why is key called by index? They seem to have the same name (though had I created without explicit name they would have called differently)...
Sorry, again...
COLLATERAL questions:
Which functionality is different between "unique constraint" vs. "unique index"? I searched today for a single difference (wanted to find 10) quite a time and could not find any.
In other words, what's for (why) are concepts (or constructs) "unique constraint" and "unique index" are duplicated in SQL Server?
Bonus question (for those whom this question seemed too simple):
What is the sense in unique index (or in unique constraint) permitting dupes?
insert into B VALUES (1)
insert into B VALUES (1)
insert into B VALUES (1)
Update: Sorry Thanks, guys and ladies (bonus is withdrawn)
Update2: Was not there a difference between "unique index" and "unique constraint" in previous SQL Server (I vaguely recall that one of them did not permit NULL)?
Update3: Really, I was always pissed off (confused) by a "foreign key constraint" is called by "foreign key" while it is not a key and the key, foreign one is in another table, foreign one ... and just found that this is universal confusion to rattle on it. At least, now I memorize that I should remember au contraire.
Update4:
#Damien_The_Unbeliever, thanks,
these are, at least, some hrooks to memorize confusion.
Though, some bewilderments:
Why are these candidates NOT NULL by default?
Initially I really wanted to insert much shorter script:
CREATE TABLE A(A INT UNIQUE);
which produced:
WTF this "candidate" for PRIMARY and KEY has multiple identities syndrome, then?
Is not "UQ_" stand for Unique constraint in naming practices?
Now, scripting of this index UQ__A__3214EC262AA05119 produces nameless... not index ... constraint(?!):
ALTER TABLE [dbo].[A] ADD UNIQUE NONCLUSTERED
(
[ID] ASC
)
WITH
( PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF,
IGNORE_DUP_KEY = OFF,
ONLINE = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON)
ON
[PRIMARY]
WTheF - How is unique index is identified as constraint? Where, by what
Scripting of key produces the same! again, constraint...
Why is it nameless? Why is it impossible to script as "modify" but only as "create"?!
This does not make sense!
Now if to execute the produced script, there are 2 dupes,
to execute once more - voila: 3 dupe "candidates"
Note: if to create the unique constraint by separate T-SQL statement with custom/manual name, as I made at the top, then scripting does not produce anonymous DML and, respectively, their execution does not permit multiplication of "candidates"
Strange candidates for primary keys, aren't they? need to visit a shrink (with multiple identities symptoms)?
A UNIQUE constraint is also known as a UNIQUE KEY constraint. Basically, there can be multiple KEYs for a table. One is selected (somewhat arbitrarily) to be the PRIMARY KEY for the table. Other keys are created as UNIQUE KEY constraints.
As an implementation detail, a UNIQUE KEY constraint is implemented by placing a UNIQUE index on the same columns in the table. However, it is possible to create UNIQUE indexes on a table (via CREATE INDEX), without creating a UNIQUE constraint.
UNIQUE constraints are similar to PRIMARY KEYs - they can be the target reference for a FOREIGN KEY constraint. A UNIQUE index, by itself, cannot be so referenced.
In SSMS, PRIMARY KEY, UNIQUE, and FOREIGN KEY constraints will always show up under the "Keys" folder for a table. CHECK and DEFAULT constraints will show up under the "Constraints" folder
This only answers part of your question, as I'm not clear on why management studio displays these objects the way it does.
A unique constraint is part of the (logical) relational model. Essentially if you were drawing out the logical model, a unique constraint would appear on the drawing.
A unique index (like all indexes) is an implementation detail, so is part of the physical model.
SQL Server uses a unique index to implement a unique constraint. There is a logical difference, and I think this shows up in the SQL Server diagramming tool--if you use a unique constraint on a foreign key in what otherwise would be a 1:n relationship, it shows up as a 1:1. This is not true when using a unique index (again, not 100% sure on this).
first off all your table has only 1 value not 3, take a look
create table B (Id int)
ALTER TABLE B
ADD CONSTRAINT IX_B
UNIQUE (ID)
WITH (IGNORE_DUP_KEY = ON)
insert into B VALUES (1)
insert into B VALUES (1)
insert into B VALUES (1)
--Duplicate key was ignored.
--Duplicate key was ignored.
select * from B
One row, right?
This is because you have this WITH (IGNORE_DUP_KEY = ON)
now do this
create table C (Id int)
ALTER TABLE C
ADD CONSTRAINT IX_C
UNIQUE (ID)
insert into C VALUES (1)
insert into C VALUES (1)
The second row won't go into the table now
The way that SQL Server implements constraint is that it creates an index behind it to facilitate fast lookups among other things.
Perhaps you really want a primary key on this table?
create table D (Id int not null primary key)
A unique constraint is implemented internally as an index.
Pretty much the only difference between explicit CREATE INDEX and adding a constraint via ALTER TABLE is the ability to have INCLUDE columns as an explicit index.
SSMS is somewhat confusing in how it presents this. No idea why
Personally, I think IGNORE_DUP_KEY is pointless and have never used it.
Specifies the error response to
duplicate key values in a multiple-row
insert operation on a unique clustered
or unique nonclustered index. The
default is OFF.
ON
A warning message is issued and
only the rows violating the unique
index fail.
OFF
An error message is issued and the
entire INSERT transaction is rolled
back.
The IGNORE_DUP_KEY setting applies
only to insert operations that occur
after the index is created or rebuilt.
The setting has no affect during the
index operation.
Edit:
Found this from me before: Can I set ignore_dup_key on for a primary key?
Generally, IGNORE_DUP_KEY has several answers too