Unique constraint on multiple columns when any columns are not null - sql-server

I am trying to add a unique constraint on 2 columns that allows multiple (null, null), but doesn't allow multiple ("a", null). I wrote the following SQL statement but I get an error
Incorrect syntax near the keyword 'with'
SQL statement:
CREATE UNIQUE NONCLUSTERED INDEX [UQ]
ON [dbo].[MyTable] ([Column_A], [Column_B])
WHERE [Column_A] IS NOT NULL OR [Column_B] IS NOT NULL
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY];
However in
WHERE [Column_A] IS NOT NULL OR [Column_B] IS NOT NULL
when I replace OR with AND, there is no syntax error anymore. But what I want is OR logic, not AND because using AND allows multiple ("a", null) entries.
So why is there an incorrect syntax error using OR? Thanks.

It's a bit clunky, but if the point is to enforce uniqueness (except for NULL/NULL) and you're happy having two indexes, you can do it via putting the NOT NULL filters on separate indexes e.g.,
CREATE UNIQUE NONCLUSTERED INDEX
[UQ_A] ON [dbo].[MyTable]
(
[Column_A],
[Column_B]
)
WHERE [Column_A] IS NOT NULL
GO
CREATE UNIQUE NONCLUSTERED INDEX
[UQ_B] ON [dbo].[MyTable]
(
[Column_A],
[Column_B]
)
WHERE [Column_B] IS NOT NULL
GO
While it's not necessary for the uniqueness check, you may want to change the order of the fields in the second index to potentially gain additional advantages of the index (depending on how these fields are used in your application - it may be better swapping them in the first index), e.g.,
CREATE UNIQUE NONCLUSTERED INDEX
[UQ_B] ON [dbo].[MyTable]
(
[Column_B],
[Column_A]
)
WHERE [Column_B] IS NOT NULL
GO

According to Martin Smith's comment, I think I can implement it by:
CREATE VIEW [dbo].[TableAView]
WITH SCHEMABINDING AS
SELECT
[ColumnA] = [ColumnA],
[ColumnB] = [ColumnB]
FROM [dbo].[TableA]
WHERE [ColumnA] IS NOT NULL OR [ColumnB] IS NOT NULL;
GO
CREATE UNIQUE CLUSTERED INDEX
[UQ] ON [dbo].[TableAView]
(
[ColumnA],
[ColumnB]
)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
ON [PRIMARY];
GO

Related

How to make entity framework respect the true nature of default values on DB columns

