PRIMARY KEYs vs. UNIQUE Constraints - sql-server

In an Alexander Kuznetsov article, he presents the follow code snippet:
CREATE TABLE dbo.Vehicles(
ID INT NOT NULL,
[Type] VARCHAR(5) NOT NULL,
CONSTRAINT Vehicles_PK PRIMARY KEY(ID),
CONSTRAINT Vehicles_UNQ_ID_Type UNIQUE(ID, [Type]),
CONSTRAINT Vehicles_CHK_ValidTypes CHECK([Type] IN ('Car', 'Truck'))
);
This snippet raises a few questions for me.
Why is it necessary to include both ID and Type in the unique constraint? If just ID is unique, then the combination of the two columns will always be unique as well.
Also, I know how to set a primary key and specify if it unique in SSMS. But how would I specify a primary key on one column, and make a unique constraint on a combination of columns? Does this create two indexes?
This came up because I'm trying to implement similar code, which does not create a composite primary key, and I get the following error. So I'm trying to understand this code better.
The columns in table 'MyTable' do not match an existing primary key or UNIQUE constraint.
EDIT
I was able to get this working by simply creating a composite primary key in MyTable. The actual table definition is shown below. Again, this works. But it is not the same as the code quoted above. And I'm not sure if it would be better if I did it the other way.
CREATE TABLE [dbo].[MessageThread](
[Id] [int] IDENTITY(1,1) NOT NULL,
[MessageThreadType] [int] NOT NULL,
CONSTRAINT [PK_MessageThread_1] PRIMARY KEY CLUSTERED
(
[Id] ASC,
[MessageThreadType] 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].[MessageThread] WITH CHECK ADD CONSTRAINT [CK_MessageThread_ValidType] CHECK (([MessageThreadType]=(2) OR [MessageThreadType]=(1)))
GO
ALTER TABLE [dbo].[MessageThread] CHECK CONSTRAINT [CK_MessageThread_ValidType]
GO

1 : I am not sure of the specific purpose of the given schema. But note that a unique constraint can be applied for multiple reasons, most commonly: (a) to enforce uniqueness and (b) to provide the optimizer with more information to base decisions.
2 : A unique constraint does not create two indexes. It creates a single index with one of the columns as the leading key column. It enforces uniqueness on both. So a unique constraint on a,b could have:
a b
---- ----
1 1
1 2
2 1
2 2
Notice that neither of the columns enforce uniqueness individually. I am not a big fan of using the table designer in SSMS (it has tons of bugs and doesn't support all functionality) but here is how to do it:
a) right-click the grid and choose Indexes/Keys...
b) choose multiple columns using the [...] button in the Columns grid
c) change Type to Unique Key
d) change the Name if desired
Here's an example of a table that already has a primary key. I could add one or more unique indexes if I wanted to:

