Composite Primary Key + Foreign Key - sql

I have a table which contains a list of Surveys (PK is ID)
CREATE TABLE [dbo].[SurveyMaster](
[ID] [nvarchar](4) NOT NULL,
[Title] [nvarchar](200) NULL,
[IsActive] [bit] NOT NULL,
And a table which contains a list of Variable Mappings.
CREATE TABLE [dbo].[VariableMappings](
[ParentSurvey_ID] [nvarchar](4) NOT NULL,
[ReportingMonth] [nvarchar](6) NOT NULL,
[VariableName] [nvarchar](400) NOT NULL,
[Value] [int] NOT NULL
My hope was to create a primary key for VariableMappings on ParentSurvey_ID, ReportingMonth and Variable Name to ensure a unique record.
At the same time though, I'd like a foreign key relationship between VariableMappings.ParentSurvey_ID and SurveyMaster.ID to ensure VariableMappings only contains relevant SurveyID's.
I've tried a couple of approaches in SQL Server but I don't believe I can create the FK due to the composite key being made up of 3 columns.
How can I go about achieving this?

Yes, you can:
CREATE TABLE [dbo].[VariableMappings](
[ParentSurvey_ID] [nvarchar](4) NOT NULL,
[ReportingMonth] [nvarchar](6) NOT NULL,
[VariableName] [nvarchar](400) NOT NULL,
[Value] [int] NOT NULL,
PRIMARY KEY (ParentSurvey_ID, ReportingMonth, VariableName),
FOREIGN KEY (ParentSurvey_ID)
REFERENCES dbo.SurveyMaster (ID)
) ;
Mostly irrelevant to your problem, but having a PRIMARY KEY that is so wide (410 nvarchars) is not the best idea.

Your primary key definition and foreign key definitions are orthogonal. You can declare your composite primary key and your single column foreign key without issue. I would suggest to you, though, that a surrogate primary key on VariableMappings is better choice vs. the composite key - specifically for foreign keys pointing at VariableMappings.

There is one primary key that does matter and it is that of SurveyMaster. As long as ID is the primary key there, you can reference it from any number of other tables, regardless of those tables' primary keys.
A foreign key requires that the referenced combination of columns is unique, and the uniqueness must be guaranteed, for example by a primary key constraint.

Related

Primary and foreign keys database design

I am creating SQL Server tables for a currency exchange system.
I have one table which is the following:
CREATE TABLE [CurrencyKeys]
(
[Key] [nchar](3) NOT NULL,
[Currency] [nvarchar](30) NOT NULL
CONSTRAINT [PK_Key] PRIMARY KEY ([Key])
);
How would I create the table for the exchange rates itself which references the keys from the CurrencyKeys table?
Currently I have the following:
CREATE TABLE [ExchangeRates]
(
[DateTime] [datetime]NOT NULL,
[FromCurrCode] [nchar](3) NOT NULL,
[ToCurrCode] [nchar] (3) NOT NULL,
[Rate] [money] NOT NULL
)
Would I need to create (FromCurrCode, ToCurrCode) as primary key as well?
If I understand you correctly, you need to define the foreign keys on the exchangeRates table.
You would define a foreign key for EACH column, like so:
alter table [ExchangeRates] add constraint fk_ExchangeRates_01 foreign key (fromCurrCode) references CurrencyKeys([Key]);
alter table [ExchangeRates] add constraint fk_ExchangeRates_02 foreign key (toCurrCode) references CurrencyKeys([Key]);
That will define foreign keys for your exchangeRates table.
Don't you just want to do this?
CREATE TABLE [ExchangeRates]
(
[DateTime] [datetime]NOT NULL,
[FromCurrCode] [nchar](3) NOT NULL,
[ToCurrCode] [nchar] (3) NOT NULL,
[Rate] [money] NOT NULL,
Foreign Key([FromCurrCode]) References [CurrencyKeys]([Key]),
Foreign Key([ToCurrCode]) References [CurrencyKeys]([Key])
)
Or are you trying to ask how to maintain primary key values for ExchangeRates table itself. Can you please make yourself clear?
It depends on whether your table could have several rows referring to the same (From,To) pair (to track different exchange rates on different dates)
If that is the case, then the PK could be (FromCurrCode, ToCurrCode, DateTime)
If you are only saving one/the last exchange rate, then (FromCurrCode, ToCurrCode) should be enough.
It appears that you are using natural keys in lieu of surrogate keys.
In order for the ExchangeRates table to use natural keys, you would need to create a composite primary key using FromCurrCode and ToCurrCode
CREATE TABLE [ExchangeRates]
(
[DateTime] [datetime]NOT NULL,
[FromCurrCode] [nchar](3) NOT NULL,
[ToCurrCode] [nchar] (3) NOT NULL,
[Rate] [money] NOT NULL
CONSTRAINT [PK_ExchangeRates] PRIMARY KEY ([FromCurrCode], [ToCurrCode])
)

Error Defining Foreign Key

I have the following two database tables defined:
CREATE TABLE [dbo].[Classrooms] (
[ID] INT IDENTITY (1, 1) NOT NULL,
[SystemAccount_ID] INT NOT NULL,
[ClassroomName] VARCHAR (30) NOT NULL,
CONSTRAINT [PK_Table] PRIMARY KEY CLUSTERED ([ID]),
CONSTRAINT [FK_Classrooms_SystemAccount] FOREIGN KEY ([SystemAccount_ID]) REFERENCES [dbo].[SystemAccounts] ([ID])
);
CREATE TABLE [dbo].[Students] (
[ID] INT IDENTITY (1, 1) NOT NULL,
[SystemAccount_ID] INT NOT NULL,
[Classroom_ID] INT NULL,
[PhotoID] INT NULL,
[FirstName] VARCHAR (20) NOT NULL,
[LastName] VARCHAR (40) NOT NULL,
[NewsTemplate] TINYINT NOT NULL,
CONSTRAINT [PK_Students] PRIMARY KEY CLUSTERED ([ID] ASC),
CONSTRAINT [FK_Students_Classrooms] FOREIGN KEY ([Classroom_ID]) REFERENCES [dbo].[Classrooms] ([ID]),
CONSTRAINT [FK_Students_SystemAccounts] FOREIGN KEY ([SystemAccount_ID]) REFERENCES [dbo].[SystemAccounts] ([ID])
);
Data model details:
Students belong to zero or one classroom via Classroom_ID FK
Students belong to one System Account via SystemAccount_ID FK
Classrooms belong to one System Account via SystemAccount_ID FK (implying a system account can have zero or more Classrooms)
What I'm attempting to do is enforce when students are added to a classroom (by setting the Classroom_ID key in the Students table) that the classroom belongs to the same system account as the student. I could easily enforce this at the business logic layer but then I'd be requiring every programmer to remember to do this. So ideally, I'd be able to do this at the data layer as a constraint.
I tried adding a FK constraint to the Students table:
CONSTRAINT [FK_Students_ToTable] FOREIGN KEY ([SystemAccount_ID]) REFERENCES [Classrooms]([SystemAccount_ID])
Which results in the following error compliments of SQL Server:
Update cannot proceed due to validation errors.
Please correct the following errors and try again.
SQL71516 :: The referenced table '[dbo].[Classrooms]' contains no primary or candidate keys that match the referencing column list in the foreign key. If the referenced column is a computed column, it should be persisted.
I've tried a few different things but my SQL mojo isn't powerful enough to hack past this one. Any help would be greatly appreciated.
Add a UNIQUE constraint on the combination of the two columns in Classrooms:
CREATE TABLE [dbo].[Classrooms] (
[ID] INT IDENTITY (1, 1) NOT NULL,
[SystemAccount_ID] INT NOT NULL,
[ClassroomName] VARCHAR (30) NOT NULL,
CONSTRAINT [PK_Table]
PRIMARY KEY CLUSTERED ([ID]),
CONSTRAINT [FK_Classrooms_SystemAccount]
FOREIGN KEY ([SystemAccount_ID])
REFERENCES [dbo].[SystemAccounts] ([ID]),
CONSTRAINT [UQ_Classrooms_ID_SystemAccount_ID]
UNIQUE ([SystemAccount_ID], [ID])
);
Then, in the Students table, combine the two FOREIGN KEY constraints into one, or in your case (because Classroom_ID isnullable), change the FK to Classroom to use the combination of the two columns:
CREATE TABLE [dbo].[Students] (
[ID] INT IDENTITY (1, 1) NOT NULL,
[SystemAccount_ID] INT NOT NULL,
[Classroom_ID] INT NULL,
[PhotoID] INT NULL,
[FirstName] VARCHAR (20) NOT NULL,
[LastName] VARCHAR (40) NOT NULL,
[NewsTemplate] TINYINT NOT NULL,
CONSTRAINT [PK_Students]
PRIMARY KEY CLUSTERED ([ID] ASC),
CONSTRAINT [FK_Students_Classrooms]
FOREIGN KEY ([SystemAccount_ID], [Classroom_ID])
REFERENCES [dbo].[Classrooms] ([SystemAccount_ID], [ID]),
CONSTRAINT [FK_Students_SystemAccounts] -- this wouldn't be needed if
FOREIGN KEY ([SystemAccount_ID]) -- Classrooms_ID was NOT NULL
REFERENCES [dbo].[SystemAccounts] ([ID])
);

Adding Composite Foreign Key

I am having a problem adding a foreign key constraint to SQL 2008 that is based on a composite primary key in another table. I have followed some directions based on a few posts on here, but haven't been able to get it to work.
I have two tables:
CREATE TABLE [Staging].[ActivityLog](
[ActivityLogId] [int] IDENTITY(1,1) NOT NULL,
...
[ActivityLogType] [varchar](10) NOT NULL,
[ActivityLogSubType] [varchar](10) NOT NULL,
CONSTRAINT [PK_ActivityLog] PRIMARY KEY CLUSTERED
(
[ActivityLogId] ASC
))
and
CREATE TABLE [Staging].[ActivityLogTypeSubType](
[ActivityLogType] [varchar](10) NOT NULL,
[ActivityLogSubType] [varchar](10) NOT NULL,
CONSTRAINT [PK_ActivityLogTypeSubType] PRIMARY KEY CLUSTERED
(
[ActivityLogType] ASC,
[ActivityLogSubType] ASC
))
GO
I am trying to add a foreign key like this:
ALTER TABLE Staging.ActivityLog
ADD CONSTRAINT FK_ActivityLog_ActivityLogTypeSubType
FOREIGN KEY(ActivityLogType, ActivityLogSubType)
REFERENCES Staging.ActivityLogTypeSubType(ActivityLogType, ActivityLogSubType)
I get this error:
The ALTER TABLE statement conflicted with the FOREIGN KEY constraint "FK_ActivityLog_ActivityLogTypeSubType". The conflict occurred in database "HMDB_DEV", table "Staging.ActivityLogTypeSubType".
I have verified that this FK doesn't already exist.
I apologize for the lengthy post. Any help would be greatly appreciated.
Thanks,
James
Have you verified data to match FK logic?

Primary key is foreign keyed to itself in a credible reference manual - How can this work?

I have a DB schema from The Data Model Resource Book, Vol. 1. In it is a table like this:
CREATE TABLE [dbo].[AccountingPeriod](
[AccountingPeriodID] [int] NOT NULL,
[RoleTypeID] [int] NOT NULL,
[PeriodTypeID] [int] NOT NULL,
[AcctgPeriodNum] [int] NOT NULL,
[FromDate] [smalldatetime] NOT NULL,
[ThruDate] [smalldatetime] NOT NULL,
[PartyID] [int] NOT NULL,
PRIMARY KEY CLUSTERED (
[AccountingPeriodID] ASC)
With a constraint defined as:
ALTER TABLE [dbo].[AccountingPeriod] WITH CHECK ADD FOREIGN KEY([AccountingPeriodID])
REFERENCES [dbo].[AccountingPeriod] ([AccountingPeriodID])
The AccountingPeriodID column has a self referencing foreign key that the text claims is a recursive reference column but I think it's an error. I think I need another column to properly store recursive references in this table. Can the author's method be implemented with the table definition supplied, why?
Yes, You need another column which will reference the primary key column of the table itself, for self referencing a table. Self referencing column really does not make any sense.

Specify a Composite Foreign Key

I'm trying to create a Foreign Key out of two values in two different tables, some pointers would be appreciated!
Below is the code I am using for this :
CREATE TABLE [dbo].[EmployeeUnsetZone](
[EmployeeID] [char](2) NULL,
[ZoneOfficeID] [int] NOT NULL,
[ZoneID] [char](4) NULL
CONSTRAINT EmployeeUnsetZone_EmployeeFK FOREIGN KEY([EmployeeID],[ZoneID])
REFERENCES [Employee]([ID]), [ZoneByOffice]([ID])
)
I'd use:
CREATE TABLE [dbo].[EmployeeUnsetZone](
[EmployeeID] [char](2) NULL,
[ZoneOfficeID] [int] NOT NULL,
[ZoneID] [char](4) NULL,
PRIMARY KEY ([EmployeeID], [ZoneID]),
CONSTRAINT fk_employeeid FOREIGN KEY([EmployeeID]) REFERENCES [Employee]([ID]),
CONSTRAINT fk_zoneid FOREIGN KEY([ZoneID]) REFERENCES [ZoneByOffice]([ID])
)
The primary key will stop duplicates, and also setup the clustered key for the two columns to make searching against better.