Complex foreign key - sql

I have a table similar to this:
CREATE TABLE [dbo].[Table1](
[Option_PK] [bigint] IDENTITY(1,1) NOT NULL,
[Option_1] [varchar](10) NULL,
[Option_2] [varchar](10) NULL,
[Option_3] [varchar](10) NULL)
What I am attempting to do, is add a table driven constraint which can effectively restrict valid entries on a per-column basis. For example, if I made a second table:
CREATE TABLE [dbo].[Table2](
[FK_Name] [varchar](10) NOT NULL,
[FK_Value] [varchar](10) NOT NULL)
I would then want to check that the value stored in Table1, column "Option_1", existed in Table2, column "FK_Value", where the value of "FK_Name" was "Option_1".
Is this possible with either a check or an FK?
** Edit to make the column datatypes match; I hand typed the example table declarations and typo'd, this wasn't relevant to the problem. I know how to do a FK, I don't know how to do an FK like what I'm describing.

Could you not just have 3 tables and three FK?
A FK needs to match types.
CREATE TABLE [dbo].[Option1]([FK_Value] [nchar](10) NOT NULL)
CONSTRAINT [PK_Option1] PRIMARY KEY CLUSTERED ([FK_Value] ASC)
ALTER TABLE [dbo].[Table1] WITH CHECK ADD CONSTRAINT [FK_Table1_Option1] FOREIGN KEY([Option_1])
REFERENCES [dbo].[Table2] ([FK_Value])
GO
Or you could have a column Option1 that defaults to a value of option1
I tried hard coding in a value for Option1 but would not go.
ALTER TABLE [dbo].[FKtest1] WITH CHECK ADD CONSTRAINT [FK_FKtest1_FKtest1] FOREIGN KEY([Option1],[ValueFK])
REFERENCES [dbo].[FKtest1FK] ([PKoption],[PKvalue])
GO

