Issues with creating SQL Server table relationships - sql

I have 2 tables.
First table:
create table abc
(
AId INT IDENTITY(1,1),
Name NVARCHAR(50),
EMP NVARCHAR(50)
CONSTRAINT PK_abc PRIMARY KEY (AId, Name)
)
And second table as:
create table def
(
AId INT,
Comment NVARCHAR(50)
constraint FK_aid FOREIGN KEY (AId) references abc (AId)
)
This throws an error:
There are no primary or candidate keys in the referenced table 'abc' that match the referencing column list in the foreign key 'FK_aid'.
So I updated as:
create table def
(
AId INT,
Name NVARCHAR(50),
Comment NVARCHAR(50)
constraint FK_AID FOREIGN KEY (AId, Name) references abc (AId, Name)
)
But this throws another error:
More than one key specified in column level FOREIGN KEY constraint, table 'def'.
Not sure what am I missing here.
---Updated----
Sorry for giving vague example. I was having difficulty in explaining my problem here. I have attached the screenshot of my problem. This show the eventual output that I am expecting by making join to 3 tables. Where the data of 3 tables is populated from an input form.
I have tried to provide in a clear way. Let me know if you need more inputs.
Attchment at:
https://imgur.com/a/jfFS6
Thanks

You have an identity column. Use it as the primary key:
create table abc (
AId INT IDENTITY(1,1) PRIMARY KEY,
Name NVARCHAR(50),
EMP NVARCHAR(50)
);
create table def (
AId INT,
Comment NVARCHAR(50)
constraint FK_aid FOREIGN KEY (AId) references abc (AId)
);
Name should not be in the definition of the primary key.

Looks like you have to do it as a separate statement:
ALTER TABLE [dbo].[def] WITH CHECK ADD CONSTRAINT [FK_def_abc] FOREIGN KEY([AId], [Name])
REFERENCES [dbo].[abc] ([AId], [Name])
GO
ALTER TABLE [dbo].[def] CHECK CONSTRAINT [FK_def_abc]
GO
But you still should NOT do that. Identity is unique and it alone should be used as primary key.

Related

SQL Constraint names

If we have query for creating table like this..
create table if not exists food
(
id int not null auto_increment,
user_id int,
name varchar(30),
constraint pk_food primary key(id,name),
foreign key(user_id) references userss(id)
);
What does pk_food mean in this example? I know this is a constraint name, but for what we should be give a name for constraint, if its working without?
create table if not exists food
(
id int not null auto_increment,
user_id int,
name varchar(30),
primary key (id, name),
foreign key (user_id) references userss(id)
);
I mean.. how to use these names and for what we need it?
You gives constraints names for basically two reasons:
You can better understand the error message when the constraint is violated.
You can more easily find the constraint if you want to delete it.

Insert null value in an association table

I have a problem with something in SQL, let's see an example of database :
CREATE TABLE person( //Employee
pe_id PRIMARY KEY NOT NULL AUTO_INCREMENT,
pe_name VARCHAR(20),
pe_office VARCHAR(20)
);
CREATE TABLE project( //Mission
pr_id PRIMARY KEY NOT NULL AUTO_INCREMENT,
pr_name VARCHAR(20),
pr_status VARCHAR(15)
);
CREATE TABLE techno( //Programming language
te_id PRIMARY KEY NOT NULL AUTO_INCREMENT,
te_name VARCHAR(20)
);
CREATE TABLE job( //developer, manager, ...
jo_id PRIMARY KEY NOT NULL AUTO_INCREMENT,
jo_name VARCHAR(20)
);
I would like to assign persons on projects for a job using technos.
For example, Rob works as a developer and project manager on the projet #13 with AngularJS and HTML.
So I created this table :
CREATE TABLE assignment(
pe_id INT,
pr_id INT,
te_id INT,
jo_id INT,
as_days INT, //Days of work
PRIMARY KEY(pe_id, pr_id, tr_id, jo_id),
CONSTRAINT fk_as_pe_id FOREIGN KEY(pe_id) REFERENCES person(pe_id),
CONSTRAINT fk_as_pr_id FOREIGN KEY(pr_id) REFERENCES project(pr_id),
CONSTRAINT fk_as_te_id FOREIGN KEY(te_id) REFERENCES techno(te_id),
CONSTRAINT fk_as_jo_id FOREIGN KEY(jo_id) REFERENCES job(jo_id)
);
I would like to have the ability to assign a developer with somes technos to a project without knowing who exaclty, like this:
INSERT INTO assignment(pr_id,te_id,jo_id,as_days) VALUES(1,2,3,4); //No person!
We suppose that this values exists in project, techno and job tables.
But it seems that I can not insert this, probably because I do not define person's ID (which is in the primary key).
How can I do this ?
Hope I'm understandable :)
You solve this problem by not having this as a primary key. Primary keys cannot be NULL or, if they're composite primary keys, cannot contain NULL. Make it a unique index instead. Create an autonumber field for the primary key. I think this is better solution in your case
Primary Key:
Can be only one in a table
It never allows null values
Primary Key is unique key identifier and can not be null and must be unique.
Unique Key:
Can be more than one unique key in one table.
Unique key can have null values(only single null is allowed).
It can be a candidate key
Unique key can be null and may not be unique.
Maybe you should do this:
Before insert disable constraint:
ALTER INDEX fk_as_pe_id ON assignment
DISABLE;
After insert enable it:
ALTER INDEX fk_as_pe_id ON assignment
REBUILD;
Another alternate way is, if it is possible to alter table structure, just exclude pe_id from the composite primary key in assignment table

