How To Create a Complex Table in sqlServer? - sql

Lets say I have a table called Employees , and each employee has a primarykey called (E_ID)
and I have another table called Positions , and each Position has a primarykey called (P_ID)
and I also have another table called offices , and each office has an ID called (O_ID)
Now I want to create a table that has three primaryKeys which are (E_ID) and (P_ID) and (O_ID) ...
ofcourse these three values must be withdrawl from the first three tables , but I just can't do it anyway ?
please help me because I neeeeeeed it badly
thanks verymuch

If it was me, I think I'd just add P_ID and O_ID to Employees. The same Position might be filled by multiple employees, and there might be multiple Employees at a given Office, but it's unlikely (without using Cloning technology) that the same Employee would need to be replicated multiple times - thus, just add P_ID and O_ID to Employee and I think you're good to go. Of course, you'll need foreign key constraints from Employee to Position (P_ID) and Office (O_ID).
EDIT: After some thought, and recalling that I've had jobs where I filled multiple positions (although at the same location), I suppose it's conceivable that a single person might have fill multiple positions which might be at different locations.
If you're really set on having a junction table between Employees, Positions, and Offices - OK, create a table called EmployeePositionOffice (or something like that) which contains the three columns E_ID, P_ID, and O_ID. The primary key should be (E_ID, P_ID, O_ID), and each field should be foreign-keyed to the related base table.
EDIT:
Not sure about the SQL Server syntax, but in Oracle the first would be something like:
ALTER TABLE EMPLOYEES
ADD (P_ID NUMBER REFERENCES POSITIONS(P_ID),
O_ID NUMBER REFERENCES OFFICES(O_ID));
while the second would be something like
CREATE TABLE EMPLOYEES_POSISTIONS_OFFICES
(E_ID NUMBER REFERENCES EMPLOYEES(E_ID),
P_ID NUMBER REFERENCES POSITIONS(P_ID),
O_ID NUMBER REFERENCES OFFICES(O_ID),
PRIMARY KEY (E_ID, P_ID, O_ID));
Share and enjoy.

Related

Query regarding SQLite and Cascading foreign keys

