SQL Server DB Project Publish Empty string inserting a Zero - sql-server

We are seeing a very strange problem when populating a field in a DB via a SQL Server DB project publish action.
This is the table definition
CREATE TABLE [dbo].[CatalogueItemExtensionFields]
(
[RowID] tinyint identity not null,
[FieldType] tinyint not null,
[Description] varchar(120) not null,
[Nullable] bit not null,
[DefaultValue] varchar(100) null,
[Active_Flag] bit null,
[OrderPriority] tinyint not null,
[ContextGuid] uniqueidentifier not null
);
This is the population script
set identity_insert CatalogueItemExtensionFields on
INSERT INTO CatalogueItemExtensionFields (rowid, fieldtype, description, nullable, defaultvalue, active_flag, orderpriority)
VALUES (dbo.ConstantProductGroupRowId(), 3, 'Product Group', 0, '', 1, dbo.ConstantProductGroupRowId()),
set identity_insert CatalogueItemExtensionFields off
If I run the INSERT script manually all works fine. When I run it as part of the DB project publish, it inserts "0".
I have looked at the publish.sql script that is generated, and all looks fine.
BTW, the only similar post I have found is this, but it does not apply to our case because the field we are inserting into is defined as varchar.
This is driving us mad. Any ideas?

We at our company finally found out that if you use SQLCMD / DBProj then it is super import to ENABLE Quoted Identifiers. Or else installer changes inputs in the exactly same way as #maurocam explained. If you enable this, then it works same as in SQL Management Studio for example.
To enable it:
SQLCMD
just use parameter -I (capital is important here, small is for file).
Example sqlcmd -S localhost -d DBNAME -U User -P Password -i path/to/sql/file.sql -I
SQL Itself
SET QUOTED_IDENTIFIER { ON | OFF }
DBProj
can be set at the project or object (proc, func, ...) level. Just click on a proj/file -> Properties and check if QUOTED_IDENTIFIER is enabled there.
For schema compare it can be set via "Ignore quoted identifiers"
https://learn.microsoft.com/en-us/sql/t-sql/statements/set-quoted-identifier-transact-sql?view=sql-server-ver16

APOLOGIES - MY MISTAKE!!! (But a very useful one to document)
I have summarised again below (also to make it clearer respect to my initial post)
TABLE DEFINITION
CREATE TABLE [dbo].[CatalogueItemExtensionFields]
(
[RowID] tinyint identity not null,
[FieldType] tinyint not null,
[Description] varchar(120) not null,
[Nullable] bit not null,
[DefaultValue] varchar(100) null,
[Active_Flag] bit null,
[OrderPriority] tinyint not null
);
INSERT STATEMENT
set identity_insert CatalogueItemExtensionFields on
INSERT INTO CatalogueItemExtensionFields (rowid, fieldtype, description, nullable, defaultvalue, active_flag, orderpriority) VALUES
(6, 3, N'Product Group', 0, N'', 1, 6),
(7, 2, N'Minimum Order Quantity', 1, NULL, 1, 7),
(8, 3, N'Additional HIBCs', 0, 1, 1, 8),
(9, 3, N'Additional GTINs', 0, N'', 1, 9)
set identity_insert CatalogueItemExtensionFields off
Because I am inserting multiple rows, when SQL parses the statement it see I am trying to insert a numeric defaultvalue = 1 for RowID = 8. As a result, even though the column is defined as a varchar, SQL decides that the INSERT statement is inserting INTs. So the empty string values (for RowIDs 7 and 9) are converted to zero. I referred to a post I had found relating to actual INT column, which results in the same behaviour.
If I instead run the following statement, with a default value of '1' for RowID = 8, it all works fine.
INSERT INTO CatalogueItemExtensionFields (rowid, fieldtype, description, nullable, defaultvalue, active_flag, orderpriority) VALUES
(6, 3, N'Product Group', 0, N'', 1, 6),
(7, 2, N'Minimum Order Quantity', 1, NULL, 1, 7),
(8, 3, N'Additional HIBCs', 0, '1', 1, 8),
(9, 3, N'Additional GTINs', 0, N'', 1, 9)
So, now the question is, why does SQL server ignore the column type definition and instead decides the type from the value in my INSERT statement?

