Azure SQL DB allow NULL values for UNIQUE constraint columns - sql

How can I set a UNIQUE constraint in Azure SQL database that allows NULL values in the column?
The below workaround from SQL Server doesn't seem to work in Azure SQL:
CREATE UNIQUE NONCLUSTERED INDEX [IX_Email] ON [dbo].[Users]([Email] ASC)
WHERE ([Email] IS NOT NULL);

The error, which is in the comments is telling you the problem:
Violation of UNIQUE KEY Constraint "UQ_Users_5120...". Cannot insert duplicate key in object 'dbo.Users'. The duplicate key value is ().
Firstly, notice that the error states "UNIQUE KEY Constraint", emphasis mine. Not Index, Constraint. Also note that name of said constraint: UQ_Users_5120... That isn't the name of the object you created (IX_Email).
You can replicate this problem with the following:
CREATE TABLE dbo.SomeTable (SomeColumn varchar(10) NULL);
GO
--Create a UNIQUE Constraint
ALTER TABLE dbo.SomeTable ADD CONSTRAINT UQ_SomeColumn UNIQUE (SomeColumn);
GO
--Create a filtered unique index
CREATE UNIQUE NONCLUSTERED INDEX UX_SomeColumn_NotNull ON dbo.SomeTable(SomeColumn)
WHERE SomeColumn IS NOT NULL;
GO
--Initial Insert
INSERT INTO dbo.SomeTable (SomeColumn)
VALUES('asdfasd'),
('asdasd'),
(NULL);
GO
--Insert another dupe, non NULL value. Fails
--This violates constraint 'UQ_SomeColumn'
INSERT INTO dbo.SomeTable (SomeColumn)
VALUES('asdfasd');
GO
--Insert another dupe, NULL value. Fails
--This violates constraint 'UQ_SomeColumn'
INSERT INTO dbo.SomeTable (SomeColumn)
VALUES(NULL);
GO
You can fix this my dropping your UNIQUE CONSTRAINT. We don't have the full name, but for the above example it would be the following:
ALTER TABLE dbo.SomeTable DROP CONSTRAINT UQ_SomeColumn;
GO
And then we can test again:
--Insert another dupe, non NULL value. Fails
--Cannot insert duplicate key row in object 'dbo.SomeTable' with unique index 'UX_SomeColumn_NotNull'. The duplicate key value is (asdfasd).
INSERT INTO dbo.SomeTable (SomeColumn)
VALUES('asdfasd');
--Insert another dupe, NULL value. Success
INSERT INTO dbo.SomeTable (SomeColumn)
VALUES(NULL);
GO
Notice as well, that the error for the duplicate value is completely different now. It mentions a unique index, not a unique key constraint, and has the name of the unique filtered index created, not something else.

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

Violating foreign key constraint with deferred constraint

