Indexing in SQLServer - sql-server

Hi I am using SQLServer2008. I want to know what is index in SQLServer and how can i use it?
This is part of my query..how can i give index? Many Thanks..
DECLARE #TableMember TABLE
(
BrokerId INT ,
RankId INT ,
MemberId INT ,
InstallmentId INT ,
PlanId INT ,
IntroducerId INT ,
Date DATETIME ,
SelfAmount DECIMAL(18, 2) ,
UnitAmount DECIMAL(18, 2) ,
SpotAmount DECIMAL(18, 2) ,
ORBPercentageSelf DECIMAL(18, 2) ,
ORBPercentageUnit DECIMAL(18, 2) ,
ORBAmountSelf DECIMAL(18, 2) ,
ORBAmountUnit DECIMAL(18, 2) ,
IsSelfBusiness BIT ,
Mode VARCHAR(50) ,
InstallmentNo INT ,
PlanType VARCHAR(50) ,
PlanName VARCHAR(50) ,
CompanyId INT ,
CscId INT ,
Year VARCHAR(50) ,
CreateDate DATETIME ,
ModifideDate DATETIME
)
INSERT INTO #TableMember
( BrokerId ,
RankId ,
MemberId ,
InstallmentId ,
PlanId ,
IntroducerId ,
Date ,
SelfAmount ,
UnitAmount ,
SpotAmount ,
ORBPercentageSelf ,
ORBPercentageUnit ,
ORBAmountSelf ,
ORBAmountUnit ,
IsSelfBusiness ,
Mode ,
InstallmentNo ,
PlanType ,
PlanName ,
CompanyId ,
CscId ,
Year ,
CreateDate ,
ModifideDate
)
( SELECT BrokerId ,
RankId ,
MemberId ,
InstallmentId ,
PlanId ,
IntroducerId ,
Date ,
SelfAmount ,
UnitAmount ,
SpotAmount ,
ORBPercentageSelf ,
ORBPercentageUnit ,
ORBAmountSelf ,
ORBAmountUnit ,
IsSelfBusiness ,
Mode ,
InstallmentNo ,
PlanType ,
PlanName ,
CompanyId ,
CscId ,
Year ,
CreateDate ,
ModifideDate
FROM dbo.MemberBusiness AS mb
WHERE ( #CscId = 0
OR mb.CscId = #CscId
)
AND mb.Date >= #StartDate
AND mb.Date <= #EndDate
AND mb.RankId >= #FromRankId
AND mb.RankId <= #ToRankId
)

Your index should be built depending on how your data is used. I would suggest reading this Indexing Best Practices as a start.

An index can be created in a table to find data more quickly and efficiently.
The users cannot see the indexes, they are just used to speed up searches/queries.
Note: Updating a table with indexes takes more time than updating a table without (because the indexes also need an update). So you should only create indexes on columns (and tables) that will be frequently searched against.
SQL CREATE INDEX Syntax
Creates an index on a table. Duplicate values are allowed:
CREATE INDEX index_name
ON table_name (column_name)
SQL CREATE UNIQUE INDEX Syntax
Creates a unique index on a table. Duplicate values are not allowed:
CREATE UNIQUE INDEX index_name
ON table_name (column_name)
Note: The syntax for creating indexes varies amongst different databases. Therefore: Check the syntax for creating indexes in your database.
CREATE INDEX Example
The SQL statement below creates an index named "PIndex" on the "LastName" column in the "Persons" table:
CREATE INDEX PIndex
ON Persons (LastName)
If you want to create an index on a combination of columns, you can list the column names within the parentheses, separated by commas:
CREATE INDEX PIndex
ON Persons (LastName, FirstName)

First and foremost, an index allows a query to return results quickly. Most indexes provide a tree structure of some kind that allow a query to skip a lot of comparisons. Instead of checking each and every table row, the query checks whether a target value is greater or less than an index root value, then, if bigger, the query checks a bigger index entry, if smaller, it checks a smaller one, and so on. The beauty of this is that the query doesn't have to check many index entries before it finds out whether the target value exists, and, if so, where an occurance is in the data table.
It's sort of a "divide and conquer" strategy.
A developer or DBA tries to anticipate which table columns will be used in a lot of searches and creates indexes on those columns. The DB SW maintains the index. The DB adds and removes index entries as the underlying table is changed. The only thing the user should be aware of is faster response.
A simple index creation example would be
CREATE INDEX IX_EmployeeName
ON EMPLOYEE(NAME);
Complete index creation syntax for Sqlserver 2008 R2 is available at
http://msdn.microsoft.com/en-us/library/ms188783(v=sql.105).aspx