Answer from SqlServerCentral
"empty string converts to an int without an error, and it's value is zero. it has to do with the precidence of implicit conversions"

Related

MSSQL - Need to query multiple records into one record / set of results - based on Id and Date

**Updated title..
First and foremost: I know it sounds like a repetitive question, but I've been scouring the web since yesterday afternoon for a solution.
I have 2 tables, this is my test stuff:
create table #UserMaster
(
UserId int identity(1,1),
FirstName varchar(50),
LastName varchar(50),
Department smallint,
JobCode varchar(8),
);
create table #UserUpdateInfoMain
(
Id int identity(1,1),
UserId int,
DateRequested smalldatetime,
SubmittedBy varchar(20),
RequestedBy varchar(20),
NameChange varchar(50),
TransferDept smallint,
TransferJob varchar(8),
TerminationDate date
);
Our HR department will submit requests for employee name changes, and transfer requests (for jobs or departments), and Termination requests. The idea behind this table (UserUpdatedInfoMain) is to house all such requests, and link them up as appropriate with the primary User information (in UserMaster). So, when we search for the user in the database, the idea is to be able to see their current last name, and current Department / Job assignment, and or if they've been terminated - also being able to see a trail of all such changes is a plus.
One request will come through for a name-change for Bob. A week later Bob may change positions, so a department transfer request will come in. I am trying to do a query to see: Bob -> NewName -> NewDept (one record, not multiple)
The trick of it is that there might be multiple name changes, and transfers, so I need to get the most recent of each one to show up in the query. I've found a few things that are somewhat similar that I can't seem to get to work (including using min/max - works great if it's just looking for one result), and I managed to get the results once along with every other row showing up at one point, just not able to discard all of the unnecessary results.
Here is the sample data that I'm playing with:
INSERT INTO #UserMaster (FirstName,LastName,Department,JobCode)
values ('John','Smith',1212,'A1234'), --1
('James','Todd',1232,'B2345'), --2
('Steph','Williams',1212,'A1234'), --3
('Casey','Bates',1212,'C2342'), --4
('Rob','Johnson',3434,'A1234'), --5
('Cindy','Lou',2314,'A1234'),
('Jesse','Dates',2323,'D3422'),
('Apple','Cider',1342,'B2312'),
('Pepsi','Cola',1432,'A1234'),
('Random','Bob',1342,'C3421'),
('Hulk','Hogan',3422,'C3221'),
('John','Cena',3432,'B2231'),
('Sasha','Banks',2321,'B2312'),
('Jimmy','Iovine',3432,'A1234'),
('Dwayne','Johnson',1325,'C2342'),
('Sydney','Pierce',1241,'A1234'),
('Expo','Marks',4321,'B2312'),
('Pat','Swizzle',2521,'C2342'),
('Monkey','Bones',1212,'D3422'),
('Happy','Gilmore',4545,'D3422');
INSERT INTO #UserUpdateInfoMain (UserId, DateRequested, SubmittedBy, RequestedBy, NameChange, TransferDept, TransferJob, TerminationDate)
values (1, cast(getdate()-5 as smalldatetime), 'rob', 'bob', 'Waters1', NULL, NULL, NULL),
(2, cast(getdate()-4 as smalldatetime), 'rob', 'bob', NULL, 7878, 'J7098', NULL),
(3, cast(getdate()-3 as smalldatetime), 'rob', 'bob', 'Cider1', NULL, NULL, NULL),
(4, cast(getdate()-4 as smalldatetime), 'rob', 'bob', NULL, NULL, NULL, cast(getdate()-3 as date)),
(5, cast(getdate()-2 as smalldatetime), 'rob', 'bob', NULL, 9898, NULL, NULL),
(1, cast(getdate()-3 as smalldatetime), 'jim', 'bob', 'Lakely2', NULL, NULL, NULL),
(1, cast(getdate() as smalldatetime), 'sue', 'bob', 'Salsa3', NULL, NULL, NULL),
(1, cast(getdate() as smalldatetime), 'sue', 'bob', NULL, 9648, 'K9487', NULL),
(2, cast(getdate()-1 as smalldatetime),'rosco', 'bob', 'Mordor1', NULL, NULL, NULL),
(2, cast(getdate() as smalldatetime), 'rosco', 'bob', 'Elves2', NULL, NULL, NULL);
Two examples that should show up ultimately are shown below...naturally, The other 3 should show up as well
(#UserUpdateInfoMain)
UserId NameChange TransferDept TransferJob TerminationDate
1 Salsa3 9648 K9487 NULL
2 Elves2 9343 H8898 NULL
Apologies for the lengthy post, I just wanted to make sure I explained things decently and had a working example of what I'm playing with. I also apologize for not providing a link / example for everything I've looked out and done.
I tried doing a CTE for max date on each column and joining it with #UserUpdatedInfoMain table, but that didn't exactly work out; I'm not able to find the right combination to get them to work properly.
EXAMPLE:
theNameChanged_cte (UserId, Req_Date)
as
(
select
i2.UserId,
max(i2.DateRequested) as Req_Date
from #UserUpdateInfoMain i2
group by i2.UserId
)
...or...
theNameChanged_cte (UserId, Req_Date, NameChange)
as
(
select
i2.UserId,
max(i2.DateRequested) as Req_Date,
i2.NameChange
from #UserUpdateInfoMain i2
group by i2.UserId, i2.NameChange
having i2.NameChange is not null
)
The first CTE legit just looks for the most recent date for the given UserId - okay cool...that's partway there. The second one pulls ALL of the NameChange requests, so some of the UserId's show up twice. If I could use MAX with a where statement, I think I could get this licked. I think my brain is just fried at this point unfortunately.
Any and all help, advice, and suggestions - even to "look at this type of function" would be appreciated (an actual solution would be baller! but I'm not above taking suggestions and working with them). I hope I got all the necessary information on this post to receive assistance.
Here's a SQL hack for you:
DECLARE
#UserId INT, #NameChange VARCHAR(50), #TransferDept smallint, #TransferJob VARCHAR(8), #TerminationDate DATE
SET #UserId = 1;
SELECT
#NameChange = CASE WHEN #NameChange IS NULL THEN NameChange ELSE #NameChange END,
#TransferDept = CASE WHEN #TransferDept IS NULL THEN TransferDept ELSE #TransferDept END,
#TransferJob = CASE WHEN #TransferJob IS NULL THEN TransferJob ELSE #TransferJob END,
#TerminationDate = CASE WHEN #TerminationDate IS NULL THEN TerminationDate ELSE #TerminationDate END
FROM #UserUpdateInfoMain ui
WHERE
ui.UserId = #UserId
ORDER BY
ui.Id DESC;
SELECT
#UserId AS UserId,
#NameChange AS NameChange,
#TransferDept AS TransferDept,
#TransferJob AS TransferJob,
#TerminationDate AS TerminationDate;
Returns
+--------+------------+--------------+-------------+-----------------+
| UserId | NameChange | TransferDept | TransferJob | TerminationDate |
+--------+------------+--------------+-------------+-----------------+
| 1 | Salsa3 | 9648 | K9487 | NULL |
+--------+------------+--------------+-------------+-----------------+

Alter ADD tempDB inside SELECT?

How can I alter tempdb inside a select?
I want a single query is that possible?
SELECT cust_ac_no, ord_no, ref_no, net_svc_id, job_type, ord_status, ord_status_date, ord_crt_date
INTO tempdb..xtiankwiksetreport
ALTER table tempdb..xtiankwiksetreport
ADD serial_no varchar(25) null,
msisdn varchar(25) null,
imsi varchar(25) null,
bts_id varchar(25) null
FROM wo_order
WHERE job_type IN ('EXTR', 'EXTC')
AND svc_type='4G'
AND ref_no=2
AND ord_status IN ('PL', 'JL')
SELECT cust_ac_no, ord_no, ref_no, net_svc_id, job_type, ord_status, ord_status_date, ord_crt_date,
serial_no=convert(varchar(25), null), msisdn-convert(varchar(25), null), imsi=convert(varchar(25), null), bts_id=convert(varchar(25), null)
INTO tempdb..xtiankwiksetreport
FROM wo_order
WHERE job_type IN ('EXTR', 'EXTC')
AND svc_type='4G'
AND ref_no=2
AND ord_status IN ('JL', 'PL')
I have started to test this in Sybase before you added the sql-server tag so I will provide both, the first one explain the idea, the second will prove this is possible in both DBMS.
Note that the solution is working but I would not use this as a long term solution. I didn't research on the possibilities of this to be a documented behavior, and could fail in the future (or already, based on my database version)
Sybase
Just use a convert function on a null value to add the empty columns.
select 'a' as foo,
convert(varchar(25), null) as serial_no
into #temp
Then to test it, I insert a long value :
insert into #temp values ('b', 'abcdefghijklmnopqrstuvwxyz')
And the result :
select * from #temp
foo,serial_no
'a',
'b','abcdefghijklmnopqrstuvwxy'
The 26th character is missing, we have a varchar(25)
SQL-Server
The same code will provide an error due to the truncated value
select 'a' as foo, convert(varchar(25), null) as serial_no
into temp_foo
insert into temp_foo values ('b', 'abcdefghijklmnopqrstuvwxyz')
insert into temp_foo values ('c', 'abcdefghijklmnopqrstuvwxy')
select * from temp_foo
The line with b provide a value to long and gives me :
Msg 8152, Level 16, State 14, Line 4
String or binary data would be truncated.
The statement has been terminated.
But the line with c with 25 character fits.
foo,serial_no
'a',
'c','abcdefghijklmnopqrstuvwxy'
SELECT
cust_ac_no, ord_no, ref_no, net_svc_id, job_type, ord_status, ord_status_date, ord_crt_date,
convert(varchar(25), null) as serial_no,
convert(varchar(25), null) as msisdn,
convert(varchar(25), null) as imsi,
convert(varchar(25), null) as bts_id,
INTO tempdb..xtiankwiksetreport
FROM wo_order
WHERE job_type IN ('EXTR', 'EXTC')
AND svc_type='4G'
AND ref_no=2
AND ord_status IN ('PL', 'JL')

Insert default value and extract value from other into new table in SQL server? [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I have a TimeSheet table and want to insert default values when pageload in asp.net by inserting ID from Employee table and current date, default value 1, but I get an error:
Msg 102, Level 15, State 1, Line 1
Incorrect syntax near ','.
Code:
create PROCEDURE [dbo].[sp_TimeSheet_Init]
#EmpID int,
#TimeCheckDate datetime,
#Day1 int,#Day2 int,#Day3 int,#Day4 int,#Day5 int,
#Day6 int,#Day7 int,#Day8 int,#Day9 int,#Day10 int,
#Day11 int,#Day12 int,#Day13 int,#Day14 int,#Day15 int,
#Day16 int,#Day17 int,#Day18 int,#Day19 int,#Day20 int,
#Day21 int,#Day22 int,#Day23 int,#Day24 int,#Day25 int,
#Day26 int,#Day27 int,#Day28 int,#Day29 int,#Day30 int,
#Day31 int
AS
INSERT INTO [TimeSheet]([EmpID], [TimeCheckDate], [Day1], [Day2], [Day3], [Day4], [Day5], [Day6], [Day7], [Day8], [Day9], [Day10], [Day11],[Day12], [Day13], [Day14], [Day15], [Day16], [Day17], [Day18], [Day19], [Day20],[Day21], [Day22], [Day23], [Day24], [Day25], [Day26], [Day27], [Day28], [Day29],[Day30], [Day31])
SELECT
Id
FROM
Employee
WHERE
WorkingStatusId = '1', getdate(), 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
This query is OK:
select Id
from Employee
where WorkingStatusId = '1'
Basically, you should separate
the basic information for a month into the Timesheet - this holds the EmployeeID, the month and year and whatever other information you need for a full month
the daily information into a separate table TimesheetDay that holds the information for each day; things like the day number (1-31), the value (number of hours worked) etc. - and it's linked to a Timesheet entry for a given month
DO NOT duplicate any data! You DO NOT need the EmployeeID on every TimesheetDay entry - the linked Timesheet row already has that information.
This is basic database design 101 - your tables should look something like this:
and this would be the script to create those two tables and their constraints:
CREATE TABLE [dbo].[Timesheet]
(
[TimeSheetID] [int] NOT NULL,
[EmployeeID] [int] NOT NULL,
[TimesheetMonth] [int] NOT NULL,
[TimesheetYear] [int] NOT NULL,
CONSTRAINT [PK_Timesheet]
PRIMARY KEY CLUSTERED ([TimeSheetID] ASC)
)
CREATE TABLE [dbo].[TimesheetDay]
(
[TimesheetDayID] [int] NOT NULL,
[TimesheetID] [int] NOT NULL,
[DayNumber] [int] NOT NULL,
[DayValue] [decimal](15, 2) NOT NULL,
CONSTRAINT [PK_TimesheetDay]
PRIMARY KEY CLUSTERED ([TimesheetDayID] ASC)
)
GO
ALTER TABLE [dbo].[TimesheetDay]
ADD CONSTRAINT [DF_TimesheetDay_DayValue]
DEFAULT ((8.0)) FOR [DayValue]
ALTER TABLE [dbo].[TimesheetDay] WITH CHECK
ADD CONSTRAINT [FK_TimesheetDay_Timesheet]
FOREIGN KEY([TimesheetID])
REFERENCES [dbo].[Timesheet] ([TimeSheetID])
ALTER TABLE [dbo].[TimesheetDay]
CHECK CONSTRAINT [FK_TimesheetDay_Timesheet]
GO
create PROCEDURE [dbo].[sp_TimeSheet_Init]
#EmpID int,
#TimeCheckDate datetime,
#Day1 int,#Day2 int,#Day3 int,#Day4 int,#Day5 int,
#Day6 int,#Day7 int,#Day8 int,#Day9 int,#Day10 int,
#Day11 int,#Day12 int,#Day13 int,#Day14 int,#Day15 int,
#Day16 int,#Day17 int,#Day18 int,#Day19 int,#Day20 int,
#Day21 int,#Day22 int,#Day23 int,#Day24 int,#Day25 int,
#Day26 int,#Day27 int,#Day28 int,#Day29 int,#Day30 int,
#Day31 int
AS
INSERT INTO [TimeSheet]([EmpID],[TimeCheckDate],[Day1],[Day2],[Day3],[Day4],[Day5],[Day6],[Day7],
[Day8],[Day9],[Day10],[Day11],[Day12],[Day13],[Day14],[Day15],
[Day16],[Day17],[Day18],[Day19],[Day20],[Day21],[Day22],[Day23],
[Day24],[Day25],[Day26],[Day27],[Day28],[Day29],[Day30],[Day31])
select Id ,getdate(),1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
from Employee where WorkingStatusId='1'

Why would an Insert statement always fire no matter the results of the preceeding IF statement?

I am using a SQL Server 2005 database, and I have an If statement that is behaving in two different manners against the same data set, depending on the actions to be taken in the begin...end block.
First, if I merely want to print to the console, the following code prints nothing, as expected:
if (not exists(select null from tblControls where Name = 'SOME_CONTROL_NAME'))
begin
print 'control not found'
end
Whereas this code prints 'control found' as expected:
if (exists(select null from tblControls where Name = 'SOME_CONTROL_NAME'))
begin
print 'control found'
end
However, if I change the code to this:
if (not exists(select null from tblControls where Name = 'SOME_CONTROL_NAME'))
begin
insert into tblControls values (632, 'NEW_CONTROL_NAME', 'New Control', 1, 1, NULL, 1, 'DataControls.CheckBox', NULL, NULL, 1)
end
The insert statement ALWAYS fires, even though a matching record exists in tblControls. Is there something special about insert statements in T-SQL 2005 that could be causing this behavior, or am I missing something obvious? I would understand if the logic of the code is wrong, but when I tested using the print statement it works as expected.
EDIT: Its generating an "Insert Error" message.
Any help with this frustrating problem is greatly appreciated.
It's the NULL in the Exists..Select that's confusing it. Try:
if (not exists(select * from tblControls where Name = 'SOME_CONTROL_NAME'))
begin
insert into tblControls values (632, 'NEW_CONTROL_NAME', 'New Control', 1, 1, NULL, 1, 'DataControls.CheckBox', NULL, NULL, 1)
end
Also, it can be rewritten without the parentheses and Begin/End:
IF NOT EXISTS (SELECT * FROM tblControls WHERE Name = 'SOME_CONTROL_NAME')
INSERT INTO tblControls VALUES (632, 'NEW_CONTROL_NAME', 'New Control', 1, 1, NULL, 1, 'DataControls.CheckBox', NULL, NULL, 1)

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