I'm trying to set my sql scripts into a transaction to achieve atomicity with my database.
The table structure is (simplified):
CREATE TABLE foo (
id serial NOT NULL,
foo varchar(50) NOT NULL,
CONSTRAINT foo_pk PRIMARY KEY (id)
);
CREATE TABLE access (
id serial NOT NULL,
foo_id int NULL
CONSTRAINT access_pk PRIMARY KEY (id)
);
ALTER TABLE access ADD CONSTRAINT access_foo
FOREIGN KEY (foo_id)
REFERENCES foo (id)
ON DELETE CASCADE
ON UPDATE CASCADE
DEFERRABLE
INITIALLY DEFERRED;
In my code I first declare:
client.query('BEGIN'); (I'm using npm library 'pg') then
insert a row into a table 'foo', then another insert to 'access' with a foo_id from the first insert. After that there is client.query('COMMIT');
All of this is in a try catch, and in the catch is client.query('ROLLBACK'); and the rolling back seems to be working if there is an issue either of the inserts. When everything should be committed I still end up in the catch block for this:
message: "insert or update on table "access" violates foreign key constraint "access_foo""
detail: "Key (foo_id)=(20) is not present in table "foo"."
I thought that deferring constraint would be enough to do this, but I guess I'm wrong. Any help is welcome.
You probably have some issue with the transaction demarcation. I ran a simple test and works wells.
insert into foo (id, foo) values (1, 'Anne');
start transaction;
insert into access (id, foo_id) values (101, 1);
insert into access (id, foo_id) values (107, 7); -- 7 does not exist yet...
insert into foo (id, foo) values (7, 'Ivan'); -- 7 now exists!
commit; -- at this point all is good
See running example at DB Fiddle.

Postgres breaking null constraint on a serial column

I have a table that I create independently, the primary key is set with the serial type and a sequence applied to the table, but when I try to insert a value a NULL CONSTRAINT error is thrown and the return looks like null was passed, am I missing something in the INSERT statement?
SQL for table generation:
DROP TABLE IF EXISTS public."Team" CASCADE;
CREATE TABLE public."Team" (
"IdTeam" serial PRIMARY KEY,
name text NOT null,
CONSTRAINT "pKeyTeamUnique" UNIQUE ("IdTeam")
);
ALTER TABLE public."Team" OWNER TO postgres;
DROP SEQUENCE IF EXISTS public."Team_IdTeam_seq" CASCADE;
CREATE SEQUENCE public."Team_IdTeam_seq"
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER TABLE public."Team_IdTeam_seq" OWNER TO postgres;
ALTER SEQUENCE public."Team_IdTeam_seq" OWNED BY public."Team"."IdTeam";
SQL for insert :
INSERT INTO public."Team" (name) values ('Manchester Untited');
The returning error:
ERROR: null value in column "IdTeam" violates not-null constraint
DETAIL: Failing row contains (null, Manchester Untited).
SQL state: 23502
I am baffled. Why are you trying to define your own sequence when the column is already defined as serial?
Second, a primary key constraint is already unique. There is no need for a separate unique constraint.
Third, quoting identifiers just makes the code harder to write and to read.
You can just do:
DROP TABLE IF EXISTS public.Team CASCADE;
CREATE TABLE public.Team (
IdTeam serial PRIMARY KEY,
name text NOT null
);
INSERT INTO public.Team (name)
VALUES ('Manchester Untited');
Dropping the sequence causes the default definition for the IdTeam column to be dropped. After recreating the sequence you will have to recreate the default definition.

H2 INSERT SELECT ON DUPLICATE KEY UPDATE throws "Unique index or primary key violation" error

H2 (started with MODE=MYSQL on) supports INSERT ON DUPLICATE KEY UPDATE statement only with VALUES clause, while throws a "Unique index or primary key violation" error when using INSERT SELECT statement.
Here is an example:
-- creating a simple table
CREATE TABLE test_table1 (
id INT NOT NULL,
value VARCHAR(255) NOT NULL,
PRIMARY KEY (id))
ENGINE = InnoDB;
-- inserting a value
INSERT INTO test_table1
VALUES (1, 'test1');
-- trying to insert on duplicate key update: it works!
INSERT INTO test_table1
VALUES (1, 'test2')
ON DUPLICATE KEY UPDATE value='test2';
-- trying using INSERT SELECT: it throws Unique index or primary key violation: "PRIMARY KEY ON PUBLIC.TEST_TABLE1(ID)"
INSERT INTO test_table1
SELECT 1, 'test2'
FROM test_table1
ON DUPLICATE KEY UPDATE value='test2';
I'm using H2 db version 1.4.192.
Is it a bug? Or is there something wrong with my code?
Thank you
On you H2 console, if you have 'HIBERNATE_SEQUENCES' table make sure to check what is the NEXT_VAL for SEQUENCE_NAME = 'default'.
In my case, I had 2 row (insert statement) in my /src/main/resources/data.sql and the NEXT_VAL was 2 which was causing problems. I changed to 3 with update statement, and it now works fine.
Is there something wrong with my code?
Yes, there is. Why are you inserting into an auto-increment column? You should be specifying the columns with non-autogenerated data. So:
INSERT INTO test_table1(value)
VALUES ('test1');
And:
INSERT INTO test_table1(value)
SELECT 'test2'
FROM test_table1
ON DUPLICATE KEY UPDATE value = VALUES(value);
Your are getting the error because ON DUPLICATE KEY resets value, but that has nothing to do with the primary key on the table.

SQL Server 2005: Nullable Foreign Key Constraint

I have a foreign key constraint between tables Sessions and Users. Specifically, Sessions.UID = Users.ID. Sometimes, I want Sessions.UID to be null. Can this be allowed? Any time I try to do this, I get an FK Constraint Violation.
Specifically, I'm inserting a row into Sessions via LINQ. I set the Session.User = null; and I get this error:
An attempt was made to remove a relationship between a User and a Session. However, one of the relationship's foreign keys (Session.UID) cannot be set to null.
However, when I remove the line that nulls the User property, I get this error on my SubmitChanges line:
Value cannot be null.
Parameter name: cons
None of my tables have a field called 'cons', nor is it in my 5,500-line DataContext.designer.cs file, nor is it in the QuickWatch for any of the related objects, so I have no idea what 'cons' is.
In the Database, Session.UID is a nullable int field and User.ID is a non-nullable int. I want to record sessions that may or may not have a UID, and I'd rather do it without disabling constraint on that FK relationship. Is there a way to do this?
I seemed to remember creating a nullable FK before, so I whipped up a quick test. As you can see below, it is definitely doable (tested on MSSQL 2005).
Script the relevant parts of your tables and constraints and post them so we can troubleshoot further.
CREATE DATABASE [NullableFKTest]
GO
USE [NullableFKTest]
GO
CREATE TABLE OneTable
(
OneId [int] NOT NULL,
CONSTRAINT [PK_OneTable] PRIMARY KEY CLUSTERED
(
[OneId] ASC
)
)
CREATE TABLE ManyTable (ManyId [int] IDENTITY(1,1) NOT NULL, OneId [int] NULL)
GO
IF NOT EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[dbo].[FK_ManyTable_OneTable]') AND parent_object_id = OBJECT_ID(N'[dbo].[ManyTable]') )
ALTER TABLE [dbo].[ManyTable] WITH CHECK ADD CONSTRAINT [FK_ManyTable_OneTable] FOREIGN KEY([OneId])
REFERENCES [dbo].[OneTable] ([OneId])
GO
--let's get a value in here
insert into OneTable(OneId) values(1)
select* from OneTable
--let's try creating a valid relationship to the FK table OneTable
insert into ManyTable(OneId) values (1) --fine
--now, let's try NULL
insert into ManyTable(OneId) values (NULL) --also fine
--how about a non-existent OneTable entry?
insert into ManyTable(OneId) values (5) --BOOM! - FK violation
select* from ManyTable
--1, 1
--2, NULL
--cleanup
ALTER TABLE ManyTable DROP CONSTRAINT FK_ManyTable_OneTable
GO
drop TABLE OneTable
GO
drop TABLE ManyTable
GO
USE [Master]
GO
DROP DATABASE NullableFKTest