Foreign key without correspondence in the referenced table - sql

I'm trying to come up with a solution for an exercise in which I have to create 3 tables: one for employees, one for projects, and one for projects and employees, in which I have to insert the employee's ID and associate with a project ID. In the project_employee table, since it has only 2 columns that reference other tables, I thought I could set them bot as foreign keys, like this:
CREATE TABLE employee_project
(
id_employee numeric FOREIGN KEY REFERENCES employee(id_employee),
id_project numeric FOREIGN KEY REFERENCES project(id_project)
)
Then I stumbled upon a problem: when inserting values on the 3 tables, I noticed that one of the employee's ID number was 4, but on the table employee there was no line with ID 4. Of course, this line wasn't created, but I want to understand: is there a way I could create a line whose ID has no matching record in the referenced table? Could it be a possible mistake in the question, or is there something I'm missing? Thanks in advance for your time!

If there is no rows in employee table with id_employee value 4 then there should not be rows in employee_project table with id_employee value 4. SQL Server will give you an error like below:
The INSERT statement conflicted with the FOREIGN KEY constraint "FK__employee__id__75F77EB0". The conflict occurred in database "test", table "dbo.employee", column 'id_employee'.
But if you want to create employee_project with composite primary key on both the column you can try this:
CREATE TABLE employee_project
(
id_employee int not null,
id_project int not null,
primary key(id_employee, id_project )
)

Related

ORA-00904, Code Newbie I need help in identifying what's wrong

CREATE TABLE TASK_SKILL (TASK_ID INT REFERENCES PROJECT_SCHEDULE_TASK(TASK_ID),
SKILL_ID INT REFERENCES PROJECT(PROJECT_ID),
NUMBER_OF_EMPLOYEES INT,
PRIMARY KEY (TASK_ID, PROJECT_ID, SKILL_ID));
Good day, I know my questions seem to be quite newbie-ish and is common sense for most of you here, but I am still trying to learn SQL
I encountered "ORA-00904: "PROJECT_ID": invalid identifier".
I have already checked and it looks like I have a Project_ID column but still, I can't seem to run it.
You are creating a table TASK_SKILL with 3 fiels: TASK_ID, SKILL_ID and NUMBER_OF_EMPLOYEES. Also you want to create a Primary Key by TASK_ID, PROJECT_ID, SKILL_ID. Oracle is right, you do not have a PROJECT_ID field in your table.
Your field is called SKILL_ID, so the Primary key should be created using it, like this:
CREATE TABLE TASK_SKILL (TASK_ID INT REFERENCES PROJECT_SCHEDULE_TASK(TASK_ID),
SKILL_ID INT REFERENCES PROJECT(PROJECT_ID),
NUMBER_OF_EMPLOYEES INT,
PRIMARY KEY (TASK_ID, SKILL_ID));
In the PK of a table you ony include fields from the table and not field from the related table. So, no need to include te referenced PROJECT_ID.
This happens because you are trying to create a table with a column that doesn't exist, in this case, PROJECT_ID. In the TASK_SKILL Table you only have 3 Columns defined:
TASK_ID, which is a foreign key of the PROJECT_SCHEDULE_TASK table
SKILL_ID, which is a foreign key of the PROJECT table.
NUMBER_OF_EMPLOYEES which is an integer.
Try to create the table like this:
CREATE TABLE TASK_SKILL (TASK_ID INT REFERENCES PROJECT_SCHEDULE_TASK(TASK_ID),
SKILL_ID INT REFERENCES PROJECT(PROJECT_ID),
NUMBER_OF_EMPLOYEES INT,
PRIMARY KEY (TASK_ID, SKILL_ID));
Lastly, if you're just starting out learning SQL, you can first lay the foundations of database design before going hands-on.
oracle documentation of foreign keys

SQL Constraint to check the value of another table's column

Suppose I have 2 tables, Employee and Sale.
Employee table has EMP_ID int as PK, and ACTIVE_STATUS bit (0 for inactive and 1 for active).
Sale table has SALE_ID as PK, EMP_ID as FK referencing Employee.EMP_ID and DATE_OF_SALE
Now, I want a constraint that checks if the EMP_ID I'm trying to insert into Sale has a value of 1 for the ACTIVE column in the Employee table, because I don't want to register a sale that is being attempted by an Inactive user.
How would I go about that? I tried CONSTRAINT CHECK_IF_ACTIVE CHECK(Employee.ACTIVE = 1) but it's not a valid statement.
You can do what you are specifically asking for using foreign keys and computed columns. First, define a redundant unique constraint in employees:
alter table employees add constraint unq_employees_empid_active_status on (empid, active_status);
Then, define a computed column in sales. Alas, this needs to be persisted, I think:
alter table sales add active_status as (convert(bit, 1)) persisted;
Then, define the foreign key constraint using both:
alter table sales add foreign key fk_sales_employees_active
foreign key (empid, active_status) references employees(empid, active_status);
Voila! The employee id can only reference active employees.
Now, you will have a problem with this -- be careful what you ask for. It is not really what you want. This enforces the constraint over all time. So, you won't be able to change the status on employees who have sales. That suggests that you need an insert trigger instead -- or a user defined function and check constraint:
create function is_employee_active (
#empid int
) returns bit as
begin
return (select active_status from employees e where e.empid = #empid);
end;
alter table sales add constraint chk_sales_employee_active
check (is_employee_active(empid) = convert(bit, 1));
Voila! This only does the check on insertion or updates. Note that once an employee is not active, you won't be able to update the row either.
You'll notice that I usually name my tables in the plural, because they contain lots of examples of an entity. My fingers just add the "s" when I'm thinking about tables.

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).