The example you have provided involves a "table variable". That means you are creating a Transact-SQL variable that can be used like a table in a SQL statement. You often don't need indexes on this sort of table because they are often small. But, if you do need an index, they can be created implicitly as the example shows below. You cannot create them explicitly. You can create a temporary table, however, and index that like any other table.
DECLARE #Employee TABLE
(
ID INT PRIMARY KEY,
NAME VARCHAR(50),
UNIQUE (NAME,ID) -- ID is included to make the indexed value unique even if NAME is not
)

I like this article http://www.mssqltips.com/sqlservertip/1206/understanding-sql-server-indexing/
I understood what is the difference between clustered and non-clustered from here

I would split this into two queries you don't want to try pulling all data or data from one id in the same stored proc.
( #CscId = 0 OR mb.CscId = #CscId)
The primary reason is you probably want a non-clustered index over CscID if you are looking for just say CscID = 104256 but if you are looking for all CscID you probably want an nonclusteredindexl over date column. I would also make sure you actually need a table variable it doesn't look from what you have like there is much of a good reason to toss one in randomly.

Related

Create a table with data validation based on 2 or more columns

I have been trying to create a production ERP using C# and SQL Server.
I want to create a table where the insert statement should only occur when at least one of the 3 main columns have a different value.
The main columns are prod_date, order_no, mach_no, shift_no, prod_type. If all the values are repeated a second time the data must not be entered.
create table p1_order(id int not null,
order_no int not null,
prod_date date notnull,
prod_type nvarchar(5),
shift_no int not null,
mach_no nvarchar(5) not null,
prod_qty,
float not null)
Based on the information you provided, You should check for the identical values when executing insert query, while writing your code. for example you can write:
if(prod_date == order_no == mach_no)// or any other 3 variables
{
//error of identical values
}
else{
// your insert query
}
The best way to implement this is by creating a unique constraint on the table.
alter table p1_order
add constraint UC_Order unique (prod_date,order_no,mach_no,shift_no,prod_type);
Due to some reason, if you are not able to create a unique constraint, you can write your query like the following using NOT EXISTS
insert into p1_order (order_no , prod_date , prod_type <remaining columns>)
select 123, '2022-09-20 15:11:43.680', 't1' <remaining values>
where not exists
(select 1
from p1_order
where order_no = 123 AND prod_date = '2022-09-20 15:11:43.680' <additional condition>)

SQL Server - Loop records insert 100 at a time

I have an INSERT query inside a stored procedure that creates a set of parcels monthly named "MONTHLY_SET".
Sometimes the set gets too big, I need to be able to continue run the same insert query, but rather than insert, for example, 5000 records in a single set named "MONTHLY SET", I need to end up with 5 sets of 1000 each named: "MONTHLY_SET1", "MONTHLY_SET2", "MONTHLY_SET3", "MONTHLY_SET4", "MONTHLY_SET5"
I do not know how this can be achieved, I am not familiar with the use of cursor, and loops in T-SQL, or if those are the only available options to do this.
Would it be possible to ask for some help to understand how can I split a shingle set into smaller sets?
The INSERT query that needs to be inside a loop, currently looks like:
DECLARE #SETSEQ AS INT;
SET #SETSEQ = (SELECT MAX(SET_SEQ_NBR) FROM SETDETAILS);
INSERT INTO SETDETAILS
( SERV_PROV_CODE
, SET_SEQ_NBR
, SET_ID
, REC_DATE
, REC_FUL_NAM
, REC_STATUS
, SOURCE_SEQ_NBR
, L1_PARCEL_NBR
)
SELECT Top 10
'STRING'
, ROW_NUMBER() OVER(ORDER BY ParcelNumber ASC) + #SETSEQ
, 'MONTHLY_SET'
, GETDATE()
, 'USR'
, 'A'
, '155'
, ParcelNumber
FROM
dbo.Parcels
WHERE
Create = 1;
Thank you for your help.

T-SQL logic for roll up and group by

I have a question to collapse or roll up data based on the logic below.
How can I implement it?
The logic that allows episodes to be condensed into a single continuous care episode is a discharge code of 22 followed by an admission code of 4 on the same day.
continuous care implementation update
EPN--is a business_key.
episode_continuous_care_key is an artificial key that can be a row number function.
Below is the table structure.
drop table #source
CREATE TABLE #source(patidid varchar(20),epn int,preadmitdate datetime,adminttime varchar(10),
admitcode varchar(10),datedischarge datetime,disctime varchar(10),disccode varchar(10))
INSERT INTO #source VALUES
(1849,1,'4/23/2020','7:29',1,'7/31/2020','9:03',22)
,(1849,2,'7/31/2020','11:00',4,'7/31/2020','12:09',22)
,(1849,3,'7/31/2020','13:10',4,'8/24/2020','10:36',10)
,(1849,4,'8/26/2020','12:25',2,null,null,null)
,(1850,1,'4/23/2020','7:33',1,'6/29/2020','7:30',22)
,(1850,2,'6/29/2020','9:35',4,'7/8/2020','10:51',7)
,(1850,3,'7/10/2020','11:51',3,'7/29/2020','9:12',7)
,(1850,4,'7/31/2020','11:00',2,'8/6/2020','10:24',22)
,(1850,5,'8/6/2020','12:26',4,null,null,null)
,(1851,1,'4/23/2020','7:35',1,'6/24/2020','13:45',22)
,(1851,2,'6/24/2020','15:06',4,'9/24/2020','15:00',2)
,(1851,3,'12/4/2020','8:59',0,null,null,null)
,(1852,1,'4/23/2020','7:37',1,'7/6/2020','11:15',20)
,(1852,2,'7/8/2020','10:56',0,'7/10/2020','11:46',2)
,(1852,3,'7/10/2020','11:47',2,'7/28/2020','13:16',22)
,(1852,4,'7/28/2020','15:17',4,'8/4/2020','11:37',22)
,(1852,5,'8/4/2020','13:40',4,'11/18/2020','15:43',2)
,(1852,6,'12/2/2020','15:23',2,null,null,null)
,(1853,1,'4/23/2020','7:40',1,'7/1/2020','8:30',22)
,(1853,2,'7/1/2020','14:57',4,'12/4/2020','12:55',7)
,(1854,1,'4/23/2020','7:44',1,'7/31/2020','13:07',20)
,(1854,2,'8/3/2020','16:30',0,'8/5/2020','9:32',2)
,(1854,3,'8/5/2020','10:34',2,'8/24/2020','8:15',22)
,(1854,4,'8/24/2020','10:33',4,'12/4/2020','7:30',22)
,(1854,5,'12/4/2020','9:13',4,null,null,null)
That Excel sheet image says little about your database design so I invented my own version that more or less resembles your image. With a proper database design the first step of the solution should not be required...
Unpivot timestamp information so that admission timestamp and discharge timestamps become one column.
I used a common table expression Log1 for this action.
Use the codes to filter out the start of the continuous care periods. Those are the admissions, marked with Code.IsAdmission = 1 in my database design.
Also add the next period start as another column by using the lead() function.
These are all the actions from Log2.
Add a row number as continuous care key.
Using the next period start date, find the current continuous period end date with a cross apply.
Replace empty period end dates with the current date using the coalesce() function.
Calculate the difference as the continuous care period duration with the datediff() function.
Sample data
create table Codes
(
Code int,
Description nvarchar(50),
IsAdmission bit
);
insert into Codes (Code, Description, IsAdmission) values
( 1, 'First admission', 1),
( 2, 'Re-admission', 1),
( 4, 'Campus transfer IN', 0),
(10, 'Trial visit', 0),
(22, 'Campus transfer OUT', 0);
create table PatientLogs
(
PatientId int,
AdmitDateTime smalldatetime,
AdmitCode int,
DischargeDateTime smalldatetime,
DischargeCode int
);
insert into PatientLogs (PatientId, AdmitDateTime, AdmitCode, DischargeDateTime, DischargeCode) values
(1849, '2020-04-23 07:29', 1, '2020-07-31 09:03', 22),
(1849, '2020-07-31 11:00', 4, '2020-07-31 12:09', 22),
(1849, '2020-07-31 13:10', 4, '2020-08-24 10:36', 10),
(1849, '2020-08-26 12:25', 2, null, null);
Solution
with Log1 as
(
select updt.PatientId,
case updt.DateTimeType
when 'AdmitDateTime' then updt.AdmitCode
when 'DischargeDateTime' then updt.DischargeCode
end as Code,
updt.LogDateTime,
updt.DateTimeType
from PatientLogs pl
unpivot (LogDateTime for DateTimeType in (AdmitDateTime, DischargeDateTime)) updt
),
Log2 as (
select l.PatientId,
l.Code,
l.LogDateTime,
lead(l.LogDateTime) over(partition by l.PatientId order by l.LogDateTime) as LogDateTimeNext
from Log1 l
join Codes c
on c.Code = l.Code
where c.IsAdmission = 1
)
select la.PatientId,
row_number() over(partition by la.PatientId order by la.LogDateTime) as ContCareKey,
la.LogDateTime as AdmitDateTime,
coalesce(ld.LogDateTime, convert(smalldatetime, getdate())) as DischargeDateTime,
datediff(day, la.LogDateTime, coalesce(ld.LogDateTime, convert(smalldatetime, getdate()))) as ContStay
from Log2 la -- log admission
outer apply ( select top 1 l1.LogDateTime
from Log1 l1
where l1.PatientId = la.PatientId
and l1.LogDateTime < la.LogDateTimeNext
order by l1.LogDateTime desc ) ld -- log discharge
order by la.PatientId,
la.LogDateTime;
Result
PatientId ContCareKey AdmitDateTime DischargeDateTime ContStay
--------- ----------- ---------------- ----------------- --------
1849 1 2020-04-23 07:29 2020-08-24 10:36 123
1849 2 2020-08-26 12:25 2021-02-03 12:49 161
Fiddle to see things in action with intermediate results.
Here is a T-SQL solution that contains primary and foreign key relationships.
To make it a bit more realistic, I added a simple "Patient" table.
I put all your "codes" into a single table which should make it easier to manage the codes.
I do not understand the purpose of your concept of "continuous care" so I just added an "is first" binary column to the Admission table.
You might also consider adding something about the medical condition for which the patient is being treated.
CREATE SCHEMA Codes
GO
GO
CREATE TABLE dbo.Code
(
codeNr int NOT NULL,
description nvarchar(50),
CONSTRAINT Code_PK PRIMARY KEY(codeNr)
)
GO
CREATE TABLE dbo.Patient
(
patientNr int NOT NULL,
birthDate date NOT NULL,
firstName nvarchar(max) NOT NULL,
lastName nvarchar(max) NOT NULL,
CONSTRAINT Patient_PK PRIMARY KEY(patientNr)
)
GO
CREATE TABLE dbo.Admission
(
admitDateTime time NOT NULL,
patientNr int NOT NULL,
admitCode int,
isFirst bit,
CONSTRAINT Admission_PK PRIMARY KEY(patientNr, admitDateTime)
)
GO
CREATE TABLE dbo.Discharge
(
dischargeDateTime time NOT NULL,
patientNr int NOT NULL,
dischargeCode int NOT NULL,
CONSTRAINT Discharge_PK PRIMARY KEY(patientNr, dischargeDateTime)
)
GO
ALTER TABLE dbo.Admission ADD CONSTRAINT Admission_FK1 FOREIGN KEY (patientNr) REFERENCES dbo.Patient (patientNr) ON DELETE NO ACTION ON UPDATE NO ACTION
GO
ALTER TABLE dbo.Admission ADD CONSTRAINT Admission_FK2 FOREIGN KEY (admitCode) REFERENCES dbo.Code (codeNr) ON DELETE NO ACTION ON UPDATE NO ACTION
GO
ALTER TABLE dbo.Discharge ADD CONSTRAINT Discharge_FK1 FOREIGN KEY (patientNr) REFERENCES dbo.Patient (patientNr) ON DELETE NO ACTION ON UPDATE NO ACTION
GO
ALTER TABLE dbo.Discharge ADD CONSTRAINT Discharge_FK2 FOREIGN KEY (dischargeCode) REFERENCES dbo.Code (codeNr) ON DELETE NO ACTION ON UPDATE NO ACTION
GO
GO