I am currently writing a application in C# that uses a SQLite database to store information the user will input. The application is basically a Management system for users who are called "Students" in the application. This is the most important table in my database and every other table is linked off this table. What I want to do is when a student is removed - they leave the institute/get kicked out etc. - is to remove their data from all the other tables so that data is no longer there as it isn't needed. An example of some of the Create table statements I have written is:
CREATE TABLE student(studentID int(5) PRIMARY KEY NOT NULL, name string(16),...,DOB string(8) );
CREATE TABLE emergencyContact(emergencyID int(5) PRIMARY KEY NOT NULL, name string(16),..., contactNumber int(16));
CREATE TABLE emergencyContactOf(studentID int(5) FOREIGN KEY REFERENCES student('studentID'), emergencyID int(5) FOREIGN KEY REFERENCES emergencyContact('emergencyID');
I have read up on this and my understanding is the data will be deleted in the EmergencyContactOf table if I include a 'ON DELETE CASCADE' statement as the StudentID key will no longer be present in the Parent table.
However, my understanding is the data in the EmergencyContact table that is for that specific student will not be deleted as there is no reference to the StudentID.
My question is, is there a way to remove the data from this table also that is relevant to that Student? For example, if I was to include a column in the EmergencyContact table which would reference the StudentID as a Foreign Key and then remove that row if the StudentID is ever deleted from the parent table? Is this a good solution to this particular problem?
All other tables I have are also designed in this way, where the data is in different tables and then linked back to the Student table with relationship tables so this will also apply to all the other tables I have.
Thanks.
My question is, is there a way to remove the data from this table also that is relevant to that Student? For example, if I was to include a column in the EmergencyContact table which would reference the StudentID as a Foreign Key and then remove that row if the StudentID is ever deleted from the parent table? Is this a good solution to this particular problem?
What happens if multiple students have the same emergency contact? You don't want to duplicate data if you don't have to - that's the whole point of the emergencyContactOf table, to efficiently set up a many to many relation between students and emergency contacts. So you don't want to do something like you describe.
You could periodically (Monthly, yearly, after purging student rosters, whatever) run a delete that removes rows from emergencyContact if they don't appear in emergencyContactOf:
DELETE FROM emergencyContact
WHERE emergencyID NOT IN (SELECT emergencyID FROM emergencyContactOf)
or the like.
Hmm, I see two scenarios here. What if two students have the same emergency contact, say two bothers having their father as emergency contact?
If in such a case you store only one record (the father) in the emergency contact table, you don't want to delete the emergency contact if only one of them leaves. You'd delete the emergency contact for the other one. So you'd need additional logic, when to delete an emergency contact. You could put that in a trigger.
You use a less sophisticated approach and multiple rows from the emergency contact table can map to one person in real life. In that case you can pull the reference to the student directly into the emergency contact table and use ON DELETE CASCADE there.
CREATE TABLE student
(studentid int(5),
name string(16),
...
PRIMARY KEY (studentid),
...);
...
CREATE TABLE emergencycontact
(emergencycontactid int(5),
studentid int(5),
name string(16),
...
PRIMARY KEY (emergencycontactid),
FOREIGN KEY (studentid)
REFERENCES student
(studentid),
...);
The second might be tempting but the "clean way" is the first one, as the second allows contradicting data. From what you posted you're already on the "clean way". But a mentioned that required triggers.

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.

Is it possible to create a foreign key constraint using "NOT IN" logic

Is it possible to add a foreign key constraint on a table which will allow values which do NOT exist in another table?
In the example below, both tables contain the field USER_ID. The constraint is that a customer and and an employee cannot have the same USER_ID value.
I am very limited in adding new tables or altering one of the tables in any way.
CUSTOMER
--------------------------
USER_ID varchar2(10)
EMPLOYEE
--------------------------
USER_ID varchar2(10)
I thought of a few workarounds, such as a view which contains the data from both tables or adding an insert trigger on the table I can modify.
No, no such thing exists, though it is possible to fake.
If you want to do this relationally (which would be a lot better than views/triggers) the easy option is to add a E to all employee IDs and a C to all customer IDs. However, this won't work if you have other attributes and you want to ensure they're not the same person (i.e. you're not just interested in the ID).
If this is the case you need to create a third table, let's call it PEOPLE:
create table people (
user_id varchar2(10) not null
, user_type varchar2(1) not null
, constraint pk_people primary key (user_id)
, constraint chk_people_user_types check ( user_type in ('C','E') )
);
C would stand for customer and E for employee in the check constraint. You then need to create a unique index/constraint on PEOPLE:
create index ui_people_id_type on people ( user_id, user_type );
Personally, I'd stop here and completely drop your CUSTOMER and EMPLOYEE tables; they no longer have any use and your problem has been solved.
If you don't have the ability to add new columns/tables you need to speak to the people who do and convince them to change it. Over-complicating things only leads to errors in logic and confusion (believe me - using a view means you need a lot of triggers to maintain your tables and you'll have to ensure that someone only ever updates the view). It's a lot easier to do things properly, even if they take longer.
However, if you really want to continue you alter your CUSTOMER and EMPLOYEE tables to include their USER_TYPE and ensure that it's always the same for every row in the table, i.e.:
alter table customers add user_type default 'C' not null;
alter table customers add constraint chk_customers_type
check ( user_type is not null and user_type = 'C' );
Unless you are willing to change the data model as someone else has suggested, the simplest way to proceed with the existing structure while maintaining mutual exclusion is to issue check constraints on the user_ids of both tables such that they validate only to mutually exclusive series.
For example, you could issue checks to ensure that only even numbers are assigned to customers and odd numbers to employees ( or vice-versa).
Or, since both IDS are varchar, stipulate using your check constraint that the ID begins with a known substring, such as 'EMP' or 'CUST'.
But these are only tricks and are hardly relational. Ideally, one would revise the data model. Hope this helps.