How to reference foreign key from more than one column (Inconsistent values)

I Have table three tables:
The first one is emps:
create table emps (id number primary key , name nvarchar2(20));
The second one is cars:
create table cars (id number primary key , car_name varchar2(20));
The third one is accounts:
create table accounts (acc_id number primary key, woner_table nvarchar2(20) ,
woner_id number references emps(id) references cars(id));
Now I Have these values for selected tables:
Emps:
ID Name
-------------------
1 Ali
2 Ahmed
Cars:
ID Name
------------------------
107 Camery 2016
108 Ford 2012
I Want to
Insert values in accounts table so its data should be like this:
Accounts:
Acc_no Woner_Table Woner_ID
------------------------------------------
11013 EMPS 1
12010 CARS 107
I tried to perform this SQL statement:
Insert into accounts (acc_id , woner_table , woner_id) values (11013,'EMPS',1);
BUT I get this error:
ERROR at line 1:
ORA-02291: integrity constraint (HR.SYS_C0016548) violated - parent key not found.
This error occurs because the value of woner_id column doesn't exist in cars table.
My work require link tables in this way.
How Can I Solve This Problem Please ?!..
Mean: How can I reference tables in previous way and Insert values without this problem ?..
One-of relationships are tricky in SQL. With your data structure here is one possibility:
create table accounts (
acc_id number primary key,
emp_id number references emps(id),
car_id number references car(id),
id as (coalesce(emp_id, car_id)),
woner_table as (case when emp_id is not null then 'Emps'
when car_id is not null then 'Cars'
end),
constraint chk_accounts_car_emp check (emp_id is null or car_id is null)
);
You can fetch the id in a select. However, for the insert, you need to be explicit:
Insert into accounts (acc_id , emp_id)
values (11013, 1);
Note: Earlier versions of Oracle do not support virtual columns, but you can do almost the same thing using a view.
Your approach should be changed such that your Account table contains two foreign key fields - one for each foreign table. Like this:
create table accounts (acc_id number primary key,
empsId number references emps(id),
carsId number references cars(id));
The easiest, most straightforward method to do this is as STLDeveloper says, add additional FK columns, one for each table. This also bring along with it the benefit of the database being able to enforce Referential Integrity.
BUT, if you choose not to do, then the next option is to use one FK column for the the FK values and a second column to indicate what table the value refers to. This keeps the number of columns small = 2 max, regardless of number of tables with FKs. But, this significantly increases the programming burden for the application logic and/or PL/SQL, SQL. And, of course, you completely lose Database enforcement of RI.

SQL Creating New Table that is based of a column in an already created table

Here is an example of what I need, different values:
I already have table 1 created in the database.
Table 1: Person
Columns: PK->ID, Name, Favorite Color, Favorite Sport, etc..
This table is already in database and filled with values.
Now I want to create a second table, Table 2 which has a primary key of Favorite Sport column from my Table 1 and just one more column for the description.
Ex:
Table 2: Sports
Columns: Pk->Favorite Sport, description
I want to make sure I am just creating this table correctly, so I don't mess anything up. Would this be the correct syntax to use? (I will fill up the data separately after table is created.)
CREATE TABLE Sports (
Favorite_Sport Varcher(25),
Description Varcher(100),
PRIMARY KEY(Favorite_Sport),
Foreign KEY(Favorite_Sport) REFERENCES Person;
)
Thanks!
There are probably several ways to do this, but I think I'd go with
CREATE TABLE Sports
(SPORT Varchar2(25)
CONSTRAINT PK_SPORTS
PRIMARY KEY
USING INDEX,
Description Varchar2(100));
(I changed the name of the primary key column on the SPORTS table to SPORT).
You really don't want nor can you have SPORTS.SPORT reference PERSON.FAVORITE_SPORT, as FAVORITE_SPORT is not a primary or unique key on PERSON. Instead, you want the foreign key relationship to go the other way around, with PERSON.FAVORITE_SPORT referencing SPORTS.SPORT:
ALTER TABLE PERSON
ADD CONSTRAINT PERSON_FK1
FOREIGN KEY (FAVORITE_SPORT) REFERENCES SPORTS(SPORT);
SQLFiddle here
Best of luck.