INSERT in TABLES with circular references SQL - sql

I have 2 tables:
Empleados(**numEmpl**, nombre, apellido, sexo, telefono, salario, numDept)
Departamentos(**numDept**, nombreDept, numDirect)
In departamentos:
numEmpl is primary key
numDept is foreign key reference to Departamentos(numDept).
In departamentos:
numDept is the primary key
And numDirect is foreign key reference to Empleados (numEmpl)
So there is a circular reference.
First of all I created the Tables:
CREATE TABLE EMPLEADOS(numEmpl primary key, nombre,
apellido, sexo, telefono, salario, numDept)
CREATE TABLE DEPARTAMENTOS(numDept primary key, nombreDept, numDirect)
(i didn't write here each of type is each colum)
Now I create the reference between them:
ALTER TABLE DEPARTAMENTOS
ADD CONSTRAINT FK_DEPT_EMP FOREIGN KEY (numDirect)
REFERENCES EMPLEADOS(numEmpl)
ALTER TABLE EMPLEADOS
ADD CONSTRAINT FK_EMP_DEPT FOREIGN KEY (numDept)
REFERENCES DEPARTAMENTOS(numDept).
It worked, so now I tried to insert some data:
INSERT INTO Empleados(numEmpl, nombre, apellidos, sexo, telefono, salario, numDept)
VALUES (1, 'Pepito', 'Pérez', 'H', '111111111', 20000, 1);
INSERT INTO Departamentos(numDept, nombreDept, numDirect)
VALUES (1, 'Direccion', 1);
But now it throws me an error, telling me I can't introduce data in a circular reference, I tryed to disable the circular reference and insert the data, and then enable it again, it worked but someone told me it isn't the right way and I have to do something special while I'm creating the tables to insert the datas in that way and it will work, but I don't have idea how to do it.
I'm using oracle sql developer by the way.
EDIT: Thanks for the answers, but they didn't worked. First of all I only can have that tables, and when I make the insert it MUST work in that way, without making a parameter null and then update it, I'm sorry I didn't say it before.
So the only way I have to do it, it's allowing the circle reference, but when I try to do it in the way someone said here, it tells me something about a rollback, someone can help?

To allow cyclic references, you need deferrable constraints:
ALTER TABLE DEPARTAMENTOS
ADD CONSTRAINT FK_DEPT_EMP FOREIGN KEY (numDirect)
REFERENCES EMPLEADOS(numEmpl)
DEFERRABLE INITIALLY DEFERRED
;
ALTER TABLE EMPLEADOS
ADD CONSTRAINT FK_EMP_DEPT FOREIGN KEY (numDept)
REFERENCES DEPARTAMENTOS(numDept)
DEFERRABLE INITIALLY DEFERRED
;
Deferrable constraints are checked at transaction end; before commit time a spurious invalid database state is allowed to exist (in the original question: between the two insert statements). But the statements must be inside a transaction, so the statements should be enclosed in BEGIN [WORK]; and COMMIT [WORK];.

Circular references are dangerous, and cause you to need to go back and update your data so it is not in an inconsistent state.
If you are in your planning stages still I urge you to take a look at other options to avoid this, otherwise you may run into a lot of headaches down the road.
http://blogs.msdn.com/b/sqlazure/archive/2010/07/01/10033575.aspx
If you do wish to use them still, then I would suggest setting NULL as an allowed value on the departments table (this allows you to insert a new value with no d), Inserting the employee, and then go back and update with the employee id.

This is happening because you can't create a record in the Department table with a value of 1 for numDirect until you have created a record in the Employees table for that employee (numEmpl=1). And you can't create the employee until you have created his department record. This is solved by making the process three steps instead of just two. To do this you have to be able to create the Department record without the numDirect FK value, or you have to able to create the Employee without the numDept FK value.
Say you decide on the latter. In that case Make NumDept Nullable in table EMPLEADOS :
Alter table EMPLEADOS Alter Column numDept null
Then you can:
First, Add a employee with a null value for numDept
INSERT Empleados(numEmpl, nombre, apellidos,
sexo, telefono, salario, numDept)
VALUES (1, 'Pepito', 'Pérez', 'H', '111111111', 20000, null);
Second, Add employee:
INSERT Departamentos(numDept, nombreDept, numDirect)
VALUES (1, 'Direccion', 1);
And finally, Update value of numDept in Department record.
Update Empleados Set numDept = 1
Where numEmpl = 1

Take the numDirect column out of your departamentos table. That table should simply describe the department. Depending on your business rules, you want a one to many relationship between departamentos and Empleados, which you have, or a many to many relationship between them. If an Empleado can work for more than one departamento, then you want to drop the numDept column from the Empleados table and create another table to set up the many to many relationship.
If you manage to figure out a way to add records with your current design, you will have a bigger problem. Instead of having just one record for each departamento, you will need one for every Empleado in it.

Related

Create constraint for control insert in table

There are two tables - orders and a list of services. In the first there is a bool field that the order is approved, if it is true then you can’t insert / delete values in the second table. With the UPDATE of the first table and the DELETE of the second, it is clear.
INSERT make as
INSERT INTO b (a_id, b_value)
SELECT *
FROM (VALUES (1, 'AA1-BB1'),(1, 'AA1-BB2'),(1, 'AA1-BB3')) va
WHERE (SELECT NOT confirm FROM a WHERE a_id = 2);
https://dbfiddle.uk/?rdbms=postgres_12&fiddle=7b0086967c1c38b0c80ca5624ebe92e9
How to forbid to insert without triggers and stored procedures? Is it possible to compose somehow complex constraint or a foreign key for checking conditions at the DBMS level?
The most recent version of Postgres supports generated columns. So, you can do:
alter table b add confirm boolean generated always as (false) stored;
Then create a unique key in a:
alter table a add constraint unq_a_confirm_id unique (confirm, id);
And finally the foreign key relationship:
alter table b add constraint fk_b_a_id_confirm
foreign key (confirm, a_id) references a(confirm, id);
Now, only confirmed = false ids can be used. Note that this will prevent updates to a that would invalidate the foreign key constraint.

How to link three tables?

I'm new to SQL and ask for your help.
There are 3 tables, these are "Employees", "Positions" and "EmployeesPositions".
For example, 2 positions can be attached to one employee.
How to link tables so that duplicates do not occur? I read about foreign keys and JOIN, but I have not yet figured out how to do it correctly.
Table structure:
Employees (id, Name);
Positions (id, Post, Rate); EmployeesPositions - I do not know how to make it right.
What I need: when adding an employee to the "Employees" table, associate an entry with posts from the "Positions" table, but as I wrote above, one employee can be associated with 2 posts (but not always). How correctly to implement the third table (EmployeesPositions), because in Positions only posts and rates are stored, and in EmployeesPositions there should be records, for example, Name1 => Post1 and Post2, and Name2 only Post 1?
If I thought something wrong, tell me please how best to implement it.
There are several ways to solve your problem, each with their own pros and cons.
First if we simplify your problem to "an employee has zero or more positions", then you can use the following table to associate an employee with a position:
create table employeespositions (
employee_id integer not null,
position_id integer not null,
constraint pk_employeespositions
primary key (employee_id, position_id),
constraint fk_employeespositions_employee
foreign key (employee_id) references employees (id),
constraint fk_employeespositions_position
foreign key (position_id) references positions (id)
)
The foreign keys enforce the existence of the employee and the position, while the primary key ensures a combination of employee and position only exists once.
This solution has two downsides:
It does not enforce that an employee has at least one position
It allows an employee to have more than two positions
The second problem is easily fixed by adding a trigger that checks if there is at most 1 position for an employee when attempting to insert (this allows a maximum of two):
create exception tooManyPositions 'Too many positions for employee';
set term #;
recreate trigger employeespositions_bi
active before insert on employeespositions
as
declare position_count integer;
begin
select count(*)
from employeespositions
where employee_id = new.employee_id
into position_count;
if (position_count > 1) then
exception tooManyPositions;
end#
set term ;#
However this solution does not enforce that an employee has at least one position. You could add a before delete trigger that ensures that the last position cannot be deleted, but that does not ensure that a newly created employee has at least one position. If you want to enforce that, you may want to consider using stored procedures for inserting and updating employees and their positions, and have the code of those stored procedures enforce that (eg by requiring a position when creating an employee).
Alternatively, you could also consider denormalizing your design, and making the positions part of the employees record, where the employee has a 'primary' and (optionally) a 'secondary' position.
create table employees (
-- using Firebird 3 identity column, change if necessary
id integer generated by default as identity primary key,
name varchar(100),
primary_position_id integer not null,
secondary_position_id integer,
constraint fk_employees_primary_position
foreign key (primary_position_id) references positions (id),
constraint fk_employees_secondary_position
foreign key (secondary_position_id) references positions (id),
constraint chk_no_duplicate_position
check (secondary_position_id <> primary_position_id)
)
The not null constraint on primary_position_id enforces the existence of this position, while the check constraint prevents assignment of the same position to both columns. Optionally you could consider adding a before insert or update trigger that when primary_position_id is set null, will set it to the value of secondary_position_id and sets secondary_position_id to null.
This solution has the advantage of allowing the enforcement of the existence of a primary position, but may lead to additional complexities when querying positions. This disadvantage can be overcome by creating a view:
create view employeespositions
as
select id as employee_id, primary_position_id as position_id
from employees
union all
select id as employee_id, secondary_position_id as position_id
from employees
where secondary_position_id is not null;
This view can then be used as if it is a table (although you can't insert into it).

SQL Insert values into table

I am trying to insert a row into my database table, but I keep on getting a SQL error.
I have a table called tbl_template_log, it has 3 Forgain Keys, user_id, temp_id, savedtemp_id, at the moment I only want to Insert a row with user_id and set temp_id and savedtemp_id to 0.
Query:
INSERT INTO tbl_template_log (user_id, temp_id, savetemp_id, send_date, send_to, email_send) VALUES (user_id=77, temp_id=0, savetemp_id=0, send_date='2013-10-10', send_to='test#test.com', email_send='hello')
Error:
INSERT INTO tbl_template_log (user_id, temp_id, savetemp_id, send_date, send_to, email_send) VALUES (user_id=77, temp_id=0, savetemp_id=0, send_date='2013-10-10', send_to='test#test.com', email_send='hello')
MySQL said: Documentation
#1452 - Cannot add or update a child row: a foreign key constraint fails (`admin_boltmail`.`tbl_template_log`, CONSTRAINT `tbl_template_log_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `tbl_user` (`user_id`))
From What I understand there is some sort of issue with user_id that the ID of the user has to be existing in tbl_user and ID 77 is an existing user id.....
Could some one point out a mistake I am doing. Thx
You can't add a row to this table unless the other tables specified in your foreign key constraints already have a record with the field value you're trying to insert. That's what a foreign key constraint means.
You're trying to break the rule you defined on your table. No, the database won't let you break the rule. You'll either have to add records to the foreign key tables or disable the constraints.

How can I insert into a table that is connected to another table with a 1-1 relationship

I dont know much about SQL, and I've got a problem using it.
I have two tables that connected to each other 1-1
Tbl1 (int_id1, str_desc1,....) and
Tbl2 (int_id2, str_desc2,....)
And these two are connected to each other
int_id1 ---- int_id2
First I want to know that is my design true?
And how can I insert into one of these of two together.
Cause I've got problem when I try to insert into one
here's the error description:
The INSERT statement conflicted with the FOREIGN KEY constraint
"FK_Tbl2_Tbl1". The conflict occurred in database "project", table
"dbo.Tbl1", column 'int_id1'.
Tnx...
It means you are trying to insert a value in a Foreign Key Column which does not exist in the Primary Key Column which it referencing to.
Any value you add in a Foreign Key Column, It must Exist in the Primary Key Column to which it referencing to, after all that is the whole Idea of adding Foreign Key Constraints. so you will not end up having orphan records in a table and also it reduces data redundancy.
Read Here for more information about Foreign Key Constraints.
First I want to know that is my design true?
When you have tables in a one to one relationship, the first question should be: why not just use one table? There may not be a need to separate the data.
Assuming you have 2 tables that have a 1to1 relationship. You can use a transaction to add rows to each. Lets say you have a Classes and Professors table, with a 1to1 relationship. Then you would do something like this:
begin tran;
insert into Classes (title) values ('Math 101');
insert into Professors(name) values ('Tim Rogers');
commit tran;
begin tran;
insert into Classes (title) values ('History 101');
insert into Professors(name) values ('Suzanne Bethany');
insert into Classes (title) values ('PE 101');
insert into Professors(name) values ('Emily Williams');
commit tran;
select * from Classes;
select * from Professors;

Dealing with circular reference when entering data in SQL

What kind of sql tricks you use to enter data into two tables with a circular reference in between.
Employees
EmployeeID <PK>
DepartmentID <FK> NOT NULL
Departments
DepartmentID <PK>
EmployeeID <FK> NOT NULL
The employee belongs to a department, a department has to have a manager (department head).
Do I have to disable constraints for the insert to happen?
I assume your Departments.EmployeeID is a department head. What I'd do is make that column nullable; then you can create the department first, then the employee.
Q: Do I have to disable constraints for the insert to happen?
A: In Oracle, no, not if the foreign key constraints are DEFERRABLE (see example below)
For Oracle:
SET CONSTRAINTS ALL DEFERRED;
INSERT INTO Departments values ('foo','dummy');
INSERT INTO Employees values ('bar','foo');
UPDATE Departments SET EmployeeID = 'bar' WHERE DepartmentID = 'foo';
COMMIT;
Let's unpack that:
(autocommit must be off)
defer enforcement of the foreign key constraint
insert a row to Department table with a "dummy" value for the FK column
insert a row to Employee table with FK reference to Department
replace "dummy" value in Department FK with real reference
re-enable enforcement of the constraints
NOTES: disabling a foreign key constraint takes effect for ALL sessions, DEFERRING a constraint is at a transaction level (as in the example), or at the session level (ALTER SESSION SET CONSTRAINTS=DEFERRED;)
Oracle has allowed for foreign key constraints to be defined as DEFERRABLE for at least a decade. I define all foreign key constraints (as a matter of course) to be DEFERRABLE INITIALLY IMMEDIATE. That keeps the default behavior as everyone expects, but allows for manipulation without requiring foreign keys to be disabled.
see AskTom: http://www.oracle.com/technology/oramag/oracle/03-nov/o63asktom.html
see AskTom: http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:10954765239682
see also: http://www.idevelopment.info/data/Oracle/DBA_tips/Database_Administration/DBA_12.shtml
[EDIT]
A: In Microsoft SQL Server, you can't defer foreign key constraints like you can in Oracle. Disabling and re-enabling the foreign key constraint is an approach, but I shudder at the prospect of 1) performance impact (the foreign key constraint being checked for the ENTIRE table when the constraint is re-enabled), 2) handling the exception if (when?) the re-enable of the constraint fails. Note that disabling the constraint will affect all sessions, so while the constraint is disabled, other sessions could potentially insert and update rows which will cause the reenable of the constraint to fail.
With SQL Server, a better approach is to remove the NOT NULL constraint, and allow for a NULL as temporary placeholder while rows are being inserted/updated.
For SQL Server:
-- (with NOT NULL constraint removed from Departments.EmployeeID)
insert into Departments values ('foo',NULL)
go
insert into Employees values ('bar','foo')
go
update Departments set EmployeeID = 'bar' where DepartmentID = 'foo'
go
[/EDIT]
This problem could be solved with deferable constraints. Such constraints are checked when the whole transaction is commited, thus allowing you to insert both employee and department in the same transaction, referring to each other. (Assuming the data model makes sense)
Refactor the schema by removing the circular reference.
Delete an ID column from either of the table schema.
Departments.EmployeeID doesn't seem to belong there in my opinion.
I can't think of a non hackish way to do this. I think you will need to remove the constraint or do some type of silly dummy values that get updated after all the inserts.
I'd recommend refactoring the DB schema. I can't think of any reasons why you would want it to work this way.
Maybe something like, Employee, EmployeeDepartment (EmployeeId, DepartmentId) and Department would be a better way to accomplish the same goal.
You could create a row in the Department table for 'Unassigned'
To create a new department with a new Employee you then would
Create the Employee (EmployeeA) in the 'Unassigned' Department
Create the new department (DepartmentA) with the employee EmployeeA
Update EmployeeA to be in DepartmentA
This wouldn't invalidate your current schema, and you could set up a task to be run regularly to check there are no members of the Unassigned department.
You would also need to create a default employee to be the Employee of Unassigned
EDIT:
The solution proposed by chaos is much simpler though
There are a few good designs I've used. All involve removing the "manager" EmployeeID from the Department table and removing the DepartmentID from the Employee table. I've seen a couple answers which mention it, but I'll clarify how we used it:
I typically end up with an EmployeeDepartment relationship link table - many to many, usually with flags like IsManager, IsPrimaryManager, IsAdmin, IsBackupManager etc., which clarify the relationship Some may be constrained so that there is only one Primary Manager allowed per department (although a person can be a PrimaryManager of multiple departments). If you don't like the single table, then you can have multiple tables: EmployeeDepartment, ManagerDepartment, etc. but then you could have situations where a person is a manager but not an employee, etc.
We also typically allowed people to be members of multiple departments.
For simplified access, you can provide views which perform the join appropriately.
Yes, in this instance you will have to disable a foreign key.
You need to get rid of one or the other reference permanently . This is not a viable design structure. Which has to be entered first? Department or Employee? Unless your departments are all one employee big, the structure doesn't make sense anyway as each employee would have to have a distinct departmentid.