Check if data exists in another table on insert?

Table A
(
Table_A_ID int
)
Table B
(
Table_B_ID int
Value int
)
Say I want to insert data into Table B, where 'Value' would be the same as a Table_A_ID.
How would I make a constraint or check that the data actually exists in the table on insertion?
You probably need to enforce data integrity not only on INSERT into Table B, but also on UPDATE and DELETE in both tables.
Anyway options are:
FOREIGN KEY CONSTRAINT on Table B
TRIGGERs on both tables
As a last resort if for some reason 1 and 2 is not an option STORED PROCEDUREs for all insert, delete update operations for both tables
The preferred way to go in most cases is FOREIGN KEY CONSTRAINT.
Yap, I agree with #peterm.
Cause, if your both Table_A_ID and Table_B_Id are primary keys for both tables, then you don't even need two tables to store the value. Since, your two tables are seems to be on 'one-to-one' relationship. It's one of the database integrity issues.
I think you didn't do proper normalisation for this database.
Just suggesting a good idea!
I found this example which demonstrates how to setup a foreign key constraint.
Create employee table
CREATE TABLE employee (
id smallint(5) unsigned NOT NULL,
firstname varchar(30),
lastname varchar(30),
birthdate date,
PRIMARY KEY (id),
KEY idx_lastname (lastname)
) ENGINE=InnoDB;
Create borrowed table
CREATE TABLE borrowed (
ref int(10) unsigned NOT NULL auto_increment,
employeeid smallint(5) unsigned NOT NULL,
book varchar(50),
PRIMARY KEY (ref)
) ENGINE=InnoDB;
Add a constraint to borrowed table
ALTER TABLE borrowed
ADD CONSTRAINT FK_borrowed
FOREIGN KEY (employeeid) REFERENCES employee(id)
ON UPDATE CASCADE
ON DELETE CASCADE;
NOTE: This tells MySQL that we want to alter the borrowed table by adding a constraint called ‘FK_borrowed’. The employeeid column will reference the id column in the employee table – in other words, an employee must exist before they can borrow a book.
The final two lines are perhaps the most interesting. They state that if an employee ID is updated or an employee is deleted, the changes should be applied to the borrowed table.
NOTE: See the above URL for more details, this is just an excerpt from that article!
Create a foreign key constraint on the column 'Value' on table B that references the 'Table_A_ID' column.
Doing this will only allow values that exist in table A to be added into the 'Value' field of table B.
To accomplish this you first need to make Table_A_ID column the primary key for table A, or it at least has to have some sort of unique constraint applied to it to be a foreign key candidate.
BEGIN TRANSACTION -- REMOVE TRANSACTION AND ROLLBACK AFTER DONE TESTING
--PUT A PRIMARY KEY ON TABLE A
CREATE TABLE A
( Table_A_ID int CONSTRAINT PK_A_Table_A_ID PRIMARY KEY)
--ON VALUE ADD A FOREIGN KEY CONSTRAINT THAT REFERENCEs TABLE A
CREATE TABLE B
( Table_B_ID int,
[Value] int CONSTRAINT FK_B_Value_A REFERENCES A(Table_A_ID)
)
-- TEST VALID INSERT
INSERT A (Table_A_ID) VALUES (1)
INSERT B (Table_B_ID, [Value]) VALUES (1,1)
--NOT ALLOW TO INSERT A VALUE THAT DOES NOT EXIST IN A
--THIS WILL THROW A FOREIGN KEY CONSTRAINT ERROR
INSERT B (Table_B_ID, [Value]) VALUES (1,2) -- 2 DNE in table A
ROLLBACK
Note: there is no magic to 'FK_B_Value_A' or 'PK_A_Table_A_ID' it simply a naming convention and be called anything. The syntax on the foreign key and primary key lines work like this:
column-definition CONSTRAINT give-the-constraint-a-name REFERENCES table-name ( table-column )
column-definition CONSTRAINT give-the-constraint-a-name PRIMARY KEY

Differences between "foreign key" and "constraint foreign key"

