Can we have two primary keys in one Foreign key field? - sql-server

My db hierarchy is like Group -> Branch -> department.
Employee and Roles are different table ,tbl_Employee having FK groupid,branchid, departmentid and RoleId. If I assign an employee as a '1' which is a groupHead according to Roles table then I have to provide this employee's branchId and depId as well. But all Branches of the particular group comes under him . So I was thinking of writing all the branchIds which comes under him. How should I proceed ?

You can build a structure like this:
create table Groups (
GroupID char(2) not null primary key
)
create table Branches (
GroupID char(2) not null foreign key references Groups (GroupID),
BranchID char(2) not null primary key,
constraint UQ_Branch_XRef UNIQUE (GroupID,BranchID)
)
create table Departments (
GroupID char(2) not null foreign key references Groups (GroupID),
BranchID char(2) not null foreign key references Branches (BranchID),
DepartmentID char(2) not null primary key,
constraint UQ_Department_XRef UNIQUE (GroupID,BranchID,DepartmentID),
constraint FK_Department_Branch_XRef FOREIGN KEY (GroupID,BranchID)
references Branches (GroupID,BranchID)
)
create table Employees (
EmployeeID char(2) not null primary key,
GroupID char(2) not null references Groups (GroupID),
BranchID char(2) null references Branches (BranchID),
DepartmentID char(2) null references Departments (DepartmentID),
RoleID int not null,
constraint FK_Employee_Branch_XRef FOREIGN KEY (GroupID,BranchID)
references Branches (GroupID,BranchID),
constraint FK_Employee_Department_XRef FOREIGN KEY (GroupID,BranchID,
DepartmentID)
references Departments (GroupID,BranchID,DepartmentID),
constraint CK_AppropriateRole CHECK (
(RoleID = 1 and BranchID is null and DepartmentID is null) or
(RoleID = 2 and BranchID is not null and DepartmentID is null) or
(RoleID = 3 and BranchID is not null and DepartmentID is not null)
)
)
And then populate it like this:
insert into Groups (GroupID) values ('aa'),('bb')
insert into Branches (GroupID,BranchID) values
('aa','cc'),('aa','dd'),('bb','ee'),('bb','ff')
insert into Departments (GroupID,BranchID,DepartmentID) values
('aa','cc','gg'),
('aa','cc','hh'),
('aa','dd','ii'),
('bb','ee','jj'),
('bb','ff','kk'),
('bb','ff','ll')
And insert some valid employees
insert into Employees (EmployeeID,GroupID,BranchID,DepartmentID,RoleID) values
('mm','aa',null,null,1),
('nn','aa','dd',null,2),
('oo','bb','ee','jj',3)
But then you can't insert invalid rows:
insert into Employees (EmployeeID,GroupID,BranchID,DepartmentID,RoleID) values
('pp','bb',null,null,3)
insert into Employees (EmployeeID,GroupID,BranchID,DepartmentID,RoleID) values
('qq','aa','cc','hh',1)
insert into Employees (EmployeeID,GroupID,BranchID,DepartmentID,RoleID) values
('rr','aa','cc','ii',3)
And then it's implied that 'mm' is attached to group 'aa' and all branches and departments under it, 'nn' is attached to branch 'dd' of group 'aa' and all departments under that, and 'oo' is in department 'jj' of branch 'ee' of group 'bb'.
Whenever you store a NULL in (a column of) a foreign key, in SQL Server, then SQL Server doesn't enforce that foreign key.
The Constraints
There are 16 constraints in the 4 above tables, and most of them serve an important purpose. I'll ignore the primary keys since they're just fulfilling their usual role.
create table Branches (
GroupID char(2) not null foreign key references Groups (GroupID),
Just a plain foreign key. Nothing special yet
...
constraint UQ_Branch_XRef UNIQUE (GroupID,BranchID)
We need this unique constraint so that foreign keys can reference this table by both columns, so that we can cross-check that where a BranchID is used, it's used with the correct GroupID.
)
create table Departments (
GroupID char(2) not null foreign key references Groups (GroupID),
BranchID char(2) not null foreign key references Branches (BranchID),
The above two foreign key references aren't strictly necessary. The other FK defined below on this table will also ensure that the values are valid. But they are the "natural" FKs that most people would define. Whether you keep them or not is a matter of preference and possibly performance.
...
constraint UQ_Department_XRef UNIQUE (GroupID,BranchID,DepartmentID),
This fulfils a similar role to UQ_Branch_XRef above, allowing FKs to reference this table by all 3 columns to ensure that all of the values are consistent.
constraint FK_Department_Branch_XRef FOREIGN KEY (GroupID,BranchID)
references Branches (GroupID,BranchID)
And this is the first FK that makes use of UQ_Branch_XRef, ensuring that our GroupID column is appropriate for our chosen BranchID.
)
create table Employees (
...
GroupID char(2) not null references Groups (GroupID),
This FK constraint is important if both BranchID and DepartmentID are null - it's our only FK constraint that actually applies in such a circumstance.
BranchID char(2) null references Branches (BranchID),
DepartmentID char(2) null references Departments (DepartmentID),
These 2 FKs are technically redundant, given other FKs declared below, but they're the "natural" FKs.
...
constraint FK_Employee_Branch_XRef FOREIGN KEY (GroupID,BranchID)
references Branches (GroupID,BranchID),
This ensures that our GroupID and BranchID are correct. This FK is only important if DepartmentID is null, since in such a circumstance, the next FK won't apply a constraint
constraint FK_Employee_Department_XRef FOREIGN KEY (GroupID,BranchID,
DepartmentID)
references Departments (GroupID,BranchID,DepartmentID),
This is the constraint that ensures all 3 of the named columns match up with the entry in the Departments table.
constraint CK_AppropriateRole CHECK (
(RoleID = 1 and BranchID is null and DepartmentID is null) or
(RoleID = 2 and BranchID is not null and DepartmentID is null) or
(RoleID = 3 and BranchID is not null and DepartmentID is not null)
)
And finally this is the constraint that applies some business rules about what roles should have which columns filled in.
)

