I was able to recreate this problem.
CREATE DATABASE Archive
ON
PRIMARY ( NAME = Arch1,
FILENAME = 'c:\data\archdat1.mdf'),
FILEGROUP FileStreamGroup1 CONTAINS FILESTREAM( NAME = Arch3,
FILENAME = 'c:\data\filestream1')
LOG ON ( NAME = Archlog1,
FILENAME = 'c:\data\archlog1.ldf')
GO
CREATE TABLE Archive.dbo.Records
(
[Id] [uniqueidentifier] ROWGUIDCOL NOT NULL UNIQUE,
[Chart] VARBINARY(MAX) FILESTREAM NULL
)
GO
ALTER TABLE Archive.dbo.Records WITH NOCHECK
ADD CONSTRAINT testfilestream UNIQUE (ID)
GO
ALTER TABLE Archive.dbo.Records
DROP CONSTRAINT testfilestream
Best I can tell this is a bug and (1) SQL Server has a hard rule relating to tables with FILESTREAM columns that says "if they try to drop ANY UNIQUE constraint on the ROWGUIDCOL column in a table with FILESTREAM, don't let them" and/or (2) There only ever is one UNIQUE constraint on a column and making another is like making an alias. So when you try and drop one, you're really dropping both.
As to how to solve it. Unfortunately, you can't add a second ROWGUIDCOL type column to a table, so that prevents a fix that's internal to the existing table. You'll probably have to create a new table with the same schema, (minus the extra unique constraint), copy the data from the current table to the new table, then drop the old table and rename the new one back to the old name.
I have modified ypercube's solution a little bit, so that both source and destination are not null, as is guaranteed by the original design. My CHECK
constraints are commented out, as they apparently will not work in MySQL. I kept them as comments because they document my intent, and they will work on other RDBMS.
CREATE TABLE PointType
( PointTypeID tinyint unsigned NOT NULL,
TypeDescription CHAR(20) NOT NULL,
PRIMARY KEY (PointTypeID),
UNIQUE (TypeDescription)
) ;
INSERT INTO PointType
(PointTypeID, TypeDescription)
VALUES
(1, 'Source'),
(2, 'Destination') ;
CREATE TABLE PointUsageQuota
( RouteID int unsigned NOT NULL,
PointTypeID tinyint unsigned NOT NULL,
PointID smallint(5) unsigned NOT NULL,
PRIMARY KEY (PointID), -- and this is what all the fuss is about
UNIQUE (RouteID , PointID, PointTypeID), -- target for the foreign keys
FOREIGN KEY (PointTypeID)
REFERENCES PointType (PointTypeID)
) ;
CREATE TABLE Route
( RouteID int unsigned NOT NULL,
SourcePointID smallint(5) unsigned NOT NULL,
SourceTypeID tinyint unsigned NOT NULL,
-- CHECK(SourceTypeID = 1),
FOREIGN KEY (RouteID , SourcePointID, SourceTypeID)
REFERENCES PointUsageQuota (RouteID , PointID, PointTypeID) ,
DestinationPointID smallint(5) unsigned NOT NULL,
DestinationTypeID tinyint unsigned NOT NULL,
-- CHECK(DestinationTypeID = 2),
FOREIGN KEY (RouteID , DestinationPointID, DestinationTypeID)
REFERENCES PointUsageQuota (RouteID , PointID, PointTypeID) ,
-- other fields
PRIMARY KEY (RouteID)
) ;
Testing:
insert into PointUsageQuota
values(1,1,666),(1,2,354);
INSERT INTO Route VALUES (1, 666, 1, 354, 2);
-- this fails:
INSERT INTO Route VALUES (2, 666, 1, 354, 2);
-- this fails too:
INSERT INTO Route VALUES (2, 354, 1, 666, 2);
Best Answer
This isn't possible with the proposed table structure declaratively. You would need triggers to enforce this.
A unique index on both columns, together with a pair of check constraints with scalar UDFs, gets quite close however.
The reason for
READCOMMITTEDLOCK
is to avoid problems with snapshot isolation.One problem with the approach above is that because the constraints are evaluated RBAR it can fail some transactions that ought to succeed.
For the example data
This statement fails
even though at the end of the transaction the constraints would have been met. But maybe it is sufficiently unlikely that you will be performing this kind of update (swapping email addresses between both type and person) that this can be ignored.