In my understanding, the reason for unique constraint on ID,[Type] is let detail tables to refer ID,[Type] as foreign key. Usually parent table is required to have unique constraint on columns used for foreign key. For instance, the table in the question can have 2 detail tables:
CREATE TABLE dbo.CARS(
....
vehicle_id INT NOT NULL,
[Type] VARCHAR(5) NOT NULL,
CONSTRAINT CAR_CHK_TYPE CHECK [Type]='Car',
CONSTRAINT CAR_FK_VEHICLE FOREIGN KEY (vehicle_id,[Type]) REFERENCES Vehincle(id,[Type]));
CREATE TABLE dbo.TRUCKS(
....
vehicle_id INT NOT NULL,
[Type] VARCHAR(5) NOT NULL,
CONSTRAINT CAR_CHK_TYPE CHECK [Type]='Truck',
CONSTRAINT CAR_FK_VEHICLE FOREIGN KEY (vehicle_id,[Type]) REFERENCES Vehincle(id,[Type]));
This way Cars will have details only about Car type, whereas TRUCKS only about Truck.
Such design is used to avoid polymorphic relationship, for instance
CREATE TABLE dbo.VEHICLE (
...,
ref_id INT NOT NULL,
-- PK of 'master' table
ref_name VARCHAR(20) NOT NULL,
-- here we put 'truck' or 'car', so we virtually have 2 parents;
-- in this case we cannot use FK constraint, the only thing that may
-- somehow enforce the logical constraint is writing a trigger
Update
Your updated table definition looks good to me. I guess the sample table was initially designed for Oracle and then ported to SQLServer. In Oracle, that unique constraint and primary key can use the same index, so there is no penalty for having both PK and Unique constraint.

Good question. Theoretically you're right; there is no reason, a record can always be uniquely identified by its PK and the unique constraint will always be satisfied as long as this is true. However, if ID and Type have some relationship outside the bounds of the data layer (maybe this table is the data model for an Enum?), then it's unlikely that there would be two different IDs with the same Type because the uniqueness of Type is enforced elsewhere. The constraint also sets up an index that includes both ID and Type, making the table relatively efficient to be queried by that combination of columns.
You set up a unique constraint using the "Manage Indexes and Keys" option. Yes, this will create an index and unique constraint for the primary key, and an index and unique constraint for the combination of PK and Type.

I suspect the reason for having both columns in the UNIQUE constraint is related to the error message you mentioned. SQL Server (in common with other SQL DBMSs) has a limitation that a FOREIGN KEY constraint can only reference exactly the set of columns defined by a uniqueness constraint. So if a FOREIGN KEY constraint references two columns then those two columns must have a uniqueness constraint on them - even if other constraints already guarantee uniqueness. This is a pointless limitation but it is part of standard SQL.
The following example is quite similar and explains why a composite foreign key and nested uniqueness constraints can be useful.
http://consultingblogs.emc.com/davidportas/archive/2007/01/08/Distributed-Keys-and-Disjoint-Subtypes.aspx

Here you go:
Cars and trucks have different attributes, so they do not belong in one table. This is why I have two tables, Cars and Trucks.
Yet cars and trucks share some attributes, such as VIN (vehicle idenification number). More to the point, VIN is unique. This is why I need a table Vehicles. A vehicle cannot be both a car and a truck, so I must make sure it is not possible to enter both (VIN=123456789, Type=Car) and (VIN=123456789, Type=Truck). This is why I have a PK on VIN only.
I must ensure that a vehicle cannot have corresponding rows in both Cars and Trucks tables. This is why I have Type column in Cars and Trucks, and this is why I want (VIN, Type) in child tables Cars and Trucks refer to the parent table Vehicles. The only reason why I need an additional unique constraint on (VIN, Type) is this: it is referred by FK constraints from child tables.
BTW, you could leave a comment on the blog - in that case sqlblog would send me a message. It is a coincidence that I noticed your question here; I was supposed to go skiing, only there is no snow.

Related

Creating in MS-SQL a primary key results in a dynamic index name format eg PK__dummy__3213E83F2E1BDC42

Hope for help because of the following problem. Assume we have a table
CREATE TABLE [dbo].[dummy](
[id] [char](36) NOT NULL,
[name] [varchar](50) NOT NULL
) ON [PRIMARY]
If I create a primary key like this (version 1)
ALTER TABLE dummy ADD CONSTRAINT PK_dummy PRIMARY KEY (ID);
I get a unique name. In this case PK_dummy.
But if I create a primary key like this (version 2)
ALTER TABLE dummy ADD PRIMARY KEY Clustered (ID);
The name changes with every recreation of this primary key.
The format is always PK__dummy__"a dynamic number"
What is the meaning of this number?
And how can I identify primary keys created with version 2 in a hugh database?
Thanks for hints.
What is the meaning of this number?
This depends on product version - it is either based on a unique id or generated randomly.
how can I identify primary keys created with version 2 in a huge database?
SELECT *
FROM sys.key_constraints
WHERE is_system_named = 1
If you don't define the name of a constraint, index, key, etc, SQL Server will give it a name. To ensure uniqueness across the database, it therefore will add "random" characters at the end.
If having a consistent name is important then define the name in your statement, as you did in the first statement.

Need Tips on Foreign Keys to Alternate Tables

I have a table called Message. Each message may be associated with an Invoice table or an Rfp table, but not both. I'm struggling with the best way to implement this:
One approach is for the Message table to have foreign keys to both Invoice and Rfp tables. One FK would be valid while the other must be NULL. (Single-Table Inheritance.) But this seems rather awkward. In addition to the unused column, I need to find ways to prevent cases where the FKs are either both used or both NULL. And this would need to be repeated for every single message.
Another approach is to create a joining table. In this case, my Message table would have an FK to the joining table, and the Invoice and Rfp tables would also have an FK to the joining table. However, the problem here is that, given a reference to the joining table, it is awkward to find the related Invoice or Rfp table because I wouldn't know which contains the FK. So, here, I need to resort to other steps to know how to find the related table, such as adding a column to signify which table is related, for which it is hard to create a constraint.
Finally, I could create two types of Message tables. This solves the problems described above, but this causes problems for our app because we have logic in places that needs to add messages without knowing what type they are. (We can only have an FK to the related table.)
Can anyone offer some tips here. None of these ways are ideal, but perhaps there are aspects I've failed to consider when choosing between the two. Or perhaps there is a better approach altogether.
In option 1, you can use a check constraint to ensure only one FK is set...
CREATE TABLE [dbo].[Rfp] (Id int IDENTITY(1,1) NOT NULL, PRIMARY KEY CLUSTERED (Id))
CREATE TABLE [dbo].[Invoice] (Id int IDENTITY(1,1) NOT NULL, PRIMARY KEY CLUSTERED (Id))
CREATE TABLE dbo.[Message] (Id int IDENTITY(1,1) NOT NULL, RfpId int, InvoiceId int,
PRIMARY KEY CLUSTERED (Id),
FOREIGN KEY (RfpId) REFERENCES [dbo].[Rfp] (Id),
FOREIGN KEY (InvoiceId) REFERENCES [dbo].[Invoice] (Id),
)
ALTER TABLE dbo.[Message]
ADD CONSTRAINT CK_FK CHECK ( (RfpId IS NULL AND InvoiceId IS NOT NULL)
OR (RfpId IS NOT NULL AND InvoiceId IS NULL));

Missing FK Relationship in Entity Framework Model

I had a lot of trouble implementing the technique described in an Alexander Kuznetsov article. Basically, the article describes a way to create a FK between one table and alternate tables, and still maintain full constraints on those relationship.
Here's part of Alexander's code:
CREATE TABLE dbo.Vehicles(
ID INT NOT NULL,
[Type] VARCHAR(5) NOT NULL,
CONSTRAINT Vehicles_PK PRIMARY KEY(ID),
CONSTRAINT Vehicles_UNQ_ID_Type UNIQUE(ID, [Type]),
CONSTRAINT Vehicles_CHK_ValidTypes CHECK([Type] IN ('Car', 'Truck'))
)
CREATE TABLE dbo.Cars(ID INT NOT NULL,
[Type] AS CAST('Car' AS VARCHAR(5)) PERSISTED,
OtherData VARCHAR(10) NULL,
CONSTRAINT Cars_PK PRIMARY KEY(ID),
CONSTRAINT Cars_FK_Vehicles FOREIGN KEY(ID, [Type])
REFERENCES dbo.Vehicles(ID, [Type])
)
I finally got it working after errors and confirmed bugs. But when I generate my EF models from the new schema, it is missing a relationship between two of my tables.
The problem is that, in order to have a FK on two columns, there must be an index or unique constraint on both those columns. However, in my case, I also have another table with a FK to a single column in the base table (Vehicles, in Alexander's code).
Since you cannot have more than one PK in a table, this means I cannot have a FK to a PK on both sides. The PK can be for one or two columns, and the other FK will need to reference the non-PK unique constraint.
Unfortunately, Entity Framework will only create relationships for you when there is a FK to a PK. That's the problem. Can someone who understand DB design better than I spot any other alternatives here?
Note: I realize some will see the obvious fix as simply modifying the model to manually add the additional relationship. Unfortunately, we are using a database project and are constantly using automated systems to regenerate the project and model from an updated database. So manual steps are really not practical.
You can't have more than one PK, but you can have more than one unique constraint, and in SQL Server you can create a foreign key constraint that references a unique constraint (one or multiple columns). Here is an example of two tables that roughly look like your model.
CREATE TABLE dbo.Vehicles
(
VehicleID INT PRIMARY KEY,
[Type] VARCHAR(5) NOT NULL UNIQUE,
CONSTRAINT u1 UNIQUE(VehicleID, [Type])
);
CREATE TABLE dbo.Cars
(
CarID INT PRIMARY KEY,
VehicleID INT NOT NULL
FOREIGN KEY REFERENCES dbo.Vehicles(VehicleID),
[Type] VARCHAR(5) NOT NULL
FOREIGN KEY REFERENCES dbo.Vehicles([Type]),
CONSTRAINT fk1 FOREIGN KEY (VehicleID, [Type])
REFERENCES dbo.Vehicles(VehicleID, [Type])
);
Note that Cars has three foreign keys: one points to the PK of vehicles (VehicleID), one points to the unique constraint on Vehicles([Type]), and one points to the multi-column unique constraint on Vehicles(VehicleID, [Type]). I realize this is not equivalent to what you are trying to do but should demonstrate that SQL Server, at least, is capable of doing everything you seem to want to do (I'm having a hard time concluding what you're actually because you keep swapping concepts between what Alex did, what you're trying to do but failing, and what you've done successfully).
Are you saying that EF will not recognize a foreign key that references a unique constraint? If so, does that affect constraints that have more than one column, or all unique constraints? If this is the case, that's a shame, because it is certainly supported in SQL Server. Seems like this would either be a bug or an intentional omission (given that the standard doesn't strictly allow FKs against unique constraints). I wonder if there are any bugs reported on Connect?
I have no idea how to force EF to recognize it, but I do know that just about all the people I know who use database projects end up performing pre- or post-deployment modifications and these can be relatively automated.

Relationships between tables

I have a table called objectives, each objective has zero to many cause-effect relationships with other objectives, these relationships I have to be stored in the database, let me know if there's a way to relate this table records.
There is not a way to relate the records without creating an additional table (you would need N-1 additional columns on your current table to model the N possible effects of a cause).
Creating an additional table like the one below should serve your purpose.
CREATE TABLE cause_effect (
cause integer NOT NULL,
effect integer NOT NULL,
CONSTRAINT cause_effect_pkey PRIMARY KEY (cause, effect),
CONSTRAINT cause_effect_cause_fkey FOREIGN KEY (cause)
REFERENCES yourtable (id),
CONSTRAINT cause_effect_effect_fkey FOREIGN KEY (effect)
REFERENCES yourtable (id)
)
Apply FKey behaviour as applies.

Incorrect value for UNIQUE_CONSTRAINT_NAME in REFERENTIAL_CONSTRAINTS

I am listing all FK constraints for a given table using INFORMATION_SCHEMA set of views with the following query:
SELECT X.UNIQUE_CONSTRAINT_NAME,
"C".*, "X".*
FROM "INFORMATION_SCHEMA"."KEY_COLUMN_USAGE" AS "C"
INNER JOIN "INFORMATION_SCHEMA"."REFERENTIAL_CONSTRAINTS" AS "X"
ON "C"."CONSTRAINT_NAME" = "X"."CONSTRAINT_NAME"
AND "C"."TABLE_NAME" = 'MY_TABLE'
AND "C"."TABLE_SCHEMA" = 'MY_SCHEMA'
Everything works perfectly well, but for one particular constraint the value of UNIQUE_CONSTRAINT_NAME column is wrong, and I need it in order to find additional information from the referenced Column. Basically, for most of the rows the UNIQUE_CONSTRAINT_NAME contains the name of the unique constraint (or PK) in the referenced table, but for one particular FK it is the name of some other unique constraint.
I dropped and re-created the FK - did not help.
My assumption is that the meta-data is somehow screwed. Is there a way to rebuild the meta data so that the INFORMATION_SCHEMA views would actually show the correct data?
edit-1: sample db structure
CREATE TABLE MY_PARENT_TABLE (
ID INTEGER,
NAME VARCHAR,
--//...
CONSTRAINT MY_PARENT_TABLE_PK PRIMARY KEY CLUSTERED (ID)
)
CREATE UNIQUE NONCLUSTERED INDEX MY_PARENT_TABLE_u_nci_ID_LongName ON MY_PARENT_TABLE (ID ASC) INCLUDE (SOME_OTHER_COLUMN)
CREATE TABLE MY_CHILD_TABLE (
ID INTEGER,
PID INTEGER,
NAME VARCHAR,
CONSTRAINT MY_CHILD_TABLE_PK PRIMARY KEY CLUSTERED (ID)
,CONSTRAINT MY_CHILD_TABLE__MY_PARENT_TABLE__FK
FOREIGN KEY (PID)
REFERENCES MY_PARENT_TABLE (ID)
ON UPDATE NO ACTION
ON DELETE NO ACTION
)
I expect the UNIQUE_CONSTRAINT_NAME to be MY_PARENT_TABLE_PK, but what I am
getting is MY_PARENT_TABLE_u_nci_ID_LongName.
Having looked at the structure, I see that in fact there are 2 UNIQUE constaints on that column - PK and the MY_PARENT_TABLE_u_nci_ID_LongName. So the real question should probably be: why does it take some other unique index and not the PK?
Since you have both a PK and a UNIQUE constraint on the same column, SQL Server picks one to use. I don't know if it picks the UNIQUE constraint because it is thinner (i.e. fewer columns involved) and might require fewer reads to confirm matches(?)
I don't see any way within SQL to enforce which one it chooses, other than ordering your scripts - create the table with the PK, create the other table and the FK, then create the UNIQUE constraint if you really need it - but is that really the case?

Resources