Related

Is it better to have an Id as primary key or a double foreign key

I want to know what is the most optimized data structure:
I have 2 tables like:
CREATE TABLE Person
(
Id Int NOT NULL PRIMARY KEY,
name varchar(50)
)
CREATE TABLE Group
(
Id Int NOT NULL PRIMARY KEY,
nameOfGroup varchar(50)
)
In my table to connect persons to groups I want to know what is the most optimized way to create it between an ID and then the foreign keys, like that:
CREATE TABLE PersonGroup
(
Id Int DISTINCT NOT NULL PRIMARY KEY,
PersonId Int, -- which is a foreign key to table Person
GroupId Int -- which is a foreign key to table Group
)
OR having the 2 foreign keys as a primary because no one can be 2 times in the same group anyway, like that :
CREATE TABLE PersonGroup
(
PersonId Int NOT NULL, -- which is a foreign key to table Person
GroupId Int NOT NULL, -- which is a foreign key to table Group
CONSTRAINT PK_PersonSgroup PRIMARY KEY (PersonId, GroupId)
)
What is the most optimized between those 2 tables for querying after (if there is more optimize).
Thank you to have read my post.
The first solution (PK identity) is incomplete because you need to have an alternate (or surrogate) key (AK) compound of the two FK.
In the two possibilities, you must know that the order of the two FK in the PK or the AK is very important. This order must follow the usual querying way. So the questions are
do you use first the group then the person in queries ?
do you use first the personthen the group in queries ?
Also you need an index on the second PK, not the first of the PK or AK compound of the two PK.
For me the best table will be:
CREATE TABLE PersonGroup
(
PersonId Int NOT NULL REFERENCES Person (PersonId),
GroupId Int NOT NULL REFERENCES Group (GroupId),
CONSTRAINT PK_PersonSgroup PRIMARY KEY (PersonId, GroupId)
);
CREATE INDEX X_PersonGroup_FK_Group (GroupId);

Chicken or Egg problem in table schema [PostgreSQL]

