SQL Conditional Unique Constraint With Where Clause Within Same Table - sql

I have a table where I want to ensure that a combination of five columns remain unique within that table. For example:
ALTER TABLE [dbo].[MyTable]
ADD CONSTRAINT [UQ__MyTable.MFG.Model.Class.Depiction.Iteration]
UNIQUE NONCLUSTERED
(
[ManufacturerID] ASC,
[Model] ASC,
[BlockClassID] ASC,
[BlockDepictionID] ASC,
[BlockIterationID] 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
I want to exclude combinations where a sixth separate column has a particular value. For example, I only want to enforce this above constraint when the column [Flag] = 0 and exclude enforcement when the column [Flag] = 1 .

As workaround, You can get the proper ANSI behavior in SQL Server 2008 and above by creating a unique, filtered index.
CREATE UNIQUE NONCLUSTERED INDEX [IX__MyTable.MFG.Model.Class.Depiction.Iteration]
ON [dbo].[MyTable] ([ManufacturerID],[Model],[BlockClassID],[BlockDepictionID],[BlockIterationID])
WHERE [Flag] = 0;
TechNet article

Related

Why is my filtered index not being used for one statement?

I have the following table (an excerpt follows w/o other columns):
USE [opg-systems-dev]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Orders]
(
[SyncChannelEngineOrder] [bit] NOT NULL,
[IsSyncing] [bit] NOT NULL,
CONSTRAINT [PK_Orders]
PRIMARY KEY CLUSTERED ([Id] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Orders]
ADD DEFAULT (CONVERT([bit], (0))) FOR [IsSyncing]
GO
-- -----
CREATE NONCLUSTERED INDEX [IX_Orders_SyncChannelEngineOrder]
ON [dbo].[Orders] ([SyncChannelEngineOrder] ASC)
WHERE ([SyncChannelEngineOrder] <> (0))
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON,
OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
CREATE NONCLUSTERED INDEX [IX_Orders_IsSyncing]
ON [dbo].[Orders] ([IsSyncing] ASC)
WHERE ([IsSyncing] <> (0))
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON,
OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
I'm trying to use a filtered index here - but it seems when I'm querying that the the index for SyncChannelEngineOrder is not being used for some reason.
The setup is supposed to be the same for both columns.
The query is requesting all columns with a select *. The indexes only contain one column (ignoring the clustered key). To provide the missing column data that is not on the index, the query optimizer is calculating a lower cost to just scan all of the rows in the source table.
If SQL Server chose to use the existing indexes, it would first scan those rows, and then it would perform a bookmark lookup to get the rest of the columns from the table. If there are very few rows in the filtered index, and the table is very large, the SQL Server query optimizer could theoretically calculate a low enough cost to use the filtered index to limit the number of rows. Otherwise, bookmark lookups are expensive on a large number of rows. I am guessing the number of rows in the index is over this threshold.
SQL Server will add the clustered key to a non-clustered index to facilitate the bookmark lookup. If the query were to request only the clustered key and the index filter columns, the indexes would cover the query, and I expect the query optimizer would choose to use the filtered indexes.

SQL Server - Is creating an index with a unique constraint as one of the columns necessary?

I have the query below:
SELECT PrimaryKey
FROM dbo.SLA
WHERE SLAName = #input
AND FK_SLA_Process = #input2
AND IsActive = 1
And this is my index for this SLA table.
CREATE INDEX IX_SLA_SLAName_FK_SLA_Process_IsActive ON dbo.SLA (SLAName, FK_SLA_Process, IsActive) INCLUDE (SLATimeInSeconds)
However, the SLAName column is unique so it has a unique constraint/index.
Is my created index an overkill? Do I still need it or will SQL Server use the index created on the unique column SLAName?
It would be an "overkill" if your index would only be on SLAName, but you are also ordering by FK_SLA_Process and IsActive so queries that need needs columns will benefit more from your index and less if you just had the unique one.
So for a query like this:
SELECT PrimaryKey
FROM dbo.SLA
WHERE SLAName = 'SomeName'
Both index will yield the same results and there would be no point in yours. But for queries like:
SELECT PrimaryKey
FROM dbo.SLA
WHERE SLAName = 'SomeName'
AND FK_SLA_Process = 'Some Value'
Or
SELECT SLATimeInSeconds
FROM dbo.SLA
WHERE SLAName = 'SomeName'
Your index will be better than the unique one (2nd example is a covering index).
You should inspect which kind of SELECT you do to this table and decide if you need this one or not. Keep in mind that having many indexes might speed up selects but slow down inserts, updates and deletes.
Assuming you have a such table declaration:
CREATE TABLE SLA
(
ID INT PRIMARY KEY,
SLAName VARCHAR(50) NOT NULL UNIQUE,
fk_SLA INT,
IsActive TINYINT
)
Under the hood we have two indexes:
CREATE TABLE [dbo].[SLA](
[ID] [int] NOT NULL,
[SLAName] [varchar](50) NOT NULL,
[fk_SLA] [int] NULL,
[IsActive] [tinyint] NULL,
PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY],
UNIQUE NONCLUSTERED
(
[SLAName] 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
So this query will have an index seek and has an optimal plan:
SELECT s.ID
FROM dbo.SLA s
WHERE s.SLAName = 'test'
Its query plan indicates index seek because we are searching by index UNIQUE NONCLUSTERED ([SLAName] ASC ) and don't use other columns in WHERE statement:
But if you add extra parameters into WHERE:
SELECT s.ID
FROM dbo.SLA s
WHERE s.SLAName = 'test'
AND s.fk_SLA = 1
AND s.IsActive = 1
Execution plan will have extra look up:
Lookup happens when index does not have necessary information. SQL Query engine has to get out from UNIQUE NONCLUSTERED index data structure to find data of columns fk_SLA and IsActive in your table SLA.
So your index is overkill as you have UNIQUE NONCLUSTERED index:
UNIQUE NONCLUSTERED
(
[SLAName] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
If SLAName column is unique and it has a unique constraint, any query that returns only one or 0 rows (all the queries with a point search that include SLAName = 'SomeName' condition) will use the unique index and make (maximum) one lookup in the base table.
Unless your queries have a range search like SLAName like 'SomeName%' there is no need in covering index because index search + 1 lookup is almost the same as only index search, and there is no need to waste space / maintain another index for such a miserable performance gain.

Non Clustered Index on UniqueIdentifier columns

WE have a simple SQL query selecting two columns like below
SELECT TEAMID,iSvALID FROM teams where teamid='{{guid}}' and subid = {{guid}}.
we had issues with performance of this query as there was no index which I also have added now... like
CREATE NONCLUSTERED INDEX [NonClusteredIndex-20170725-191322] ON [dbo].[TableName]
(
[subid ] ASC,
[TeamId] ASC,
[Date] ASC
)
INCLUDE ([IsVAlid])
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
The problem now is that it takes a few Milliseconds sometimes to execute the query but a few seconds!!! some times .
And because of some constraints we can't change the type of GUID column and also we get this from an exernal system.
Is there a way we can still make sure performance will be good

Composite Keys SQL server

I have created a joining table for many-to-many relationship.
The table only has 2 cols in it, ticketid and groupid
typical data would be
groupid ticketid
20 56
20 87
20 96
24 13
24 87
25 5
My question is when creating the composite key should I have ticketid followed by groupid
CONSTRAINT [PK_ticketgroup] PRIMARY KEY CLUSTERED
(
[ticketid] ASC,
[groupid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
Or the other way, groupid followed by ticketid
CONSTRAINT [PK_ticketgroup] PRIMARY KEY CLUSTERED
(
[groupid] ASC,
[ticketid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
Would searching the index be quicker in option 1 as the ticketid's have more chance of being unique then the groupids and they would be at the start of the composite key? Or is this negligible?
The difference would most likely be negligible.
It is however recommended for SQL Server that the most selective column be placed fist. If a column with low selectivity is placed first, the Optimizer may determine that your index is not very selective and will choose to ignore it. See this sqlserverpedia.com Wiki Article for more information.
I would actually create two indexes. Given that ticket IDs are more likely to be unique, the clustered index would be GroupID,TicketID in that order. I would then create a non-clustered non-unique index on TicketID.
The reason being that if you wanted to query based only on group ID, they would logically be contiguous and there would be a block of them. The other index will give you fastest when only TicketID is specified.
I do think it would probably be negligible overall depending on how the data will be queried (i.e. if groupid and ticketid are always provided).

create primary key on existing table with data

As part of a migration project, we have imported data from a JDE iSeries DB2 database. An SSIS package was created to create the destination tables and import data. The import went successfully.
Now comes the problem - The customer wants Primary Keys created in the destination DB (SQL 2008 R2). The problem table in this case, would be one table that has 104 columns and 7.5 million rows of data. The PK required for this table is composite and has 7 columns.
We are considering this :
BEGIN TRANSACTION
GO
ALTER TABLE [dbo].[F0911] ADD CONSTRAINT [F0911_PK] PRIMARY KEY CLUSTERED
(
[GLDCT] ASC,
[GLDOC] ASC,
[GLKCO] ASC,
[GLDGJ] ASC,
[GLJELN] ASC,
[GLLT] ASC,
[GLEXTL] 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
COMMIT
or this:
-- Rename existing tables
sp_RENAME '[F0911]' , '[F0911_old]'
GO
-- Create new table
SELECT * INTO F0911 FROM F0911_old WHERE 1=0
GO
--Create PK constraints
ALTER TABLE [dbo].[F0911] ADD CONSTRAINT [F0911_PK] PRIMARY KEY CLUSTERED
(
[GLDCT] ASC,
[GLDOC] ASC,
[GLKCO] ASC,
[GLDGJ] ASC,
[GLJELN] ASC,
[GLLT] ASC,
[GLEXTL] 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
--Insert data into new tables
INSERT INTO F0911
SELECT * FROM F0911_old
GO
-- Drop old tables
DROP TABLE F0911_old
GO
Which would be a more efficient approach, performance wise? I have a gut feeling that both are the same and even the first approach does the same thing as the second one does, implicitly. Is this understanding correct?
Please note that all these columns already exist in the table and we cannot modify the table definition.
Thanks,
Raj
They're the same. The effect of creating a clustered index is to arrange the pages which will happen in both cases. For non-clustered indexes it will help to disable the index and then turn it back on and rebuilding it.
I think the first approach is right, but I don't understand the reason of BEGIN Transaction and END transaction. I don't think Transaction keyword is necessary because you are not modifying data of the table. Transaction is used where we have to lock the data and we are modifying real time data so the old data is not used.