T-SQL - Value does not match at least one item in list

I have an MS SQL table with several columns, each for different employee roles, and a corresponding column for each indicating whether the current employee in that role is active, on leave, terminated, etc. Due to dependency on an external data source, the fields aren't really normalized.
I'm looking to run a query to return all of the rows where at least one of the 'is active' columns is not equal to 'ACTIVE'. There are several possible values other than Active.
I know one of the longer ways to do this would be
SELECT * FROM MYTABLE
WHERE IsActive1 <> 'ACTIVE' OR
IsActive2 <> 'ACTIVE' OR
IsActive3 <> 'ACTIVE' OR
IsActive4 <> 'ACTIVE' OR
... etc
Just wondering if there is a shorter way, possibly more efficient way to do this. I've seen plenty of solutions for finding a match across multiple columns, but not one for looking for non-matches.
I don't know that this is more elegant, but you could do something like this:
where replace(IsActive1 + IsActive2 + IsActive3 + . . .,
'ACTIVE', '') <> ''
Note that if the values could be NULL, then you would need to replace them with something else. Also, this assumes that the strings themselves are not empty.
EDIT:
If you want to do this efficiently and want the code to look good, add a computed column and build an index on that column:
alter table mytable
add IsAllActive as (case when IsActive1 = 'ACTIVE' and
IsActive2 = 'ACTIVE' and
. . .
then 'ACTIVE'
else 'INACTIVE'
end);
create index mytable_IsAllActive on mytable(IsAllActive);
You might want to add other relevant columns to the index as well.
Try using a filtered index, which is quite limited in what you can include in the WHERE clause:
--simulate your existing table
create table Test (RowID int identity(1,1) primary key
,c1 varchar(10), c2 varchar(10),c3 varchar(10),c4 varchar(10)
)
go
--simulate your existing data
INSERT INTO Test VALUES ('ACTIVE','ACTIVE','ACTIVE','ACTIVE') --1
, ('ACTIVE','ACTIVE','ACTIVE','ACTIVE') --2
, ('ACTIVE','ACTIVE','ACTIVE','ACTIVE') --3
, ('INACTIVE','ACTIVE','ACTIVE','ACTIVE') --4 <Inactive
, ('ACTIVE','INACTIVE','ACTIVE','ACTIVE') --5 <Inactive
, ('ACTIVE','ACTIVE','INACTIVE','ACTIVE') --6 <Inactive
, ('ACTIVE','ACTIVE','ACTIVE','INACTIVE') --7 <Inactive
, ('INACTIVE','ACTIVE','ACTIVE','INACTIVE') --8 <Inactive
, ('INACTIVE','ACTIVE','INACTIVE','INACTIVE') --9 <Inactive
, ('INACTIVE','INACTIVE','INACTIVE','INACTIVE')--10<Inactive
go
--what you need to add to your existing table
CREATE NONCLUSTERED INDEX fIX_Test_c1 ON Test(c1) WHERE C1 !='ACTIVE'
CREATE NONCLUSTERED INDEX fIX_Test_c2 ON Test(c2) WHERE C2 !='ACTIVE'
CREATE NONCLUSTERED INDEX fIX_Test_c3 ON Test(c3) WHERE C3 !='ACTIVE'
CREATE NONCLUSTERED INDEX fIX_Test_c4 ON Test(c4) WHERE C4 !='ACTIVE'
--your query o get the data
SELECT * FROM Test WHERE c1 !='ACTIVE'
UNION SELECT * FROM Test WHERE c2 !='ACTIVE'
UNION SELECT * FROM Test WHERE c3 !='ACTIVE'
UNION SELECT * FROM Test WHERE c4 !='ACTIVE'
you could try a persisted computed column, with an index:
ALTER TABLE Test ADD C_Summary AS (LEFT(ISNULL(c1,'Z'),1)+LEFT(ISNULL(c1,'Z'),1)+LEFT(ISNULL(c1,'Z'),1)+LEFT(ISNULL(c1,'Z'),1))
go
CREATE NONCLUSTERED INDEX fIX_Test_C_Summary ON Test(C_Summary)
go
select * from test WHERE C_Summary !='AAAA'
You'll have to try these (and other variations) out on your data, which can skew the index usage. Also, you might want to try changing the table design, where you split all the C1, C2, C3, C4,... columns into different rows in a child table. You could then do a single filtered index on that table.