I mean for example I can create table like
create table XTable
(
idt int not null primary key,
value nvarchar(50),
idq int,
constraint fk_idq foreign key(idq) references YTable(idq)
)
and I can create it like this
create table XTable
(
idt int not null primary key,
value nvarchar(50),
idq int,
foreign key(idq) references YTable(idq)
)
I usually create table like in the second example but now I'm curious about the first example. What is the difference?
The first one assigns a user-defined name to the foreign key, the second one will assign a system-generated name to the foreign key.
User-defined foreign key names can be useful for subsequent statements like these:
ALTER TABLE XTable DROP CONSTRAINT fk_idq;
ALTER TABLE XTable ENABLE CONSTRAINT fk_idq;
ALTER TABLE XTable DISABLE CONSTRAINT fk_idq;
It's harder to alter constraints with system-generated names, as you have to discover those names first.
The first option is purely for naming the constraint.
From SQL FOREIGN KEY Constraint
To allow naming of a FOREIGN KEY constraint, and for defining a FOREIGN KEY constraint on multiple columns, use the following SQL syntax
CREATE TABLE Orders
(
O_Id int NOT NULL,
OrderNo int NOT NULL,
P_Id int,
PRIMARY KEY (O_Id),
CONSTRAINT fk_PerOrders FOREIGN KEY (P_Id)
REFERENCES Persons(P_Id)
)
Also, from CREATE TABLE (Transact-SQL) one can see that [ CONSTRAINT constraint_name ] is optional.
Apart from controlling the name, nothing really. SQL Server will supply a name if you omit it. FYI, you only need this syntax (SQL Fiddle):
create table XTable
(
idt int not null primary key,
value nvarchar(50),
idq int references YTable(idq)
)
Here's a fuller example.

How to implement bidirectional referenced tables?

I have a table School and a table Teacher having a one-to-many relationship. However, one of the teachers is the school's principle, and only one teacher can be the school principle. So I thought of saving the teachers id (principle) in the School table as follows:
CREATE TABLE School (
ID INT PRIMARY KEY,
Name VARCHAR(40),
PrincipleID INT FOREIGN KEY REFERENCES Teacher.ID
)
CREATE TABLE Teacher (
ID INT PRIMARY KEY,
Name VARCHAR(40),
SchoolID INT FOREIGN KEY REFERENCES School.ID
)
I know I could loose the foreign key reference in the school table, but that's not an option.
Should I make the reference after the table creation? If yes, how?
Another solution is to create a new table, let's say SchoolsPrinciples with just two fields:
CREATE TABLE SchoolsPrinciples
(
SchoolId int,
TeacherId int,
CONSTRAINT uc_SchoolTeacher UNIQUE (SchoolId, TeacherId)
)
A UNIQUE constraint let you obtain exactly one teacher per each school.
When building the tables, you'll need to add the constraint as a separate alter statement. Note also that when creating foreign keys, you should only specify the table name, not the referenced column (the column is implied by the primary key).
CREATE TABLE School (
ID INT PRIMARY KEY,
Name VARCHAR(40),
PrincipleID INT);
CREATE TABLE Teacher (
ID INT PRIMARY KEY,
Name VARCHAR(40),
SchoolID INT
CONSTRAINT FK_Teacher_School
FOREIGN KEY REFERENCES School);
ALTER TABLE School add
CONSTRAINT FK_School_Teacher
FOREIGN KEY (PrincipleID) REFERENCES Teacher;
When you add data, you'll need to set the PrincipleID field as a separate update:
insert into School (ID, Name)
values (1, 'Blarg Elementary');
insert into Teacher (ID, Name, SchoolID)
values (1, 'John Doe', 1),
(2, 'Bob Smith', 1),
(3, 'Adam Walker', 1);
update School set PrincipleID = 2 where ID = 1;
Put a boolean IsPrincipal on the Teacher table instead. Or add a third relationship table
CREATE TABLE SchoolPrincipals (
INT SchoolID PRIMARY KEY FOREIGN KEY REFERENCES School.ID,
INT TeacherID FOREIGN KEY REFERENCES Teacher.ID
)
Keeps everything tidy without painful delete logic.
You can take a column in Teacher table as
IsPrincipal where only one row will have value as true as referred
by jonnyGold,
This can be checked by triggers.
OR
You can use filtered index if using Sql Server 2008.
Create unique filtered index where SchoolID, IsPrincipal
is NOT NULL and are unique
Boss where this will contain ID of principal hence creating employee manager relationship which in your case is not suitable.
CREATE TABLE EmpManager
(
TeacherID int
SchoolID int
IsPrincipal bit
)
And use filtered index or trigger to handle the scenario.
EDIT:
CREATE TABLE [dbo].[Teacher](
[ID] [int] NOT NULL primary key,
[Name] [varchar](40) NULL,
[SchoolID] [int] NULL,
)
GO
CREATE TABLE [dbo].[School](
[ID] [int] NOT NULL primary key,
[Name] [varchar](40) NULL,
[PrincipleID] [int] NULL,
)
GO
ALTER TABLE [dbo].[Teacher] WITH CHECK ADD CONSTRAINT [FK_Teacher_School] FOREIGN KEY([SchoolID])
REFERENCES [dbo].[School] ([ID])
GO
ALTER TABLE [dbo].[School] WITH CHECK ADD CONSTRAINT [FK_School_Teacher] FOREIGN KEY([PrincipleID])
REFERENCES [dbo].[Teacher] ([ID])
GO
Better design should be the one suggested by ADC