How do you add Foreign Key Relationships? - sql

I'm working with an existing SQL 2005 database that was not implemented with FK relationships between tables. I tried to add the relationships with a database diagram and my application immediately blew up trying to edit or insert any data that is tied to the new FK.
dbo.person [person_id | firstname | lastname | dateofbirth]
dbo.campaign [campaign_id | campaign_description]
dbo.disposition [disposition_id | disposition_description]
dbo.person_campaigns [person_campaign_id | person_id | campaign_id | disposition_id]
The person_campaigns table is where a person, campaign, and disposition are tied together. Can you please provide the appropriate SQL syntax for adding the proper FK relationships between these entities?
EDIT
CREATE TABLE [dbo].[person_campaigns](
[person_campaigns_id] [int] IDENTITY(1,1) NOT NULL,
[person_id] [int] NOT NULL,
[d_campaign_id] [int] NOT NULL,
[d_physician_disposition_id] [int] NULL,
CONSTRAINT [PK_person_campaigns] PRIMARY KEY CLUSTERED
(
[person_campaigns_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]
GO
CREATE TABLE [dbo].[d_campaign](
[d_campaign_id] [int] IDENTITY(1,1) NOT NULL,
[name] [varchar](50) NULL,
[year] [int] NULL,
[isactive] [bit] NOT NULL,
CONSTRAINT [PK_d_campaign] PRIMARY KEY CLUSTERED
(
[d_campaign_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]
GO
SET ANSI_PADDING OFF
GO
ALTER TABLE [dbo].[d_campaign] ADD CONSTRAINT [DF_d_campaign_isactive] DEFAULT ((1)) FOR [isactive]
GO
CREATE TABLE [dbo].[d_disposition](
[d_disposition_id] [int] IDENTITY(1,1) NOT NULL,
[name] [varchar](50) NULL,
[isactive] [bit] NOT NULL,
CONSTRAINT [PK_d_disposition] PRIMARY KEY CLUSTERED
(
[d_disposition_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]
GO
SET ANSI_PADDING OFF
GO
ALTER TABLE [dbo].[d_disposition] ADD CONSTRAINT [DF_d_disposition_isactive] DEFAULT ((1)) FOR [isactive]
GO
CREATE TABLE [dbo].[person](
[person_id] [int] IDENTITY(1,1) NOT NULL,
[firstname] [varchar](30) NULL,
[lastname] [varchar](30) NULL,
[dateofbirth] [datetime] NULL
CONSTRAINT [PK__person__0BC6C43E] PRIMARY KEY CLUSTERED
(
[person_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]
GO

the easiest way to do it is through the database diagram editor; do them one at a time and save the diagram to affect the tables after each connection is made. If it "blows up" it is most likely because the tables contain foreign-key values that do not exist; you'll have to clean these up first.

If you have to add them after the table is created the syntax is
create table person (person_id int primary key
,firstname varchar(10)
, lastname varchar(10)
, dateofbirth varchar(10))
create table campaign (campaign_id int primary key
, campaign_description varchar(10))
create table disposition (disposition_id int primary key
,disposition_description varchar(10))
create table person_campaigns(person_campaign_id int
,person_id int, campaign_id int ,disposition_id int)
go
alter table person_campaigns add Constraint
fk_person_campaigns_person_id
Foreign Key (person_id) References person(person_id)
GO
alter table person_campaigns add Constraint
fk_person_campaigns_campaign_id
Foreign Key (campaign_id) References campaign(campaign_id)
GO
alter table person_campaigns add Constraint
fk_person_campaigns_disposition_id
Foreign Key (disposition_id) References disposition(disposition_id)
GO

Suppose I had two tables that should have had a foreign key but did not. The first thing to do is check to see if there will be a data problem if I set a foreign key.
something like the below code would get you the records in the child table that do not have a match in the parent table.
select t2.FKField, t2.PKfield from table2 t2
left join Table1 t1 on t2.fkfield = t1.pkfield
where t1.pkfield is null
Once you can see what is wrong with the existing data, then you need to create a way to fix it. The fix will vary depending on what data you have that has no relationship to the Parent table and what the tables represent. Suppose your parent table contained a VIN number for automobiles as the PK. If your child table contains the cars that were worked on by the shop, you would want to fix the issue by adding the nonexisting VINS to the primary table becasue you wouldn't want to lose the history of what was worked on. There are other structures where you might want to simply delete the records that don't match in child table because they are meaningless. In other circumstances you might want to update those records to some default value (perhaps a customer in the customer table called unknown). In still other circumstances, you might need to go to audit tables or backups to find the value of the PK that was deleted without the associated child records being deleted. The actual way to fix this problem is highly dependent on what the data is used for and how important it is to retain all historical records. Since you should never delete any record that might be related to a financial transaction for legal (and accounting) reasons, you need to be most careful with those.
After fixing all the data, then you run the code to create the FK constraint.

Since I don't have SQL Server on this PC and I don't memorize the syntax, the easiest thing to do is to create two new test tables, create TableA with an ID field, TableB with a field that is a FK of TableA.ID, and then script out TableB to see the ADD CONSTRAINT syntax. Do this using SQL Server Management Studio via a database diagram.
However, if you were able to successfully create the FKs in a data diagram, and you only can't add or update records, I believe something else is wrong. Script out the person_campaigns table and post the code.

Related

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.

T-SQL/SSIS - Tables locking after redesign with primary/foreign keys

I've managed to get myself into a bit of a pickle, I did a quick redesign of the tables used in a notifications system & have managed to make the things entirely useless.
Any attempt to use them (select/update/insert/delete/alter) results in a query that runs indefinitely. I believe that I've managed to accidentally misuse the CASCADE option to nearing epic proportions. Below are the three tables and associated keys that I've used
I have managed to SELECT form the tables by using the "WITH (NOLOCK)" hint, however my attempts to delete/alter the CONSTRAINTS on the TBL_NOTIFICATIONS table have met with no luck. Hopefully one of you will be able to see where I've gone wrong & chastise me properly.
http://i.imgur.com/s8YrFFn.png (Relationship structure, I don't have enough reputation in include images yet)
TBL_NOTIFICATIONS:
CREATE TABLE [dbo].[TBL_NOTIFICATIONS]
(
[NotificationID] INT IDENTITY(0,1) NOT NULL,
[ApplicationID] INT NULL,
[SubApplicationID] INT NULL,
[Title] NVARCHAR(50) NULL,
[ShortDesc] NVARCHAR(512) NULL,
[Link] NVARCHAR(100) NULL,
[RaisedBy] NVARCHAR(36) NULL,
[RaisedFor] NVARCHAR(36) NULL,
[Show] INT NULL,
[DateCreated] DATETIME NULL,
[DateToArchive] DATETIME NULL,
CONSTRAINT [PK_TBL_NOTIFICATIONS] PRIMARY KEY CLUSTERED
(
[NotificationID] 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].[TBL_NOTIFICATIONS]
WITH CHECK ADD CONSTRAINT [FK_TBL_NOTIFICATIONS_TBL_NOTIFICATIONS_APPLICATION_DESCRIPTIONS]
FOREIGN KEY( [ApplicationID] )
REFERENCES [dbo].[TBL_NOTIFICATIONS_APPLICATION_DESCRIPTIONS] ( [ApplicationID] )
ON UPDATE CASCADE
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[TBL_NOTIFICATIONS] CHECK CONSTRAINT [FK_TBL_NOTIFICATIONS_TBL_NOTIFICATIONS_APPLICATION_DESCRIPTIONS]
GO
ALTER TABLE [dbo].[TBL_NOTIFICATIONS]
WITH CHECK ADD CONSTRAINT [FK_TBL_NOTIFICATIONS_TBL_NOTIFICATIONS_SUBAPPLICATION_DESCRIPTIONS]
FOREIGN KEY( [SubApplicationID], [ApplicationID] )
REFERENCES [dbo].[TBL_NOTIFICATIONS_SUBAPPLICATION_DESCRIPTIONS] ( [SubApplicationID], [ApplicationID] )
ON UPDATE CASCADE
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[TBL_NOTIFICATIONS] CHECK CONSTRAINT [FK_TBL_NOTIFICATIONS_TBL_NOTIFICATIONS_SUBAPPLICATION_DESCRIPTIONS]
GO
TBL_NOTIFICATIONS_APPLICATION_DESCRIPTIONS:
CREATE TABLE [dbo].[TBL_NOTIFICATIONS_APPLICATION_DESCRIPTIONS]
(
[ApplicationID] [int] NOT NULL,
[Description] [nvarchar](50) NULL,
CONSTRAINT [PK_TBL_NOTIFICATIONS_APPLICATION_DESCRIPTIONS] PRIMARY KEY CLUSTERED
(
[ApplicationID] 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
And finally...
TBL_NOTIFICATIONS_SUBAPPLICATION_DESCRIPTIONS:
CREATE TABLE [dbo].[TBL_NOTIFICATIONS_SUBAPPLICATION_DESCRIPTIONS]
(
[SubApplicationID] [int] NOT NULL,
[ApplicationID] [int] NOT NULL,
[Description] [nvarchar](50) NULL,
[ImagePath] [nvarchar](200) NULL,
CONSTRAINT [PK_TBL_NOTIFICATIONS_SUBAPPLICATION_DESCRIPTIONS] PRIMARY KEY CLUSTERED
(
[SubApplicationID] ASC,
[ApplicationID] 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
As I've mentioned, SELECT statements can be executed against the tables by using "WITH (NOLOCK)" however any attempt to modify them meets with infinitely loading queries. I've attempted to alter the UPDATE and DELETE CONSTRAINTS (to "NO ACTION" and "SET NULL") with no success. Likewise using ALTER TABLE with DROP CONSTRAINT does nothing but run forever.
I can view the constraints when querying "sys.foreign_keys" however I don't believe I can modify system tables directly, although I'd very much like being told otherwise on this point.
Does anyone have any ideas on either:
a) How to remove the constraints/keys on these tables, or
b) How to drop the tables without encountering another lock
Any and all ideas welcome, especially welcome would be a hint to how on earth I've managed to make these tables immune to my SQL-fu.
Thanks in advance
I don't have enough reputation to comment, so forgive me for posting this as an answer, maybe a Mod wants to move it?
Have you tried going to the "Activity Monitor" in SSMS and seeing if you have a blocked process (you can kill processes from there)? A stuck process holding a lock would cause the symptoms you are describing.
There is a nice thread on blocking here How to find what is locking my tables

How to create two or more Unique Columns in SQL Azure?

I want to select two columns in my table and make them unique but I don't know how to do it in SQL Azure database. As you can see in the image below, it doesn't show any option to modify the table properties, so everything is done using sql queries:
Here is the generated script of the table:
USE [mydbase]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[clientaccess](
[ID] [bigint] IDENTITY(1,1) NOT NULL,
[ModuleName] [nvarchar](50) NOT NULL,
[ClientAuthenticationId] [bigint] NOT NULL,
[HasAccess] [bit] NOT NULL,
CONSTRAINT [PK_clientaccess_ID] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
)
GO
ALTER TABLE [dbo].[clientaccess] WITH CHECK ADD CONSTRAINT [CAI_caID] FOREIGN KEY([ClientAuthenticationId])
REFERENCES [dbo].[clientauthentication] ([ID])
GO
ALTER TABLE [dbo].[clientaccess] CHECK CONSTRAINT [CAI_caID]
GO
This is the preview where I encountered the problem, it contains duplicate records:
Hope someone understand my explanation.
Sometimes GUIS have limitations (or not but you haven't discovered yet how all functionalities work). You can always add a unique constraint with ALTER TABLE:
ALTER TABLE [dbo].[clientaccess]
ADD CONSTRAINT Module_Client_UQ --- choose a name
UNIQUE (ModuleName, ClientAuthenticationId) ;

Should I use the inserted table in this trigger to set a default value for a nullable foreign key?

I have a table with a nullable FK field. I made it nullablle to avoid changing the loader.
In order to achieve (Advertisers)* 1(Currencies), I wrote a simple trigger:
ALTER TRIGGER InsertedAdvertisersDefaultCurrency
ON dbo.Advertisers
FOR INSERT
AS
UPDATE Advertisers
SET Currency_Id=(SELECT TOP 1 Id FROM Currencies WHERE Name='USD')
WHERE Currency_Id=NULL
My question is basically about how I am checking the whole table for null rows on every insert.
I feel like I should be using the inserted table (?)
Here's the table:
CREATE TABLE [dbo].[Advertisers](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](max) NOT NULL,
[Currency_Id] [int] NULL,
CONSTRAINT [PK_Advertisers] PRIMARY KEY CLUSTERED
(
[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]
GO
ALTER TABLE [dbo].[Advertisers] WITH CHECK ADD CONSTRAINT [FK_CurrencyAdvertiser1] FOREIGN KEY([Currency_Id])
REFERENCES [dbo].[Currencies] ([Id])
GO
ALTER TABLE [dbo].[Advertisers] CHECK CONSTRAINT [FK_CurrencyAdvertiser1]
GO
Yes, you should just update the insertd records:
UPDATE Advertisers
SET Currency_Id=(SELECT TOP 1 Id FROM Currencies WHERE Name='USD')
WHERE Id in (select Id from inserted)
Or you may use a default value in that field
ALTER TABLE [dbo].[Advertisers] ADD CONSTRAINT [DF_Advertisers_Currency_Id] DEFAULT ((101)) FOR [Currency_Id]
GO
Where 101 is the USD id