Find missing rows between three related tables - sql

I have three tables:
Person
person_id
-------------
10001
10002
10003
10004
Dates
date_type date
-------------- -----------------------
PUBLIC_HOLIDAY 2020-04-10 00:00:00.000
PUBLIC_HOLIDAY 2020-04-13 00:00:00.000
Absence
person_id date absence_type
--------- ----------------------- ------------
10001 2020-04-10 00:00:00.000 HOLIDAY
10001 2020-04-13 00:00:00.000 HOLIDAY
10002 2020-04-10 00:00:00.000 HOLIDAY
10003 2020-04-13 00:00:00.000 HOLIDAY
I need to find all of the person_id's in the Person table and the date's from the Dates table who have not booked any absence matching the following criteria:
Dates.date_type = 'PUBLIC_HOLIDAY'
Absence.absence_type = 'HOLIDAY'
Basically, I need to find the people and the dates which are public holidays they have not booked an absence for as holiday.

You can try this below logic-
DEMO HERE
SELECT Person.person_id,Dates.dat,ISNULL(Absence.dat, 'Not Bokked')
FROM Dates
CROSS JOIN Person
LEFT JOIN Absence ON Person.person_id = Absence.person_id AND Dates.dat = Absence.dat
WHERE Dates.date_type = 'PUBLIC_HOLIDAY'
If you wants only information with not booked, just simply add below line to the script-
AND Absence.dat IS NULL

I think that you want a cross join to generate all combinations of persons and dates, and then not exists to filter on those that do not exist in the absence table:
select p.*, d.*
from person p
cross join dates d
where
d.date_type = 'PUBLIC_HOLIDAY'
and not exists (
select 1
from absence a
where a.person_id = p.person_id and a.date = d.date and a.absence_type = 'HOLIDAY'
)

Try:
select distinct person_id from absence a
where absence_type = 'HOLIDAY'
and not exists (select 1 from dates
where date = a.date
and date_type = 'PUBLIC_HOLIDAY')
union all
select person_id from person p
where not exists ( select 1 from absence
where p.person_id = person_id)
If you want to have them with dates, use below query:
select person_id, date from absence a
where absence_type = 'HOLIDAY'
and not exists (select 1 from dates
where date = a.date
and date_type = 'PUBLIC_HOLIDAY')
union all
-- in person table we don;t have any dates
select person_id, null from person p
where not exists ( select 1 from absence
where p.person_id = person_id)

