Primary Key error when trying to create Foreign Key - sql

Running into a problem creating a Foreign Key after creating a Parent/Child table scheme with partitioning setup.
CREATE TABLE [dbo].[MessageHeader]
([MessageHeaderID] [int] IDENTITY(1,1) NOT NULL,
[MessageHeaderGlobalId] [uniqueidentifier] NULL,
[CreatedDateTime] [datetime] NOT NULL
)
GO
ALTER TABLE [dbo].[MessageHeader]
ADD CONSTRAINT [PC_MessageHeader_CreatedDateTime_1]
PRIMARY KEY CLUSTERED ([MessageHeaderID], [CreatedDateTime] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = OFF)
ON [PS_Monthly] ([CreatedDatetime])
CREATE TABLE [dbo].[MessageDataInfo]
([MessageDataInfoID] [int] IDENTITY(1,1) NOT NULL,
[MessageHeaderID] [int] NOT NULL,
[CreatedDateTime] [datetime] NOT NULL)
GO
ALTER TABLE [dbo].[MessageDataInfo]
ADD CONSTRAINT [PC__CreatedDateTime_1]
PRIMARY KEY CLUSTERED ([MessageDataInfoID], [CreatedDateTime])
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = OFF)
ON [PS_Monthly] ([CreatedDatetime])
GO
ALTER TABLE [dbo].[MessageDataInfo] WITH CHECK
ADD CONSTRAINT [FK_HeaderID]
FOREIGN KEY([MessageHeaderID])
REFERENCES [dbo].[MessageHeader] ([MessageHeaderID])
What happens is I get the error:
Msg 1776, Level 16, State 0, Line 1
There are no primary or candidate keys in the referenced table 'dbo.MessageHeader' that match the referencing column list in the foreign key 'FK_HeaderID'.
I am not sure why this is happening because that column is obviously the primary key! Any help is appreciated.

You are trying to create a foreign key only on part of Primary key of the parent table. You have to use whole primary key to create a foreign key.

Your foreign key is referencing a single column, but the primary key on MessageHeader is composite. You probably don't mean it to be, so remove CreatedDateTime from the statement that creates the primary key (the first ALTER TABLE one.)

Related

Multiple foreign key constraints to the same primary key