How do I create and insert rows to the following table schema in PostgreSQL:
Table: employee
emp_id, emp_name, emp_dept, emp_manager
Table: department
dept_id, dept_name, dept_manager
emp_manager is a foreign key to employee(emp_id)
emp_dept is a foreign key to department(dept_id)
dept_manager is a foreign key to employee(emp_id)
It can work like this:
CREATE TABLE employee (
emp_id int PRIMARY KEY
, emp_dept int NOT NULL
, emp_manager int
, emp_name text NOT NULL
, CONSTRAINT fk_emp_manager FOREIGN KEY (emp_manager) REFERENCES employee(emp_id)
, UNIQUE (emp_dept, emp_id) -- needed for FK fk_dept_manager
);
CREATE TABLE department (
dept_id int PRIMARY KEY
, dept_manager int
, dept_name text NOT NULL
, CONSTRAINT fk_dept_manager FOREIGN KEY (dept_id, dept_manager) REFERENCES employee(emp_dept, emp_id)
);
ALTER TABLE employee
ADD CONSTRAINT fk_emp_dept
FOREIGN KEY (emp_dept) REFERENCES department(dept_id);
Note how I change fk_dept_manager into a multicolumn FK reference to only allow employees of the same department to be department manager. Assuming you want that.
You might also want a CHECK constraint in table employee to disallow employees from being their own manager:
CHECK (emp_manager <> emp_id)
How to INSERT?
As usual. To overcome mutual dependencies, either make FK constraints DEFERRABLE and run multiple commands in a single transaction (more expensive) or use a single command with one or more CTEs.
Example: to insert a new department and a new employee as its manager at once:
WITH ins_dept AS (
INSERT INTO department
(dept_manager , dept_name)
VALUES (nextval(pg_get_serial_sequence('employee', 'emp_id')), 'Sales')
RETURNING *
)
INSERT INTO employee
(emp_id , emp_dept, emp_manager, emp_name)
SELECT dept_manager, dept_id , NULL, 'Bob'
FROM ins_dept;
db<>fiddle here
Further reading:
Complex foreign key constraint in SQLAlchemy
SET CONSTRAINTS ALL DEFERRED not working as expected
How to deal with mutually dependent inserts

Can't create tables with Foreign Key of each other

Is there a way to create both tables while being a foreign key of one another? I am using SQL Server Management Studio
--DROP TABLE orders
CREATE TABLE orders
(
orderId bigint PRIMARY KEY IDENTITY(880001, 1) NOT NULL,
receiptNo bigint FOREIGN KEY REFERENCES receipt(receiptId),
productId bigint FOREIGN KEY REFERENCES productServices(productId),
quantity int,
dateOrdered datetime DEFAULT CURRENT_TIMESTAMP
)
--DROP TABLE receipt
CREATE TABLE receipt
(
receiptNo bigint PRIMARY KEY IDENTITY(900001, 1) NOT NULL,
employeeId bigint FOREIGN KEY REFERENCES employeeInfo(employeeId),
customerId bigint FOREIGN KEY REFERENCES customerInfo(customerId),
orderId bigint FOREIGN KEY REFERENCES orders(orderId),
paymentMethod varchar(4),
dateOfPurchase datetime DEFAULT CURRENT_TIMESTAMP
)
The requirement always is: a table must exist before you can create a foreign key reference to it. So in your case, obviously, you cannot create both table with the full FK references in place, since they reference each other.
What you need to do is:
CREATE TABLE orders without the FK reference to Receipt (since that table doesn't exist yet at this time)
CREATE TABLE receipt - here you can include the FK reference to orders - that table has been created and exists
Alter your table orders to add the FK reference to receipt:
ALTER TABLE dbo.orders
ADD CONSTRAINT FK_Orders_Receipt
FOREIGN KEY (receiptNo) REFERENCES dbo.receipt(receiptId);
Of course, the same applies to all the other FK constraints you have - you cannot reference a table that doesn't exist yet. You must first create the tables, then you can add the FK constraints.
For you Case, you change the order of creating the Table.
1.) Before adding a Foreign Key References to a new table, the table to be refereed as a foreign key must be already exists.
2.) Also a add a batch Separator GO statement while single Execution of creating tables.
--DROP TABLE receipt
CREATE TABLE receipt
(
receiptNo bigint PRIMARY KEY IDENTITY(900001, 1) NOT NULL,
employeeId bigint FOREIGN KEY REFERENCES employeeInfo(employeeId),
customerId bigint FOREIGN KEY REFERENCES customerInfo(customerId),
orderId bigint FOREIGN KEY REFERENCES orders(orderId),
paymentMethod varchar(4),
dateOfPurchase datetime DEFAULT CURRENT_TIMESTAMP
)
GO
--DROP TABLE orders
CREATE TABLE orders
(
orderId bigint PRIMARY KEY IDENTITY(880001, 1) NOT NULL,
receiptNo bigint FOREIGN KEY REFERENCES receipt(receiptId),
productId bigint FOREIGN KEY REFERENCES productServices(productId),
quantity int,
dateOrdered datetime DEFAULT CURRENT_TIMESTAMP
)
GO
This is hard because it's wrong. There's no need for two FKs and they are actively harmful as you could have an order whose receipt points to a different order. The right design here is to just have one FK, and enforce uniqueness on the FK column. Thus an order may be created first, and later at most one receipt can be added for that order.
eg:
create table employeeInfo(employeeid bigint primary key)
create table customerInfo(customerID bigint primary key)
create table productServices(productId bigint primary key)
CREATE TABLE orders
(
orderId bigint PRIMARY KEY IDENTITY(880001, 1) NOT NULL,
--receiptNo bigint FOREIGN KEY REFERENCES receipt(receiptId),
productId bigint FOREIGN KEY REFERENCES productServices(productId),
quantity int,
dateOrdered datetime DEFAULT CURRENT_TIMESTAMP
)
CREATE TABLE receipt
(
receiptNo bigint PRIMARY KEY IDENTITY(900001, 1) NOT NULL,
employeeId bigint FOREIGN KEY REFERENCES employeeInfo(employeeId),
customerId bigint FOREIGN KEY REFERENCES customerInfo(customerId),
orderId bigint FOREIGN KEY REFERENCES orders(orderId) unique,
paymentMethod varchar(4),
dateOfPurchase datetime DEFAULT CURRENT_TIMESTAMP
)