you can use below query. I have tested this in SQL Server 2014.
CREATE TABLE #Person(person_id INT)
INSERT INTO #person
values
(10001),
(10002),
(10003),
(10004);
CREATE TABLE #Dates (date_Type VARCHAR(50), [datevalue] datetime)
INSERT INTO #Dates
VALUES
('PUBLIC_HOLIDAY','2020-04-10 00:00:00.000'),
('PUBLIC_HOLIDAY','2020-04-13 00:00:00.000');
CREATE TABLE #Absence (person_id int, datevalue datetime, absence_type VARCHAR(50))
INSERT INTO #Absence
VALUES
(10001,'2020-04-10 00:00:00.000','HOLIDAY'),
(10001,'2020-04-13 00:00:00.000','HOLIDAY'),
(10002,'2020-04-10 00:00:00.000','HOLIDAY'),
(10003,'2020-04-13 00:00:00.000','HOLIDAY');
SELECT p.person_id, od.datevalue
FROM #Person AS p
CROSS JOIN (SELECT * FROM #Dates WHERE date_type ='PUBLIC_HOLIDAY') AS od
WHERE NOT EXISTS
(
SELECT 1 FROM
#Absence AS a
INNER JOIN #Dates AS d
ON a.datevalue = d.datevalue
WHERE a.absence_type = 'Holiday' AND d.date_type = 'PUBLIC_HOLIDAY'
AND a.person_id = p.person_id and d.datevalue = od.datevalue)
Below is the resultset:
+-----------+-------------------------+
| person_id | datevalue |
+-----------+-------------------------+
| 10003 | 2020-04-10 00:00:00.000 |
| 10004 | 2020-04-10 00:00:00.000 |
| 10002 | 2020-04-13 00:00:00.000 |
| 10004 | 2020-04-13 00:00:00.000 |
+-----------+-------------------------+

Related

Create a subquery in a view SQL- ORACLE

I am setting up a database view.
I have a table PERSON and a RELATION_COMPANY related by the person_id. I amb trying to obtain the min date from the relation_company for each person_id.
I need to use a subquery?
This is what I have tried so far.
CREATE OR REPLACE VIEW PEOPLE_ADDRESS
select
p.id as ADDRESS_CODE,
case p.type_
when 1 then 'JURIDICAL'
when 0 then 'NATURAL'
end PERSON_TYPE,
(select min(start_date) from relation_company where relation_company.person_id = p.id) as INITIAL_dATE
P.ID as PERSON_CODE,
p.ADDRESS_NAME as ADDRESS,
p.POSTAL_CODE as POSTAL_CODE,
case P.COUNTRY_ID
when 68 then 'NO'
else 'YES'
end FOREIGN_INDICATOR
from
PERSON p join relation_company r on relation_company = p.id ;
That's my person table:
ID TYPE_ ADDRESS_NAME NAME OTHER COLUMNS
1 0 AVENUE SMITH, 19 MICHAEL SCOTT ....
2 1 OXFORD STREET , 119 COMPANY A ....
3 1 CHESTHAM ROAD, 7 COMPANY B ....
That's the RELATION_COMPANY table:
ID PERSON_ID START_DATE
1 1 2016-12-12 00:00:00
2 1 1981-07-09 00:00:00
3 2 1991-10-10 00:00:00
4 2 1981-03-09 00:00:00
5 3 1984-07-05 00:00:00
6 3 1981-08-11 00:00:00
7 3 1987-07-09 00:00:00
8 3 2011-07-09 00:00:00
select min(START_DATE) from RELATION_COMPANY group by PERSON_ID;
The result should be:
ADDRESS_CODE PERSON_TYPE INITIAL_dATE other columns
1 NATURAL 1981-07-09 00:00:00 ....
2 JURIDICAL 1981-03-09 00:00:00 ....
3 JURIDICAL 1981-08-11 00:00:00
I want to get the min(start_date) for each person_id and print it to the view to the column INITIAL_dATE.
I am having trouble to create that query.
You Already have the solution, this query are missing the "person_id" column in your SELECT statement.
Change this query
select min(START_DATE) from RELATION_COMPANY group by PERSON_ID;
To this.
select min(START_DATE),PERSON_ID from RELATION_COMPANY group by PERSON_ID;
Hope this helps.
You don't need a subquery for your solution.
Only add a group-clause to your query.
like this one:
select
p.id as PERSON_ID,
case p.type_
when '1' then 'JURIDICAL'
when '0' then 'NATURAL'
end PERSON_TYPE,
min(start_date) as INITIAL_dATE,
p.ADDRESS_NAME as ADDRESS,
p.POSTAL_CODE as POSTAL_CODE,
case P.COUNTRY_ID
when 68 then 'NO'
else 'YES'
end FOREIGN_INDICATOR
from PERSON p join relation_company r on r.person_id = p.id
group by p.id, type_, ADDRESS_NAME, POSTAL_CODE, P.COUNTRY_ID;

Joining multiple tables with same identifier & pulling in a field based on shared identifier

The context: a health care clinic has 2 tables: one on patient visits, and one on patient appointments. They are not 1:1; it is possible to have an appointment without a visit, or to have a visit without an appointment. The identifier for both tables is the encounter ID, enc_ID. I'm trying to outer join these tables together and to pull in patient names.
A boiled-down example of the visit table (V):
enc_ID Visit_date Patient_ID
1 2018-06-01 10
2 2018-06-02 11
And the appointment data (A):
enc_ID Appointment_time Patient_ID
1 2018-06-01 13:00 10
3 2018-06-03 14:00 12
Outer-joining these on visit_ID would produce something like:
enc_ID V.Visit_date A.Appointment_time V.Patient_ID A.Patient_ID
1 2018-06-01 2018-06-01 13:00 10 10
2 2018-06-02 NULL 11 NULL
3 NULL 2018-06-03 14:00 NULL 12
Say I want to basically combine V.Patient_ID and A.Patient_ID, and pull in patient name from another table (P), joined on Patient_ID. The desired output:
enc_ID V.Visit_date A.Appointment_time Patient_ID Patient_Name
1 2018-06-01 2018-06-01 13:00 10 Patient A
2 2018-06-02 NULL 11 Patient B
3 NULL 2018-06-03 14:00 13 Patient C
How might this be accomplished? I'm probably missing something obvious, but I don't see how I can join in P.Patient_Name without having to join it to either V.Patient_ID or A.Patient_ID, either of which would result in null patient names.
Thanks in advance!
As long as enc_ID in both tables are not nullable, you can combine those fields with a COALESCE:
SELECT
COALESCE(V.Patient_ID, A.Patient_ID) AS enc_ID,
V.Visit_date,
A.Appointment_time,
COALESCE(V.Patient_ID, A.Patient_ID) AS Patient_ID,
P.Patient_Name
FROM V
FULL JOIN A ON V.enc_ID = A.enc_ID
INNER JOIN P
ON P.Patient_ID = V.Patient_ID
OR P.Patient_ID = A.Patient_ID
-- ON P.Patient_ID = COALESCE(V.Patient_ID, A.Patient_ID) also works
You can use ISNULL to join table P.
...(Your outer join query)
LEFT JOIN P
ON P.Patient_ID = ISNULL(V.Patient_ID, A.PatientID)

SQL Database Table Joining to create an IdealTable

I need some helps to join the tables I have currently.
Leave, Overtime And Roster's Date, EmployeeID need to match
Note: ShiftDuration is set to default value = 8.25
Note: Leave and Overtime table will only have entries when an employee applies for leave and overtime.
Employee
EmplyeeeID | Username | Password | GivenName | FamilyName | TeamID | ContactNo | StaffType
------------------------------------------------------------------------------------------
123 123 abc John Snow 1 999 1
1234 1234 abcd Jack Waller 2 223 1
12345 12345 abcde Ali Saw 1 123 1
123456 123456 abcdef Peter Peter 2 223 1
1234567 1234567 abcdeg Bryan Peter 1 333 1
Roster
Duty_ID | EmployeeID | Date | ShiftType | ShiftDuration
--------------------------------------------------------------------
2 123 2018-05-05 1 8.25
4 1234 2018-05-04 1 8.25
5 12345 2018-05-05 1 8.25
7 123456 2018-05-04 1 8.25
8 1234567 2018-05-05 1 8.25
Overtime
OTID | EmployeeID | Date | OT_Duration | OT_Reason
------------------------------------------------------------
2 1234 2018-05-04 2 Cover Duty
Leave
LeaveID | EmployeeID | Date | Duration_Off | Reason
----------------------------------------------------------
3 123 2018-05-05 2 NIL
IdealTable (Via Query)
Date | EmployeeID | GivenName | FamilyName | TeamID | ShiftType | ShiftDuration | Duration_Off | OT_Duration | Total_Hours
---------------------------------------------------------------------------------------------------------------------------------
2018-05-05 123 John Snow 1 1 8.25 2 0 6.25
2018-05-04 1234 Jack Waller 1 1 8.25 0 2 10.25
2018-05-05 12345 Ali Saw 1 1 8.25 0 0 8.25
2018-05-04 123456 Peter Peter 1 1 8.25 0 0 8.25
2018-05-05 1234567 Bryan Peter 1 1 8.25 2 0 8.25
I have 4 tables, they are Employee, Leave, Overtime, Roster
Employee
-EmployeeID (PK)
-Username
-Password
-GivenName
-FamilyName
-TeamID
-ContactNo
-StaffType
Leave
-LeaveID (PK)
-EmployeeID (FK)
-Date
-Duration_Off
-Reason
Overtime
-OTID (PK)
-EmployeeID (FK)
-Date
-OT_Duation
-OT_Reason
Roster
-DutyID (PK)
-EmployeeID (FK)
-Date
-ShiftType
-Shift Duration (Default Value = 8.25)
What I am trying to do is join the data from this 4 tables using Query
Ideal Table
-Date (From Leave, Overtime and Roster Table)
-EmployeeID (Employee Table)
-GivenName (Employee Table)
-FamilyName (Employee Table)
-TeamID (Employee Table)
-ShiftType (Roster Table)
-ShiftDuration (Roster Table)
-Duration_Off (Leave Table)
-OT_Duration (Overtime Table)
-Total_Hours (Calculation from joint table [(ShiftDuration + OT_Duration) - Duration_Off]
My database diagram design Do ignore the TimeData table as I initially wanted to use the TimeData table to achieve the IdealTable
My current query
USE [SMRT Dashboard]
GO
;With Dates
AS
(
SELECT [Date] FROM dbo.Roster
UNION
SELECT [Date] FROM dbo.Leave
UNION
SELECT [Date] FROM dbo.Overtime
),
Work_Matrix
AS
(
SELECT EmployeeID,[Date],ShiftType,ShiftDuration,CAST(NULL AS Decimal(30,2)) AS Duration_Off,CAST(NULL AS Decimal(30,2)) AS OT_Duration
FROM dbo.Roster
UNION ALL
SELECT EmployeeID,[Date], NULL, NULL,Duration_Off
FROM dbo.Leave
UNION ALL
SELECT EmployeeID,[Date],NULL,NULL,NULL,OT_Duration
FROM dbo.Overtime
)
SELECT d.[Date],
e.EmployeeID,
e.GivenName,
e.FamilyName,
e.TeamID,
w.ShiftType,
w.ShiftDuration,
w.Duration_Off,
w.OT_Duration,
w.Total_Hours
FROM Dates d
INNER JOIN
(
SELECT EmployeeID,
[Date],
MAX(ShiftType) AS ShiftType,
SUM(ShiftDuration) AS ShiftDuration,
SUM(Duration_Off) AS Duration_Off,
SUM(OT_Duration) AS OT_Duration,
SUM(ShiftDuration) + SUM(OT_Duration) - SUM(Duration_Off) AS Total_Hours
FROM Work_Matrix
GROUP BY EmployeeID,
[Date]
)w
ON d.[Date] = w.[Date]
JOIN dbo.Employee e
ON e.EmployeeID = w.EmployeeID
Current Errors:
Msg 205, Level 16, State 1, Line 4
All queries combined using a UNION, INTERSECT or EXCEPT operator must have an equal number of expressions in their target lists.
I have not tested this but you may now check this. Hope your problem will be solved.
SELECT
a.EmployeeID,
a.GivenName,
a.FamilyName,
a.TeamID,
d.ShiftType,
d.ShiftDuration,
b.Duration_Off,
c.OT_Duration,
b.Date,
(d.ShiftDuration + c.OT_Duration) - b.Duration_Off as Total_Hours
FROM Employee a
INNER JOIN Roster d ON a.EmployeeID = d.EmployeeID
LEFT JOIN Leave b ON a.EmployeeID = b.EmployeeID
LEFT JOIN Overtime c ON a.EmployeeID = c.EmployeeID
I assume Date is same from all three tables (Leave, Overtime and Roster Table)
Try this query :
select
l.Date,
o.Date,
r.Date,
e.EmployeeID,
e.GivenName,
e.FamilyName,
e.TeamID,
r.ShiftType,
r.ShiftDuration,
l.Duration_Off,
o.OT_Duration,
((r.ShiftDuration+o.OT_Duration)-l.Duration_Off) as Total_Hours
FROM Employee e
INNER JOIN Leave l ON e.EmployeeID = l.EmployeeID
INNER JOIN Overtime o ON e.EmployeeID = o.EmployeeID
INNER JOIN Roster r ON r.EmployeeID = r.EmployeeID;
Try this Query ! You will get the correct output .
SELECT
e.EmployeeID AS 'Emp ID',
e.GivenName AS 'Emp Name',
l.Date AS Date,
e.FamilyName AS 'Family Name',
e.TeamID AS 'Team ID',
r.ShiftType AS 'Shift Type',
r.ShiftDuration AS 'Shift Duration',
l.Duration_Off AS 'Duration Off',
o.OT_Duration AS 'OT Duration',
(r.ShiftDuration + o.OT_Duration) - l.Duration_Off as 'Total Hours'
FROM
Employee e,
Leave l,
Overtime o,
Roster r
WHERE
e.EmployeeID = l.EmployeeID
AND
e.EmployeeID = o.EmployeeID
AND
e.EmployeeID = r.EmployeeID
I think the issue here is that an employee may have leave, may have overtime, or neither. By using INNER JOIN, you're filtering down the set of employees to ones that have Leave, and on a Roster, and have Overtime.
I created a SqlFiddle with the data you present (see here) that should get you closer.
SELECT
COALESCE(r.Date, o.Date, l.Date) as Date,
e.EmployeeID,
e.GivenName,
e.FamilyName,
e.TeamID,
r.ShiftType,
r.ShiftDuration,
IFNULL(l.Duration_Off, 0) as Duration_Off,
IFNULL(o.OT_Duration, 0) as OT_Duration,
r.ShiftDuration + IFNULL(o.OT_Duration, 0) - IFNULL(l.Duration_Off, 0) as Total_Hours
FROM Employee e
INNER JOIN Roster r on
e.EmployeeID = r.EmployeeID
LEFT JOIN Overtime o on
e.EmployeeID = o.EmployeeID
LEFT JOIN `Leave` l on
e.EmployeeID = l.EmployeeID
There's a few cavaets that this query won't solve:
If an employee has multiple leave entries or multiple overtime entries, the person will be listed N times. You can potentially use a GROUP BY statement to deal with this
The sample data in your question doesn't reconcile correctly. For example, Employe 1234 has a shift on 5/4/2018 and OT on 5/5/2018 but gets 10.25 on 5/4/2018.
If you use TimeData, this becomes a lot easier (assuming one record per date per employee):
SELECT
COALESCE(r.Date, o.Date, l.Date) as Date,
e.EmployeeID,
e.GivenName,
e.FamilyName,
e.TeamID,
r.ShiftType,
IFNULL(r.ShiftDuration, 0) as ShiftDuration,
IFNULL(l.Duration_Off, 0) as Duration_Off,
IFNULL(o.OT_Duration, 0) as OT_Duration,
IFNULL(r.ShiftDuration, 0) + IFNULL(o.OT_Duration, 0) - IFNULL(l.Duration_Off, 0) as Total_Hours
from TimeData t
INNER JOIN Employee e on
t.EmployeeID = e.EmployeeID
LEFT JOIN Roster r on
t.Duty_ID = r.Duty_ID
LEFT JOIN Overtime o on
t.OTID = o.OTID
LEFT JOIN `Leave` l on
t.LeaveID = l.LeaveID
Example SqlFiddle can be found here

How to sum the hours using two Date Fields and group them by the user id in SQL

I feel like the task is straight forward but I am having hard time getting it to do what I want.
Here is a table in my database:
ID |Empl_Acc_ID |CheckIn |CheckOut |WeekDay
----------------------------------------------------------------------------
1 | 1 | 2017-09-24 08:03:02.143 | 2017-09-24 12:00:00.180 | Sun
2 | 1 | 2017-09-24 13:02:23.457 | 2017-09-24 17:01:02.640 | Sun
3 | 2 | 2017-09-24 08:05:23.457 | 2017-09-24 13:01:02.640 | Mon
4 | 2 | 2017-09-24 14:05:23.457 | 2017-09-24 17:00:02.640 | Mon
5 | 3 | 2017-09-24 07:05:23.457 | 2017-09-24 11:30:02.640 | Tue
6 | 3 | 2017-09-24 12:31:23.457 | 2017-09-24 16:01:02.640 | Tue
and so on....
I want to group Empl_Acc_ID by the same date and sum up the total hours each employee worked that day. Each employee could have either one or more records per day depending on how many breaks he/she took that day.
For example if Empl_Acc_ID (2) worked 3 different days with one break, the table will contain 6 records for that person but in my query I want to see 3 records with the total hours they worked each day.
Here is how I constructed the query:
select distinct w.Empl_Acc_ID, ws.fullWorkDayHours
from Work_Schedule as w
INNER JOIN (
SELECT Empl_Acc_ID, fullWorkDayHours = Sum(DATEDIFF(hour, w.CheckIn, w.CheckOut))
from Work_Schedule w
GROUP BY Empl_Acc_ID
) ws on w.Empl_Acc_ID = ws.Empl_Acc_ID
This query does not quite get me what I need. It only returns the sum of hours per employee for all the days they worked. Also, this query only has 2 columns but I want to see more columns. when I tried adding more columns, the records no longer are distinct by Empl_Acc_ID.
What is wrong with the query?
Thank you
You do not need self-join this table in that case, just group by casting the datetime field to date.
create table Work_Schedule (
ID TINYINT,
Empl_Acc_ID TINYINT,
CheckIn DATETIME,
CheckOut DATETIME,
WeekDay CHAR(3)
);
INSERT INTO Work_Schedule VALUES (1, 1,'2017-09-24 08:03:02.143','2017-09-24 12:00:00.180','Sun');
INSERT INTO Work_Schedule VALUES (2, 1,'2017-09-24 13:02:23.457','2017-09-24 17:01:02.640','Sun');
INSERT INTO Work_Schedule VALUES (3, 2,'2017-09-24 08:05:23.457','2017-09-24 13:01:02.640','Mon');
INSERT INTO Work_Schedule VALUES (4, 2,'2017-09-24 14:05:23.457','2017-09-24 17:00:02.640','Mon');
INSERT INTO Work_Schedule VALUES (5, 3,'2017-09-24 07:05:23.457','2017-09-24 11:30:02.640','Tue');
INSERT INTO Work_Schedule VALUES (6, 3,'2017-09-24 12:31:23.457','2017-09-24 16:01:02.640','Tue');
SELECT w.Empl_Acc_ID,
CAST(CheckIn AS DATE) [date],
SUM(DATEDIFF(hour, w.CheckIn, w.CheckOut)) fullWorkDayHours
FROM Work_Schedule w
GROUP BY w.Empl_Acc_ID, CAST(CheckIn AS DATE)
DROP TABLE Work_Schedule;
Empl_Acc_ID date fullWorkDayHours
1 2017-09-24 8
2 2017-09-24 8
3 2017-09-24 8
Try this. You just have to group by date and employee account.
select Employee.Empl_Acc_ID, FirstName, LastName, Username,
convert(varchar(10), checkin, 101) as checkin, convert(varchar(10),
checkout, 101) as checkout, sum(datediff(hour, checkin, checkout)) as hours
from Employee
inner join Employee_Account on Employee.Empl_Acc_ID =
Employee_Account.Empl_Acc_ID
inner join Work_Schedule on Employee_Account.Empl_Acc_ID =
Work_Schedule.Empl_Acc_ID
group by convert(varchar(10), checkin, 101), convert(varchar(10), checkout,
101), Employee.Empl_Acc_ID, FirstName, LastName, Username
order by Employee.Empl_Acc_ID
You do not group by date, that's the issue:
SELECT DISTINCT w.Empl_Acc_ID, ws.fullWorkDayHours, ws.CheckInDate
FROM Work_Schedule as w
INNER JOIN (
SELECT Empl_Acc_ID, CAST(w.CheckIn AS DATE) AS [CheckInDate], fullWorkDayHours = Sum(DATEDIFF(hour,
w.CheckIn, w.CheckOut))
from Work_Schedule w
GROUP BY Empl_Acc_ID, CAST(w.CheckIn AS DATE)
) ws on w.Empl_Acc_ID = ws.Empl_Acc_ID
No need of doing self join, it works fine without it:
Select distinct Empl_Acc_ID, Sum(DATEDIFF(hour,CheckIN,CheckOut)) As
FullDayWorkHours from EMP2
where DATEPART(day,CheckIn)=DATEPART(day,CheckOut)
Group By Empl_Acc_ID

Sql SELECT for personnel available in a specific time period

I have 3 tables. First one is a list of personnel:
Table Personnel
Id_personnel Name
1 John
2 Alice
3 Tom
4 Charles
5 Ben
Second is a table with medical leave:
Table Medical
Id_personnel Start_date End_date
3 2012-08-02 2012-08-05
2 2012-08-02 2012-08-15
4 2012-10-04 2012-10-06
2 2012-10-02 2012-10-15
2 2012-09-20 2012-09-21
Third is a table with holiday leave:
Table Holiday
Id_personnel Start_date End_date
3 2012-08-02 2012-08-05
2 2012-02-02 2012-02-15
5 2012-10-01 2012-10-05
I have two event dates: Symposium_start_date and a Symposium_end_date (as variables) and I would like to select all Personnel.Id_personnel and Personnel.Name available for the symposium (not in medical leave or on holiday).
In my example if Symposium_start_date = 2012-10-02 and Symposium_end_date = 2012-10-06 the result must be:
Id_personnel Name
1 John
3 Tom
(as you can see Alice and Charles are on medical leave and Ben on holiday)
Thank you!
For a MS SQL server.
declare #start date = '2012-10-02', #end date = '2012-10-10'
select personnel.*
from personnel
left join holiday on personnel.Id_personnel = holiday.Id_personnel
and holiday.start_date<=#end
and holiday.end_date>=#start
left join medical on personnel.Id_personnel= medical.Id_personnel
and medical.start_date<=#end
and medical.end_date>=#start
where holiday.Id_personnel is null
and medical.Id_personnel is null
assuming MS-SQL, similar to others, but have shown tables unioned together, and subquery that shows all those who conflict with the symposium dates - I think this is easier to write first - and then selecting all those people who are NOT conflicting. Regards!
declare #Symposium_start_date date = '2012-10-02'
declare #Symposium_end_date date = '2012-10-06'
select * from Personnel P where p.Id_personnel not in (
select Id_personnel from (
select m.Id_personnel, m.Start_date, m.End_date
from Medical M
union
select h.Id_personnel, h.Start_date, h.End_date
from Holiday H
) as Leave
where (Start_date between #Symposium_start_date and #Symposium_end_date)
or (End_date between #Symposium_start_date and #Symposium_end_date)
or (Start_date <= #Symposium_start_date and End_date >= #Symposium_end_date)
)
this is an example with the Medical table only. You can easily add the Holiday
declare #Symposium_start_date datetime
declare #Symposium_end_date datetime
set #Symposium_start_date = '2012-08-01'
set #Symposium_end_date = '2012-08-04'
select *
from personel p left join medical m on p.Id_personel = m.Id_personel
where (#Symposium_start_date > m.end_date) or (#Symposium_end_date<m.start_date)