CREATE TABLE [dbo].[Table1](
[Option_PK] [bigint] IDENTITY(1,1) NOT NULL,
[Option_1] [varchar](10) NULL,
[Option_1_FK] [varchar](8) NOT NULL DEFAULT 'OPTION_1',
[Option_2] [varchar](10) NULL,
[Option_2_FK] [varchar](8) NOT NULL DEFAULT 'OPTION_2',
[Option_3] [varchar](10) NULL,
[Option_3_FK] [varchar](8) NOT NULL DEFAULT 'OPTION_3'
)
CREATE TABLE [dbo].[Options](
[FK_Name] [nchar](8) NOT NULL,
[FK_Value] [nchar](10) NOT NULL,
CONSTRAINT [PK_Option1] PRIMARY KEY CLUSTERED ([FK_Name], [FK_Value] ASC)
)
ALTER TABLE [dbo].[Table1] WITH CHECK ADD
CONSTRAINT [FK_Table1_Option1] FOREIGN KEY([Option_1], [Option_1_FK) REFERENCES [dbo].[Options] ([[FK_Value], [FK_Name])
CONSTRAINT [FK_Table1_Option2] FOREIGN KEY([Option_2], [Option_2_FK) REFERENCES [dbo].[Options] ([[FK_Value], [FK_Name])
CONSTRAINT [FK_Table1_Option3] FOREIGN KEY([Option_3], [Option_3_FK) REFERENCES [dbo].[Options] ([[FK_Value], [FK_Name])
GO
Which is untested, unnormalized and ugly. You should probably add constraints to ensure the value of Option_X_FK does not change. Actually, this being T-SQL, you might be able to use computed columns for that, but I'm not sure if including them in a foreign key is allowed.

Related

Add a referential constraint with a constant value

I have a table of locations:
CREATE TABLE [dbo].[loca_location] (
[loca_id] [int] NOT NULL IDENTITY,
[loca_name] [nvarchar](100) NOT NULL,
[loca_address_line_1] [nvarchar](100),
[loca_address_line_2] [nvarchar](100),
[loca_address_line_3] [nvarchar](100),
[loca_address_town] [nvarchar](100),
[loca_address_county] [nvarchar](100),
[loca_post_code] [nvarchar](12),
[loca_active] [bit] NOT NULL,
[loca_created] [datetimeoffset](0) NOT NULL,
[loca_created_by] [nvarchar](50) NOT NULL,
[loca_modified] [datetimeoffset](0) NOT NULL,
[loca_modified_by] [nvarchar](50) NOT NULL,
[loca_deleted] [datetimeoffset](0),
[loca_deleted_by] [nvarchar](50),
[loca_type] [char](1),
CONSTRAINT [PK_dbo.loca_store] PRIMARY KEY ([loca_id])
)
Now some locations may be my own, others may be customers. If customer, then loca_type will be C. If I own it, it will be L. If its a supplier, then it will be S.
Now a contract should always belong to a customer, so I want to make it so the loca_type = C is a constraint as well as the loca_id.
ALTER TABLE [dbo].[cont_contract] ADD CONSTRAINT [FK_dbo.cont_contract_dbo.loca_location_cust_id] FOREIGN KEY ([loca_id_customer]) REFERENCES [dbo].[loca_location] ([loca_id], `C`)
That doesn't work. Is it possible to do what I want?
This may work
1.Declare UNIQUE ([loca_id], [loca_type] ) on [loca_location] table. That is correct as any superset of PK is unique.
2.Add constant column to [cont_contract] table.
3.Create FK.
CREATE TABLE [dbo].[loca_location] (
[loca_id] [int] NOT NULL IDENTITY,
--..
[loca_type] [char](1) CHECK ([loca_type] IN ('A','B','C')), -- change as needed
CONSTRAINT [PK_dbo.loca_store] PRIMARY KEY ([loca_id])
)
ALTER TABLE [dbo].[loca_location] ADD CONSTRAINT [u1] UNIQUE ([loca_id],[loca_type]);
CREATE TABLE [dbo].[cont_contract] (
[id] [int] NOT NULL IDENTITY,
--..
[loca_id_customer] Int,
-- Constant column
[loca_type] [char](1) DEFAULT 'C' CHECK ([loca_type] ='C')
);
ALTER TABLE [dbo].[cont_contract] ADD CONSTRAINT [FK_dbo.cont_contract_dbo.loca_location_cust_id]
FOREIGN KEY ([loca_id_customer],[loca_type]) REFERENCES [dbo].[loca_location] ([loca_id], [loca_type]);

Update PK columns in table having clustered keys

I have a database with tables that have clustered primary keys. I believe the term I picked up on is use of a Natural Key. The frontend to the SQL database is programmed to affect changes to all related foreign key tables for the 3,000 selected values which I want to modify. It takes about 13 seconds per change. I have a need to do this in a much shorter timeframe if possible.
The reason for doing this is prep work to migrate to a new CMMS program.
I found reference for use of ON UPDATE CASCADE, but I am not certain this applies.
Among many references, I used the following:
https://social.technet.microsoft.com/Forums/sqlserver/it-IT/23294919-3e6a-4146-a70d-66fa155ed1b3/update-primary-key-column-in-sql-server?forum=transactsql
An example of 2 of the 15 tables having the same named [EQNUM] column follows. Table A is the table that is first modified using the frontend. I left out many columns for each table:
CREATE TABLE A
(
[EQNUM] [varchar](30) NOT NULL,
CONSTRAINT [PK_A] PRIMARY KEY CLUSTERED ([EQNUM] ASC)
)
GO
SET ANSI_PADDING OFF
GO
CREATE TABLE B
(
[EQNUM] [varchar](30) NOT NULL,
[ColA] [varchar](10) NOT NULL,[ColB] [datetime] NOT NULL,
[ColC] [varchar](30) NOT NULL, [ColD] [varchar](30) NOT NULL,
[ColE] [varchar](30) NOT NULL, [ColF] [varchar](30) NOT NULL,
[ColG] [varchar](11) NOT NULL,[ColH] [varchar](10) NOT NULL,
[ColI] [datetime] NOT NULL,[ColJ] [varchar](15) NOT NULL,
[ColK] [int] NULL,
CONSTRAINT [PK_B]
PRIMARY KEY CLUSTERED ([EQNUM] ASC,
[ColA] ASC,[ColB] ASC,[ColC] ASC,[ColD] ASC,[ColE] ASC,
[ColF] ASC,[ColG] ASC,[ColH] ASC,[ColI] ASC,[ColJ] ASC)
)
An example of 1 of 4 sets of UPDATE queries, for which I believe the added first and last ALTER TABLE lines would allow me to affect the update:
ALTER TABLE A NOCHECK CONSTRAINT [PK_EQUIP]
UPDATE A
SET [EQNUM] = REPLACE([EQNUM],'-B','-B0')
WHERE [EQNUM] LIKE '%-A[1-9][0-5][0-9]-%' OR
[EQNUM] LIKE '%-A[1-9][A-F][0-5][0-9]-%' OR
ALTER TABLE A CHECK CONSTRAINT [PK_EQUIP]
ALTER TABLE A NOCHECK CONSTRAINT [PK_B]
UPDATE B
SET [EQNUM] = REPLACE([EQNUM],'-B','-B0')
WHERE [EQNUM] LIKE '%-A[1-9][0-5][0-9]-%' OR
[EQNUM] LIKE '%-A[1-9][A-F][0-5][0-9]-%' OR
ALTER TABLE A CHECK CONSTRAINT [PK_B]
Is it this simple, or am I missing something? Is there a better way?

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])
)

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.