Add a unique constraint of a sql table as foreign key reference to an another sql table

how to add a unique constraint of a sql table as foreign key reference to an another sql table in sql server 2005
In order to add FK constraint (in child table to parent table) you have to add unique constraint to parent table columns of relationship.
All the rest is optional or has nothing to do with FK:
no obligatory need of any primary key
no need of uniqueness in child table colums(s)
The parent table (in such FK relation) is frequently called (including by SSMS) as Primary Key table but PK is not must, unique key/constraint in parent table is enough (as PK is unique, it is particular case of unique constraint in parent table).
Drop TableA and TableB from answer by Matt, which is confusing for beginners,
and recreate them as
CREATE TABLE parentB--TableB
(
PK1 INT NOT NULL,
PK2 INT NOT NULL,
--I would not have additional non-referenced data in parent table,
--rather in child table
--SomeData VARCHAR(1000),
--CONSTRAINT PK_TableB PRIMARY KEY CLUSTERED (PK1, PK2)
)
CREATE TABLE childA--TableA
(
--PK INT, -- NOT NULL,
FK1 INT-- NOT NULL, -- Or NULL, if you''d rather.
FK2 INT --NOT NULL --,
, SomeData VARCHAR(1000)
--CONSTRAINT PK_TableA PRIMARY KEY CLUSTERED (PK),
--CONSTRAINT FK_TableA_FK1FK2 FOREIGN KEY (FK1, FK2) REFERENCES TableB (PK1, PK2),
--CONSTRAINT Cons2cols UNIQUE(FK1, FK2)
)
Now, in order, to add FK
ALTER TABLE childA
ADD
--constraint FK1_childA
--this is optional, if one needs to add his own custom name
FOREIGN KEY (FK1) REFERENCES parentB(PK1);
you should first create unique constraint on corresponding referenced column in parent table column:
ALTER TABLE parentB ADD
--CONSTRAINT YourUniqueName --uncomment for adding your own name to constraint
UNIQUE(PK1)
Similarly for 2 columns foreign key constraint
(first, you need corresponding unique constraint in parent table):
ALTER TABLE parentB ADD
--CONSTRAINT YourUniqueName --for adding your own name to unique constraint
UNIQUE(PK1,PK2)
ALTER TABLE childA
ADD
--constraint yourUniqueName --uncomment for adding your own name to FK constraint
FOREIGN KEY (FK1, FK2) REFERENCES parentB(PK1, PK2);
Apologies but I'm not really sure what you're asking here. Giving more of an example with table definitions would help! I think you're saying you have two columns in TableA in a unique constraint named "Cons2cols", and you also want these two columns to be a FK to a two column PK / unqiue pair in TableB.
That works as follows, if you're creating the tables from scratch:
CREATE TABLE TableB (
PK1 INT NOT NULL,
PK2 INT NOT NULL,
SomeData VARCHAR(1000),
CONSTRAINT PK_TableB PRIMARY KEY CLUSTERED (PK1, PK2)
)
CREATE TABLE TableA (
PK INT NOT NULL,
FK1 INT NOT NULL, -- Or NULL, if you''d rather.
FK2 INT NOT NULL,
CONSTRAINT PK_TableA PRIMARY KEY CLUSTERED (PK),
CONSTRAINT FK_TableA_FK1FK2 FOREIGN KEY (FK1, FK2) REFERENCES TableB (PK1, PK2),
CONSTRAINT Cons2cols UNIQUE(FK1, FK2)
)
If the tables already exist, you can add in these same constraints after the fact:
ALTER TABLE TableA ADD CONSTRAINT FK_TableA_FK1FK2 FOREIGN KEY (FK1, FK2) REFERENCES TableB (PK1, PK2);
ALTER TABLE TableA ADD CONSTRAINT Cons2cols UNIQUE(FK1, FK2);
Either way, TableA now has a unique, 2 column FK to another table.
You need to keep in mind that adding a FK on a column does not automatically put an index on that column. You'll need to do this in two steps.
1) Make a column in your table a FK to a parent table.
2) Add a unique constraint on that same column
Forget about the unique constraint for now. Just create your new foreign key on the two columns.
ALTER TABLE dbo.PurchaseDetail
ADD FOREIGN KEY (Customer, Product)
REFERENCES dbo.Purchase (Customer, Product)
I prefer this approach where this table references another table (transaction_log):
CREATE TABLE transaction_settings_log
(
transaction_fk UUID NOT NULL
CONSTRAINT transaction_log_pkey REFERENCES transaction_log (id) UNIQUE,
group_selected BOOLEAN DEFAULT TRUE,
leg_closed BOOLEAN DEFAULT FALSE
);