Is it possible to a db constraint in for this rule?

I wish to make sure that my data has a constraint the following check (constraint?) in place
This table can only have one BorderColour per hub/category. (eg. #FFAABB)
But it can have multiple nulls. (all the other rows are nulls, for this field)
Table Schema
ArticleId INT PRIMARY KEY NOT NULL IDENTITY
HubId TINYINT NOT NULL
CategoryId INT NOT NULL
Title NVARCHAR(100) NOT NULL
Content NVARCHAR(MAX) NOT NULL
BorderColour VARCHAR(7) -- Can be nullable.
I'm gussing I would have to make a check constraint? But i'm not sure how, etc.
sample data.
1, 1, 1, 'test', 'blah...', '#FFAACC'
1, 1, 1, 'test2', 'sfsd', NULL
1, 1, 2, 'Test3', 'sdfsd dsf s', NULL
1, 1, 2, 'Test4', 'sfsdsss', '#AABBCC'
now .. if i add the following line, i should get some sql error....
INSERT INTO tblArticle VALUES (1, 2, 'aaa', 'bbb', '#ABABAB')
any ideas?
CHECK constraints are ordinarily applied to a single row, however, you can cheat using a UDF:
CREATE FUNCTION dbo.CheckSingleBorderColorPerHubCategory
(
#HubID tinyint,
#CategoryID int
)
RETURNS BIT
AS BEGIN
RETURN CASE
WHEN EXISTS
(
SELECT HubID, CategoryID, COUNT(*) AS BorderColorCount
FROM Articles
WHERE HubID = #HubID
AND CategoryID = #CategoryID
AND BorderColor IS NOT NULL
GROUP BY HubID, CategoryID
HAVING COUNT(*) > 1
) THEN 1
ELSE 0
END
END
Then create the constraint and reference the UDF:
ALTER TABLE Articles
ADD CONSTRAINT CK_Articles_SingleBorderColorPerHubCategory
CHECK (dbo.CheckSingleBorderColorPerHubCategory(HubID, CategoryID) = 1)
Another option that is available is available if you are running SQL2008. This version of SQL has a feature called filtered indexes.
Using this feature you can create a unique index that includes all rows except those where BorderColour is null.
CREATE TABLE [dbo].[UniqueExceptNulls](
[HubId] [tinyint] NOT NULL,
[CategoryId] [int] NOT NULL,
[BorderColour] [varchar](7) NULL,
)
GO
CREATE UNIQUE NONCLUSTERED INDEX UI_UniqueExceptNulls
ON [UniqueExceptNulls] (HubID,CategoryID)
WHERE BorderColour IS NOT NULL
This approach is cleaner than the approach in my other answer because it doesn't require creating extra computed columns. It also doesn't require you to have a unique column in the table, although you should have that anyway.
Finally, it will also be much faster than the UDF/Check Constraint solutions.
You can also do a trigger with something like this (this is actually overkill - you can make it cleaner by assuming the database is already in a valid state - i.e. UNION instead of UNION all etc):
IF EXISTS (
SELECT COUNT(BorderColour)
FROM (
SELECT INSERTED.HubId, INSERTED.CategoryId, INSERTED.BorderColour
UNION ALL
SELECT HubId, CategoryId, BorderColour
FROM tblArticle
WHERE EXISTS (
SELECT *
FROM INSERTED
WHERE tblArticle.HubId = INSERTED.HubId
AND tblArticle.CategoryId = INSERTED.CategoryId
)
) AS X
GROUP BY HubId, CategoryId
HAVING COUNT(BorderColour) > 1
)
RAISEERROR
If you have a unique column in your table, then you can accomplish this by creating a unique constraint on a computer column.
The following sample created a table that behaved as you described in your requirements and should perform better than a UDF based check constraint. You might also be able to improve the performance further by making the computed column persisted.
CREATE TABLE [dbo].[UQTest](
[Id] INT IDENTITY(1,1) NOT NULL,
[HubId] TINYINT NOT NULL,
[CategoryId] INT NOT NULL,
[BorderColour] varchar(7) NULL,
[BorderColourUNQ] AS (CASE WHEN [BorderColour] IS NULL
THEN cast([ID] as varchar(50))
ELSE cast([HuBID] as varchar(3)) + '_' +
cast([CategoryID] as varchar(20)) END
),
CONSTRAINT [UQTest_Unique]
UNIQUE ([BorderColourUNQ])
)
The one possibly undesirable facet of the above implementation is that it allows a category/hub to have both a Null AND a color defined. If this is a problem, let me know and I'll tweak my answer to address that.
PS: Sorry about my previous (incorrect) answer. I didn't read the question closely enough.

Resources