I have two tables:
Image -> ImageId, ImageBinary (Id is int and auto-incremented)
Customer -> Id, CardImageFront, CardImageBack
Where CardImageFront and CardImageBack are Ids referencing to the ImageId column. The customer will take two pictures of their card: one for the front and one for the back. Both of them will be inserted in the Image table with different ImageId. Now I need tried to add two foreign key constraints on the Customer table (one for CardImageFront and another for CardImageBack) referencing the ImageId column, it says:
Introducing FOREIGN KEY constraint on table 'Customer' may cause cycles or multiple cascade paths
Should only a single foreign key constraint is necessary in this case? Will deleting the CardImageFront from Image table will also delete the CardImageBack? Or is there something wrong with the design?
Code for the Image table:
CREATE TABLE [dbo].[Image](
[Id] [int] IDENTITY(1,1) NOT NULL,
[BinaryImage] [varbinary](8000) NOT NULL,
CONSTRAINT [PK_Image] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
Code for the Customer table:
CREATE TABLE [dbo].[Customer](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [varchar](100) NOT NULL,
[ImageFront] [int] NOT NULL,
[ImageBack] [int] NOT NULL,
PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
Code for FK constraints:
ALTER TABLE [dbo].[Customer] WITH CHECK ADD CONSTRAINT [FK_1] FOREIGN KEY([ImageFront])
REFERENCES [dbo].[Image] ([Id])
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[Customer] WITH CHECK ADD CONSTRAINT [FK_2] FOREIGN KEY([ImageBack])
REFERENCES [dbo].[Image] ([Id])
ON DELETE CASCADE
GO
Error:
Msg 1785, Level 16, State 0, Line 6
Introducing FOREIGN KEY constraint 'FK_2' on table 'Customer' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Msg 1750, Level 16, State 1, Line 6
Could not create constraint or index. See previous errors.

What is the right way to create relations between these tables in a SQL database?

I have three tables: offices, suboffices, and sales. Each office owns several suboffices, both offices and suboffices sell products. How should I design my sales table to store there office or suboffice where this sale was made?
I was thinking about having a compound foreign key made of office_id and suboffice_id (where suboffice_id may be null, in which case a sale was made in office)
Is it the right way to design a database?
I was also thinking about having two sales tables: for offices and suboffices. But in my opinion it makes things a bit harder..
UPDATE
Sales can be of different types, so they will need different tables. And there will be one table 'SALES' which will store type of a sale and where it was made
Your sub-offices sound the same as your offices from a properties point of view. Here is a diagram of what your table design could look like by having your Office table include a ParentOffice foreign key.
And here is the SQL to create those tables:
CREATE TABLE [dbo].[Office](
[OfficeId] [int] NOT NULL,
[ParentOfficeId] [int] NULL,
[MoreStuff] [nvarchar](50) NULL,
CONSTRAINT [PK_Office] PRIMARY KEY CLUSTERED
(
[OfficeId] 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].[Office] WITH CHECK ADD CONSTRAINT [FK_Office_Office] FOREIGN KEY([ParentOfficeId])
REFERENCES [dbo].[Office] ([OfficeId])
GO
ALTER TABLE [dbo].[Office] CHECK CONSTRAINT [FK_Office_Office]
GO
CREATE TABLE [dbo].[Product](
[ProductId] [int] NOT NULL,
[MoreStuff] [nvarchar](50) NULL,
CONSTRAINT [PK_Product] PRIMARY KEY CLUSTERED
(
[ProductId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
CREATE TABLE [dbo].[Sale](
[SaleId] [int] NOT NULL,
[ProducitId] [int] NOT NULL,
[OfficeId] [int] NOT NULL,
CONSTRAINT [PK_Sale] PRIMARY KEY CLUSTERED
(
[SaleId] 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].[Sale] WITH CHECK ADD CONSTRAINT [FK_Sale_Office] FOREIGN KEY([OfficeId])
REFERENCES [dbo].[Office] ([OfficeId])
GO
ALTER TABLE [dbo].[Sale] CHECK CONSTRAINT [FK_Sale_Office]
GO
ALTER TABLE [dbo].[Sale] WITH CHECK ADD CONSTRAINT [FK_Sale_Sale] FOREIGN KEY([ProducitId])
REFERENCES [dbo].[Product] ([ProductId])
GO
ALTER TABLE [dbo].[Sale] CHECK CONSTRAINT [FK_Sale_Sale]
GO
One way to go about this, assuming OFFICE and SUBOFFICE share many of the same attributes is to eliminate the SUBOFFICE table and add a PARENT_OFFICE_ID column to the OFFICE table. Under this design, the distinction between an office and a sub-office would be whether or not PARENT_OFFICE_ID is null. Then SALES can simply have an OFFICE_ID column that can reference either kind of office.

Creating one-to-one relation using just one primary key

In my job, checking the database diagrams I have found a One-to-one relation between two tables, but the relation is not between two primary keys, the relation is between a primary key in one table and other non primary key attribute in the other table. In the database diagrams appear as "One to One relation". I wonder how can I create this kind of relationship "One to One" using just one primary on one table and using a non primary key in the other table.
Here are the scripts for "create" that I found in the database
---------------To create table Agreement Documents--------------
CREATE TABLE [dbo].[AgreementDocuments](
[AgreementDocumentID] [int] IDENTITY(1,1) NOT NULL,
[AgreementID] [int] NOT NULL,
[Document] [varbinary](max) NOT NULL,
CONSTRAINT [PK_AgreementDocuments] PRIMARY KEY CLUSTERED
([AgreementDocumentID] ASC) WITH (PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
ALTER TABLE [dbo].[AgreementDocuments] WITH CHECK ADD CONSTRAINT
[FK_AgreementDocuments_Agreements] FOREIGN KEY([AgreementID])
REFERENCES [dbo].[Agreements] ([AgreementID])
GO
ALTER TABLE [dbo].[AgreementDocuments] CHECK CONSTRAINT
[FK_AgreementDocuments_Agreements]
GO
--------------------------To create table Agreements-----------------------
CREATE TABLE [dbo].[Agreements](
[AgreementID] [int] IDENTITY(1,1) NOT NULL,
[ContactID] [int] NOT NULL,
[ClientID] [int] NOT NULL,
CONSTRAINT [PK_Agreements] PRIMARY KEY CLUSTERED
([AgreementID] 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
If I run this two queries in a separate database, it creates two tables with "One to Many" relation. How is that possible?
You can make this a one-to-one relationship by adding a UNIQUE CONSTRAINT to the AgreementID field:
ALTER TABLE dbo.AgreementDocuments
ADD CONSTRAINT uq_AgreementDocuments_AgreementId UNIQUE (AgreementId)
With the unique constraint, it will force it to be a one-to-one, rather than a one-to-many:
Before Constraint:
After Constraint:
Create table one with an identifying ID column.
AgreementDocumentID is identity and distinct
In the other table do a foreign key to the first table AgreementDocumentID field. And then put a distinct index on the second table for AgreementDocumentID.
If I understand your question.
No Er-Digram tool works 100% you have to edit what they create to make it right. Depending on the tool quality your amount of edits change.

How to set hierarchic foreign key through another table?

I am not sure if my title really explains the question, so I'll try an example:
Let's say I have:
1) region table: parent regions and subregions in the same table (link A)
2) product table: each product is linked to a single parent region (link B)
3) product_price table: lists the price of a product (C) in all sub regions of that product region (link D >>> the link in question).
(the diagram is showing only the relevant fields, there are a lot more data in region and product)
Is there a way to define the region->region_price key, to include only sub_region_id's of the parent_region in the product->region key??
Or, in the terms of the image, how do I make the D link to include only regions that are children [as in A] of the B & C link?
Hope you are getting my point...
Here are the real tables and links:
CREATE TABLE [dbo].[product](
[product_id] [int] NOT NULL,
[product_name] [nchar](10) NOT NULL,
[parent_region_id] [int] NOT NULL,
CONSTRAINT [PK_product] PRIMARY KEY CLUSTERED
(
[product_id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
CREATE TABLE [dbo].[product_price](
[product_id] [int] NOT NULL,
[sub_region_id] [int] NOT NULL,
[price] [decimal](18, 0) NOT NULL,
CONSTRAINT [PK_product_price] PRIMARY KEY CLUSTERED
(
[product_id] ASC,
[sub_region_id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
CREATE TABLE [dbo].[region](
[region_id] [int] NOT NULL,
[region_name] [nvarchar](50) NOT NULL,
[parent_region_id] [int] NULL,
CONSTRAINT [PK_region] PRIMARY KEY CLUSTERED
(
[region_id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
ALTER TABLE [dbo].[product] WITH CHECK ADD CONSTRAINT [FK_product_region] FOREIGN KEY([parent_region_id])
REFERENCES [dbo].[region] ([region_id])
ALTER TABLE [dbo].[product] CHECK CONSTRAINT [FK_product_region]
ALTER TABLE [dbo].[product_price] WITH CHECK ADD CONSTRAINT [FK_product_price_product] FOREIGN KEY([product_id])
REFERENCES [dbo].[product] ([product_id])
ALTER TABLE [dbo].[product_price] CHECK CONSTRAINT [FK_product_price_product]
ALTER TABLE [dbo].[product_price] WITH CHECK ADD CONSTRAINT [FK_product_price_region] FOREIGN KEY([sub_region_id])
REFERENCES [dbo].[region] ([region_id])
ALTER TABLE [dbo].[product_price] CHECK CONSTRAINT [FK_product_price_region]
ALTER TABLE [dbo].[region] WITH CHECK ADD CONSTRAINT [HK_region_region] FOREIGN KEY([parent_region_id])
REFERENCES [dbo].[region] ([region_id])
ALTER TABLE [dbo].[region] CHECK CONSTRAINT [HK_region_region]
I guess you are trying to enforce a business rule with a database rule, but they're not always the same. Instead, you can run validation querys in your code before inserting or updating, or you could implement a trigger that validates your rules or throws an error.

Are foreign key constraints enforced on DELETE when deleting the child?

I want to delete rows on a child table. I receive the error
The DELETE statement conflicted with the REFERENCE constraint
"FK_Address_UserDataSet". The conflict occurred in
database "XYZ", table "dbo.Address", column
'DataSetId'. The statement has been terminated.
I have a database structure with a parent UserDataSet and child Address table (where a parent can have any number of childs).
There is a foreign key constraint (mentioned in the error) that requires the child's DataSetId to relate to a valid UserDataSet.
Here are the table and constraint scripts, created with MS SQL Server Management Studio 2008 in simplified form:
CREATE TABLE [dbo].[Address](
[AddressId] [int] IDENTITY(1,1) NOT NULL,
[DataSetId] [int] NOT NULL,
--other fields
CONSTRAINT [PK_Address] PRIMARY KEY CLUSTERED
(
[AddressId] 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
---
CREATE TABLE [dbo].[UserDataSet](
[DataSetId] [int] IDENTITY(1,1) NOT NULL,
--other fields
CONSTRAINT [PK_UserDataSet] PRIMARY KEY CLUSTERED
(
[DataSetId] 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
---Create the constraint
ALTER TABLE [dbo].[Address] WITH NOCHECK ADD CONSTRAINT [FK_Address_UserDataSet] FOREIGN KEY([DataSetId])
REFERENCES [dbo].[UserDataSet] ([DataSetId])
GO
ALTER TABLE [dbo].[Address] CHECK CONSTRAINT [FK_Address_UserDataSet]
GO
But, how can deleting a child (not the parent) be a problem in this setup?
Can it be that the row to delete is currently invalid, probably added while the constraint was not (yet) in use), an the constraint now is enforced while deleting the child with an invalid foreign key?
Why are you adding the constraint with NOCHECK?
From MSDN documentation...
If you do not want to verify new CHECK or FOREIGN KEY constraints
against existing data, use WITH NOCHECK. We do not recommend doing
this, except in rare cases. The new constraint will be evaluated in
all later data updates. Any constraint violations that are suppressed
by WITH NOCHECK when the constraint is added may cause future updates
to fail if they update rows with data that does not comply with the
constraint.