multiple constraint keys

if i have tabled:
Resource (id (PK), name)
Manager (id(PK), resource_id (FK), manager_resource_ID(FK))
Should resource_id and manager_id both be foreign keys into the Resource table.
i obviously dont want to enter any values in each of those columns that are not proper resources
when i add the first relationship (resource_id <-> id) it works fine but
when i add the second one (manager_resource_id <-> id) it fails with the error:
Unable to create relationship [ . . .] The ALTER TABLE statement conflicted with the FOREIGN KEY constraint [... ]. The conflict occured in table Resource, column id
or do i need to break this out into 3 tables?
Resource(id, first, last)
Resource_manager(id, resource_id, manager_ID)
Manager(id)
Just a hint:
UPDATE:
If your model has employee-manager as many-to-many (bit unusual) then you could do:
CREATE TABLE Employee
(
EmployeeID int NOT NULL
,[Name] varchar(50)
)
go
ALTER TABLE Employee ADD
CONSTRAINT PK_Employee PRIMARY KEY CLUSTERED (EmployeeID ASC)
go
CREATE TABLE Manager
(
EmployeeID int NOT NULL
,ManagerID int NOT NULL
)
go
ALTER TABLE Manager ADD
CONSTRAINT PK_Manager PRIMARY KEY CLUSTERED (EmployeeID ASC, ManagerID ASC)
,CONSTRAINT FK1_Manager FOREIGN KEY (EmployeeID) REFERENCES Employee(EmployeeID)
,CONSTRAINT FK2_Manager FOREIGN KEY (ManagerID) REFERENCES Employee(EmployeeID)
,CONSTRAINT chk_Manager CHECK (EmployeeID <> ManagerID)
go
You have to create the foreign keys in the Manager table.

Resources