Consider the following table definition which creates a table with default values which are not nullable and which have default values for when a value is not supplied
drop table [defaultTest]
CREATE TABLE [defaultTest](
[TestId] [int] IDENTITY(1,1) NOT NULL,
[TestData] [nvarchar](100) NOT NULL,
[TestKey] [int] NOT NULL,
[TestTimeStamp] [datetimeoffset](7) NOT NULL,
PRIMARY KEY CLUSTERED
(
[TestId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [defaultTest] ADD CONSTRAINT [DF_Test_TestKey] DEFAULT (NEXT VALUE FOR [SomeSequence]) FOR [TestKey]
GO
ALTER TABLE [defaultTest] ADD CONSTRAINT [DF_Test_TestTimeStamp] DEFAULT (sysdatetimeoffset()) FOR [TestTimeStamp]
GO
CREATE UNIQUE NONCLUSTERED INDEX [defaultTest_TestKey_Insert_UK] ON [defaultTest]
(
[TestKey] ASC,
[TestTimeStamp] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
declare #testkey int;
declare #id int;
insert into [defaultTest]([TestData]) values ('Original');
set #id = ##IDENTITY;
select #testkey = [TestKey] from [defaultTest] where [TestId] = #id;
insert into [defaultTest]([TestData], [TestKey] ) values ('Updated', #testkey);
select * from [defaultTest];
TestId TestData TestKey TestTimeStamp
1 Original 27 2019-06-26 14:40:22.1042605 +10:00
2 Updated 27 2019-06-26 14:40:22.1062673 +10:00
In the database this works perfectly. An insert can supply a value or not and the database will ensure that a value is always there.
But when this database table is referenced in entity framework database first i'm struggling to get entity framework to respect the true nature of the situation.
Observed behaviour is that it will either always pass zero values if StoredGeneratedPattern = none and the fields are empty and always pass null if StoredGeneratedPattern = Computed (or Identity) even if a value is supplied.
That is not how the definition ever works at the database level so why entity framework was programmed that way is a mystery?
Is there a way to get entity framework to behave properly for this scenario?
EDIT: I tried removing the not null constraint from the Key but entity framework refuses to pull the generated value back from the database.

Add non-null unique constraint - not index

Basically I want to turn this:
ALTER TABLE [dbo].[Computer] ADD CONSTRAINT [AK_ResourceId] UNIQUE NONCLUSTERED
(
[ResourceId] 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]
GO
Into not affecting NULL values. The other posts only give an answer to make a unique index, but an index does not create violation when a duplicate value occurs on INSERT. I want a constraint, but I can't seem to make a WHERE statement on this..
Using SQL Server 2014
Update
It does work with an index after all :)
SQL:
CREATE UNIQUE NONCLUSTERED INDEX IX_ResourceId
ON dbo.Computer(ResourceId)
WHERE ResourceId IS NOT NULL;
This will create the following error:
Cannot insert duplicate key row in object 'dbo.Computer' with unique index 'IX_ResourceId'. The duplicate key value is (1234)

violation of primary key constraint in insert query not touching the PK column

I have a query that inserts record in a table. the primary key column of that table is an Identity field that auto-increments. the select part of the query will have duplicates, but I have an an unique constraint with ignore_dup_key=on on fields (city_nm, prov_en_nm) that should skip them on insert. this used to work fine, but for some reason now it gives me this message. this is the first time I try it since the database was moved from a 2012 sql server to a 2014 if that can have an impact
Violation of PRIMARY KEY constraint 'Dim_city_province_country_pk'. Cannot insert duplicate key in object 'HD_DtlClm.dim_city_province_country_t'. The duplicate key value is (###). (where ### is an ID, a different one every time I run it)
Here is the query.
INSERT INTO HD_DtlClm.[dim_city_province_country_t] (
city_nm, prov_en_nm, prov_fr_nm, contry_fr_nm, contry_en_nm
)
SELECT gr_mbr_city_nm, PROV_ENG_NM, PROV_FR_NM, CONTRY_ENG_NM, CONTRY_FR_NM
FROM isu.gr_dentl_clm_v
LEFT JOIN HD_DtlClm.province_information_t
ON gr_dentl_clm_v.gr_mbr_prov_cd = HD_DtlClm.province_information_t.PROV_CLM_CD
UNION
SELECT gr_prvdr_city_nm, PROV_ENG_NM, PROV_FR_NM, CONTRY_ENG_NM, CONTRY_FR_NM
FROM isu.gr_dentl_clm_v
LEFT JOIN HD_DtlClm.province_information_t
ON gr_dentl_clm_v.gr_prvdr_prov_cd IN (HD_DtlClm.province_information_t.PROV_ENG_CD, HD_DtlClm.province_information_t.PROV_CLM_CD)
Any idea why I get this error that I didn't get in the past?
EDIT to add primary key creation script:
ALTER TABLE [HD_DtlClm].[dim_city_province_country_t] ADD CONSTRAINT [Dim_city_province_country_pk] PRIMARY KEY CLUSTERED
( [cpc_key] 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]
GO
EDIT2 to add table creation script
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [HD_DtlClm].[dim_city_province_country_t](
[cpc_key] [int] IDENTITY(1,1) NOT NULL,
[city_nm] [char](50) NOT NULL,
[prov_en_nm] [char](50) NULL,
[prov_fr_nm] [char](50) NULL,
[contry_en_nm] [char](75) NULL,
[contry_fr_nm] [char](75) NULL,
[create_ts] [datetime] NOT NULL,
[update_ts] [datetime] NOT NULL,
CONSTRAINT [Dim_city_province_country_pk] PRIMARY KEY CLUSTERED
(
[cpc_key] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY],
CONSTRAINT [dim_city_province_country_ak1] UNIQUE NONCLUSTERED
(
[city_nm] ASC,
[prov_en_nm] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = ON, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
ALTER TABLE [HD_DtlClm].[dim_city_province_country_t] ADD DEFAULT (getdate()) FOR [create_ts]
GO
ALTER TABLE [HD_DtlClm].[dim_city_province_country_t] ADD DEFAULT (getdate()) FOR [update_ts]
GO
Try running: DBCC CHECKIDENT ('HD_DtlClm.[dim_city_province_country_t]'); look at the results returned in the messages tab & make sure the current identity value is equal to or higher than the current column value. NB running this may even fix the problem itself.
To expand: looks like something had reseeded your identity column, so the insert was causing duplicates to be picked up. Don't think there's any way to check historically what changed it; the most likely candidates are the DBCC CHECKIDENT command with RESEED option, or a TRUNCATE operation (will reseed to the original value).

Outer join on two tables with sequential guid stalls

I'm attempting to perform a full outer join on two tables that are not related. Each table has a location_id which will eventually form the primary/foreign key relationship (once I figure out this performance issue). When executing the outer join, it just clocks away. Queries and triggers performed against each table on its own complete in less than a second.
This table has 21000 records:
CREATE TABLE [dbo].[TBL_LOCATIONS](
[OBJECTID] [int] NOT NULL,
[Loc_Name] [nvarchar](100) NULL,
[Location_ID] [uniqueidentifier] NULL,
[SHAPE] [geometry] NULL,
CONSTRAINT [R33_pk] PRIMARY KEY CLUSTERED
(
[OBJECTID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 75) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[TBL_LOCATIONS] WITH CHECK ADD CONSTRAINT [g17_ck] CHECK (([SHAPE].[STSrid]=(26917)))
GO
ALTER TABLE [dbo].[TBL_LOCATIONS] ADD CONSTRAINT [DF_TBL_LOCATIONS_Location_ID] DEFAULT (newsequentialid()) FOR [Location_ID]
GO
CREATE SPATIAL INDEX [S17_idx] ON [dbo].[TBL_LOCATIONS]
(
[SHAPE]
)USING GEOMETRY_GRID
WITH (
BOUNDING_BOX =(224827, 3923750, 323464, 3967780), GRIDS =(LEVEL_1 = HIGH,LEVEL_2 = HIGH,LEVEL_3 = HIGH,LEVEL_4 = HIGH),
CELLS_PER_OBJECT = 16, PAD_INDEX = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
CREATE UNIQUE NONCLUSTERED INDEX [UUID_OID_33] ON [dbo].[TBL_LOCATIONS]
(
[Location_ID] ASC,
[OBJECTID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 75) ON [PRIMARY]
GO
This table has 53000 records
CREATE TABLE [dbo].[TBL_EVENTS](
[OBJECTID] [int] NOT NULL,
[Event_ID] [uniqueidentifier] NULL,
[Location_ID] [uniqueidentifier] NULL,
CONSTRAINT [PK_TBL_EVENTS] PRIMARY KEY CLUSTERED
(
[OBJECTID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[TBL_EVENTS] ADD CONSTRAINT [DF_TBL_EVENTS_Event_ID] DEFAULT (newsequentialid()) FOR [Event_ID]
GO
ALTER TABLE [dbo].[TBL_EVENTS] ADD CONSTRAINT [DF_TBL_EVENTS_Event_ID] DEFAULT (newsequentialid()) FOR [Event_ID]
GO
CREATE UNIQUE NONCLUSTERED INDEX [R36_SDE_ROWID_UK] ON [dbo].[TBL_EVENTS]
(
[OBJECTID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 75) ON [PRIMARY]
GO
And here is the query that is running....and running...1 hour and no results.
SELECT
TBL_LOCATIONS.Loc_Name,
TBL_LOCATIONS.Location_ID,
TBL_LOCATIONS.SHAPE,
TBL_EVENTS.Event_ID
FROM
TBL_EVENTS
FULL OUTER JOIN
TBL_LOCATIONS ON TBL_EVENTS.Location_ID = TBL_LOCATIONS.Location_ID
I've tried every permutation of attribute indexes on both tables, rebuilding and reorganizing them, nothing affects the performance. The use of ObjectID as PK is mandated by the application, as is the sequentialGUID. I don't think those are factors here, as both these tables perform splendidly outside of this query. SQL Server 2008 SP1 64BIT on RAID 10/48 GB RAM.
FULL JOIN works well when data in columns used to links tables are unique.
For rows containing duplicated data FULL JOIN behaves like CROSS JOIN and can cause performace issues.
So probably bottleneck comes from duplicates in LOCATION_ID column.
Maybe you need to consider turning off Transaction Logging whilst doing all that.
If the linked field values are not all that unique (location), the query size could approach quite a large number.
In an extreme example, if location only had the value of "1" in both tables, the total rows would be close to the cross join size, about 1,113,000,000 rows (21,000 * 53,000). A query of this size (over a billion rows) will take a long time to run.
EDIT - updating incorrect statement as pointed out in comments

Filtered non-clustered Index abnormal behaivior

I have a table that created some filtered non-clustered on some columns such:
CREATE UNIQUE NONCLUSTERED INDEX [IX_Sh_Esh] ON [dbo].[My_Tbl]
(
[City_Code] ASC,
[Sh_Esh] ASC
)
WHERE ([Sh_Bod]=(0) AND [Noe_Fa]=(0))
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
and:
CREATE UNIQUE NONCLUSTERED INDEX [IX_Kho] ON [dbo].[My_Tbl]
(
[City_Code] ASC,
[Kho] ASC
)
WHERE ([Sh_Bod]=(0) AND [Sh_Esh]=(0) AND [Noe_Fa]=(1))
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
I create this indexes on my table with no error but when I want to add a new column I get this error:
'My_Tbl' table
- Unable to create index 'IX_Sh_Esh'.
The CREATE UNIQUE INDEX statement terminated because a duplicate key was found for the object name 'My_Tbl' and the index name 'IX_Sh_Esh'. The duplicate key value is (3, 0).
The statement has been terminated.
my table data is:
According to first Index because the rows No. 1 and No. 4 does not satisfy the where cluase should not create index on them.Why I get the above error?
thanks
EDIT 1) :
there is Interesting point.If I delete that index and add the Column(s) then ReCreate that Index,the index create with no error.STRANGE!!!!
The reason you can drop the index and then add the data, then recreate the index is because creating an index does not check existing data, just data that you are trying to insert/update.
You're not getting the error because its a filtered index, you're getting it because its a unique index and you're trying to add duplicate values to the table's column that the unique index is on. That's the beaty of them! If you need duplicate data don't make the index unique.

Resources