count and sum over date ranges in sql - sql

I am stuck on a particular scenario when a employee changes his/her department.
Here are the exact details:
I have 2 tables-Department and Employee in SAP HANA database.
Dept_ID|Start_Date |End_Date
1 |15-Jan-2017|31-Dec-9999
Emp_ID|Dept_ID|Start_Date |End_Date
123 |1 |1-Jan-2017 |31-Dec-9999
456 |1 |1-Jan-2017 |31-Dec-9999
789 |1 |1-Jan-2017 |25-Jan-2017
789 |2 |26-Jan-2017 |31-Dec-9999
666 |1 |23-Jan-2017 |31-Dec-9999
What i need in the output is count of employees in each department over time -
Dept_ID|Emp_Count|Start_Date |End_Date
1 |3 |15-Jan-2017|23-Jan-2017
1 |4 |23-Jan-2017|25-Jan-2017
1 |3 |25-Jan-2017|31-Dec-9999
I tried with CTE using sum over (partition), but i am not able to get the desired result.
Please help me in solving this problem.
Edit:
Adding create definitions and insert statements
CREATE COLUMN TABLE DEPT ("DEPT_ID" NVARCHAR(400) NOT NULL ,
"START_DATE" LONGDATE CS_LONGDATE NOT NULL ,
"END_DATE" LONGDATE CS_LONGDATE NOT NULL ,
PRIMARY KEY INVERTED VALUE ("DEPT_ID",
"START_DATE")) UNLOAD PRIORITY 5 AUTO MERGE
CREATE COLUMN TABLE EMP ("EMP_ID" NVARCHAR(400) NOT NULL ,
"DEPT_ID" NVARCHAR(4000),
"START_DATE" LONGDATE CS_LONGDATE NOT NULL ,
"END_DATE" LONGDATE CS_LONGDATE NOT NULL ,
PRIMARY KEY INVERTED VALUE ("EMP_ID",
"START_DATE")) UNLOAD PRIORITY 5 AUTO MERGE
insert into DEPT values('1','15.01.2017 22:58:09.0','31.12.9999 00:00:00.0')
insert into EMP values('123','1','01.01.2017 22:58:09.0','31.12.9999 00:00:00.0')
insert into EMP values('456','1','01.01.2017 22:58:09.0','31.12.9999 00:00:00.0')
insert into EMP values('789','1','01.01.2017 22:58:09.0','25.01.2017 10:00:00.0')
insert into EMP values('789','2','25.01.2017 10:00:00.0','31.12.9999 00:00:00.0')
insert into EMP values('666','1','23.01.2017 22:58:09.0','31.12.9999 00:00:00.0')

Unfortunately I'm unable to test it on SAP HANA, therefore, I post a solution for SQL Server. I tried to use syntax that I found valid for SAP HANA as well.
with dates as
(
select *, row_number() over (partition by t.dept_id order by dat) rn
from
(
select dept_id, start_date dat from emp where emp.start_date > (select start_date from dept where dept_id = emp.dept_id)
union all
select dept_id, end_date dat from emp where emp.end_date < (select end_date from dept where dept_id = emp.dept_id)
union all
select dept_id, start_date dat from dept
union all
select dept_id, end_date dat from dept
) t
)
select e.dept_id, count(*), t.startd, t.endd
from emp e
join
(
select d1.dept_id, d1.dat startd, d2.dat endd
from dates d1
join dates d2 on d1.dept_id = d2.dept_id and d1.rn + 1 = d2.rn
) t on t.dept_id = e.dept_id and e.start_date < t.endd and e.end_date > t.startd
group by e.dept_id, t.startd, t.endd
demo
RESULT
dept_id count startd endd
1 3 15/01/2017 23/01/2017
1 4 23/01/2017 25/01/2017
1 3 25/01/2017 31/12/9999

I wasn't able to complete this yesterday but as I had some preparation already done here are some small alterations to the solution by Radim Bača. The differences are:
use a join to include the department from/to dates on employee rows
use the lead() function instead of row_number() (which avoids a self-join)
there is a department 2 row in the departments table
Demo at SQL Fiddle
CREATE TABLE Department
([Dept_ID] int, [Start_Date] datetime, [End_Date] datetime)
;
INSERT INTO Department
([Dept_ID], [Start_Date], [End_Date])
VALUES
(1, '2017-01-15 00:00:00', '9999-12-31 00:00:00'),
(2, '2017-01-15 00:00:00', '9999-12-31 00:00:00')
;
CREATE TABLE Employee
([Emp_ID] int, [Dept_ID] int, [Start_Date] datetime, [End_Date] datetime)
;
INSERT INTO Employee
([Emp_ID], [Dept_ID], [Start_Date], [End_Date])
VALUES
(123, 1, '2017-01-01 00:00:00', '9999-12-31 00:00:00'),
(456, 1, '2017-01-01 00:00:00', '9999-12-31 00:00:00'),
(789, 1, '2017-01-01 00:00:00', '2017-01-25 00:00:00'),
(789, 2, '2017-01-26 00:00:00', '9999-12-31 00:00:00'),
(666, 1, '2017-01-23 00:00:00', '9999-12-31 00:00:00')
;
Query 1:
WITH
e AS (
SELECT e.*, d.start_date stdt, d.end_date endt
FROM Employee e
INNER JOIN Department d ON e.dept_id = d.dept_id
),
range AS (
SELECT
dept_id
, start_date AS from_date
, LEAD(start_date) OVER (PARTITION BY dept_id
ORDER BY start_date) to_date
FROM (
SELECT dept_id , start_date FROM e WHERE e.start_date > e.stdt
UNION ALL
SELECT dept_id , end_date FROM e WHERE e.end_date < e.endt
UNION ALL
SELECT dept_id , start_date FROM Department
UNION ALL
SELECT dept_id , end_date FROM Department
) r
)
SELECT
e.dept_id
, r.from_date
, r.to_date
, COUNT(*) num_employees
FROM Employee e
INNER JOIN range r ON e.dept_id = r.dept_id
AND e.start_date < r.to_date
AND e.end_date > r.from_date
AND r.to_date IS NOT NULL
GROUP BY
e.dept_id
, r.from_date
, r.to_date
Results:
| dept_id | from_date | to_date | num_employees |
|---------|----------------------|----------------------|---------------|
| 1 | 2017-01-15T00:00:00Z | 2017-01-23T00:00:00Z | 3 |
| 1 | 2017-01-23T00:00:00Z | 2017-01-25T00:00:00Z | 4 |
| 1 | 2017-01-25T00:00:00Z | 9999-12-31T00:00:00Z | 3 |
| 2 | 2017-01-26T00:00:00Z | 9999-12-31T00:00:00Z | 1 |

Please see below written in SQL server.
DECLARE #Department TABLE
(
[Dept_ID] [int] NOT NULL,
[Start_Date] [date] NOT NULL,
[End_Date] [date] NOT NULL
)
DECLARE #Employee TABLE
(
[Emp_ID] [int] NOT NULL,
[Dept_ID] [int] NOT NULL,
[Start_Date] [date] NOT NULL,
[End_Date] [date] NOT NULL
)
DECLARE #Ranges TABLE
(
[Range_ID] [int] NOT NULL,
[Dept_ID] [int] NOT NULL,
[Start_Date] [date] NOT NULL,
[End_Date] [date] NOT NULL
)
INSERT INTO #Department (Dept_ID, Start_Date, End_Date)
VALUES (1, '15-Jan-2017', '31-Dec-9999')
INSERT INTO #Employee (Emp_ID, Dept_ID, Start_Date, End_Date)
VALUES (123,1,'1-Jan-2017','31-Dec-9999'),
(456,1,'1-Jan-2017','31-Dec-9999'),
(789,1,'1-Jan-2017','25-Jan-2017'),
(789,2,'26-Jan-2017','31-Dec-9999'),
(666,1,'23-Jan-2017','31-Dec-9999')
INSERT INTO #Ranges (Range_ID,Dept_ID, Start_Date, End_Date)
VALUES (1,1,'20170115','20170123'),
(2,1,'20170123','20170125'),
(3,1,'20170125','99991231')
SELECT E.Dept_ID, COUNT(*) As Emp_Count, R.Start_Date, R.End_Date
FROM #Employee E
INNER JOIN #Department D ON D.Dept_ID = E.Dept_ID
INNER JOIN #Ranges R ON R.Dept_ID = D.Dept_ID
WHERE 1=1
AND E.Start_Date <= R.Start_Date
AND E.End_Date >= R.End_Date
AND D.Start_Date <= R.Start_Date
AND D.End_Date >= R.End_Date
GROUP BY E.Dept_ID, R.Start_Date, R.End_Date

Related

Trying to include rows with a Full Outer Join & Comparing in the same table

I'm attempting to return the First person to check in in each Room_id by joining the PERSON and CHECK_IN tables
http://sqlfiddle.com/#!17/35d930 > Select PostGreSQL 9.6 > Build Schema > Paste Query
CREATE TABLE person
("id" int)
;
INSERT INTO person
("id")
VALUES
(1),
(2),
(3),
(4),
(5),
(6)
;
CREATE TABLE check_in
("id" int, "person_id" int, "room_id" int, "check_in_date" timestamp, "check_out_date" timestamp)
;
INSERT INTO check_in
("id", "person_id", "room_id", "check_in_date", "check_out_date")
VALUES
(100, 1, 202, '2020-10-01 00:00:00', '2021-09-05 00:00:00'),
(101, 2, 201, '2020-12-15 00:00:00', '2021-02-15 00:00:00'),
(104, 3, 204, '2021-05-20 00:00:00', '2021-07-04 00:00:00'),
(106, 4, 202, '2022-08-01 00:00:00', NULL),
(108, 3, 204, '2021-08-15 00:00:00', NULL)
;
select c1.person_id, c1.room_id, c1.check_in_date
from check_in c1
FULL OUTER JOIN check_in c2 on c2.room_id = c1.room_id
where c1.check_in_date < c2.check_in_date
order by c1.room_id
I'm returning room_ids 202 and 204, but cannot get the select to return 201.. Should I not be using a full outer join?
We don't need to join the person table as we have all the info we need in the check_in table.
select id
,person_id
,room_id
,check_in_date
,check_out_date
from (
select *
,row_number() over(partition by room_id order by check_in_date desc) as rn
from check_in
) t
where rn = 1
id
person_id
room_id
check_in_date
check_out_date
101
2
201
2020-12-15 00:00:00
2021-02-15 00:00:00
106
4
202
2022-08-01 00:00:00
null
108
3
204
2021-08-15 00:00:00
null
Fiddle
Your where condition transforms the outer join into an inner one. See the answer with window function above as well
select c1.person_id, c1.room_id, c1.check_in_date
from check_in c1
where c1.check_in_date = (select min(c2.check_in_date) from check_in c2
where c2.room_id = c1.room_id )
order by c1.room_id
select c1.person_id, c1.room_id, c1.check_in_date
from check_in as c1
where c1.check_in_date in (select min(check_in_date)
from check_in as c2
join person as p
on p.id = c2.person_id
group by c2.room_id)
group by 2,1,3
order by c1.room_id

Recursively loop through a SQL table and find intervals based on Start and End Dates

I have a SQL table that contains employeeid, StartDateTime and EndDatetime as follows:
CREATE TABLE Sample
(
SNO INT,
EmployeeID NVARCHAR(10),
StartDateTime DATE,
EndDateTime DATE
)
INSERT INTO Sample
VALUES
( 1, 'xyz', '2018-01-01', '2018-01-02' ),
( 2, 'xyz', '2018-01-03', '2018-01-05' ),
( 3, 'xyz', '2018-01-06', '2018-02-01' ),
( 4, 'xyz', '2018-02-15', '2018-03-15' ),
( 5, 'xyz', '2018-03-16', '2018-03-19' ),
( 6, 'abc', '2018-01-16', '2018-02-25' ),
( 7, 'abc', '2018-03-08', '2018-03-19' ),
( 8, 'abc', '2018-02-26', '2018-03-01' )
I want the result to be displayed as
EmployeeID | StartDateTime | EndDateTime
------------+-----------------+---------------
xyz | 2018-01-01 | 2018-02-01
xyz | 2018-02-15 | 2018-03-19
abc | 2018-01-16 | 2018-03-01
abc | 2018-03-08 | 2018-03-19
Basically, I want to recursively look at records of each employee and datemine the continuity of Start and EndDates and make a set of continuous date records.
I wrote my query as follows:
SELECT *
FROM dbo.TestTable T1
LEFT JOIN dbo.TestTable t2 ON t2.EmpId = T1.EmpId
WHERE t1.EndDate = DATEADD(DAY, -1, T2.startdate)
to see if I could decipher something from the output looking for a pattern. Later realized that with the above approach, I need to join the same table multiple times to get the output I desire.
Also, there is a case that there can be multiple employee records, so I need direction on efficient way of getting this desired output.
Any help is greatly appreciated.
This will do it for you. Use a recursive CTE to get all the adjacent rows, then get the highest end date for each start date, then the first start date for each end date.
;with cte as (
select EmployeeID, StartDateTime, EndDateTime
from sample s
union all
select CTE.EmployeeID, CTE.StartDateTime, s.EndDateTime
from sample s
join cte on cte.EmployeeID=s.EmployeeID and s.StartDateTime=dateadd(d,1,CTE.EndDateTime)
)
select EmployeeID, Min(StartDateTime) as StartDateTime, EndDateTime from (
select EmployeeID, StartDateTime, Max(EndDateTime) as EndDateTime from cte
group by EmployeeID, StartDateTime
) q group by EmployeeID, EndDateTime
You can use this.
WITH T AS (
SELECT S1.SNO,
S1.EmployeeID,
S1.StartDateTime,
ISNULL(S2.EndDateTime, S1.EndDateTime) EndDateTime,
ROW_NUMBER() OVER(PARTITION BY S1.EmployeeId ORDER BY S1.StartDateTime)
- ROW_NUMBER() OVER(PARTITION BY S1.EmployeeId, CASE WHEN S2.StartDateTime IS NULL THEN 0 ELSE 1 END ORDER BY S1.StartDateTime ) RN,
ROW_NUMBER() OVER(PARTITION BY S1.EmployeeId, ISNULL(S2.EndDateTime, S1.EndDateTime) ORDER BY S1.EmployeeId, S1.StartDateTime) RN_END
FROM Sample S1
LEFT JOIN Sample S2 ON DATEADD(DAY,1,S1.EndDateTime) = S2.StartDateTime
)
SELECT EmployeeID, MIN(StartDateTime) StartDateTime,MAX(EndDateTime) EndDateTime FROM T
WHERE RN_END = 1
GROUP BY EmployeeID, RN
ORDER BY EmployeeID DESC, StartDateTime
Result:
EmployeeID StartDateTime EndDateTime
---------- ------------- -----------
xyz 2018-01-01 2018-02-01
xyz 2018-02-15 2018-03-19
abc 2018-01-16 2018-03-01
abc 2018-03-08 2018-03-19

modify output from 1 query

need Your suggestion Guy's. I don't know what the title of my question. but I has 1 query which give an ouput like this picture :
and this is my query :
select to_char(aa.DATE_AWAL, 'dd/mm/yyyy hh24:mi') DATE_AWAL, to_char(aa.DATE_AKHIR, 'dd/mm/yyyy hh24:mi') DATE_AKHIR,
to_char(aa.DATE_AWAL, 'hh24:mi') TIME_AWAL, to_char(aa.DATE_AKHIR, 'hh24:mi') TIME_AKHIR,
cc.NAMARUANG,aa.IDMEETING from TMEETING_ROOM aa
inner join MMEETING_TYPE bb on aa.IDTYPE=bb.IDMEETING
inner join MMEETING_ROOM cc on aa.IDMEETINGROOM = cc.IDMEETINGROOM
inner join HR.VWKARYAWAN dd on aa.IDPENGUSUL=dd.IDKARYAWAN
inner join HR.MLOKASI ee on aa.IDLOKASI = ee.IDLOKASI
where aa.IS_DELETE IS NULL
and aa.IDCANCEL IS NULL
and (
wm_overlaps (
wm_period(aa.DATE_AWAL, aa.DATE_AKHIR),
wm_period(
TO_DATE(TO_CHAR(trunc(sysdate) + 08/24, 'yyyy-mm-dd hh24:mi'), 'yyyy-mm-dd hh24:mi'),
TO_DATE(TO_CHAR(trunc(sysdate) + 23/24, 'yyyy-mm-dd hh24:mi'), 'yyyy-mm-dd hh24:mi')
)
) = 1
) and aa.idlokasi = 'I' order by cc.NAMARUANG asc, aa.DATE_AWAL asc;
Can any body give me suggestion how to make from this query can like this picture:
I'm newbie using oracle SQL
Note: the time and room are dynamic.
Here is an example of how you might achieve a "generic" pivot table in MySQL.
The technique used requires row numbering (and in v8 of MySQL there will be an easier way to do this) but for now it requires using #variables.
Then, with each row number, we "transform" rows to columns using case expressions inside he max() function (conditional aggregates).
You will need to decide how many columns you need, and note that the order by inside the subquery t is vital to successfully arranging the data.
SQL Fiddle
MySQL 5.6 Schema Setup:
CREATE TABLE YourQueryHere
(`id` int, `date_column` datetime, `code_column` varchar(7), `data_for_cells1` varchar(5), `data_for_cells2` varchar(5))
;
INSERT INTO YourQueryHere
(`id`, `date_column`, `code_column`, `data_for_cells1`, `data_for_cells2`)
VALUES
(1, '2017-11-14 00:00:00', 'Bintang', '09:00', '10:30'),
(2, '2017-11-14 00:00:00', 'Bintang', '11:00', '12:30'),
(3, '2017-11-14 00:00:00', 'Bintang', '14:00', '17:00'),
(4, '2017-11-14 00:00:00', 'Sapporo', '11:30', '14:00'),
(5, '2017-11-14 00:00:00', 'Sapporo', '14:30', '15:00'),
(6, '2017-11-14 00:00:00', 'Tiger', '08:00', '09:30'),
(7, '2017-11-14 00:00:00', 'Tiger', '11:00', '12:00')
;
Query 1:
select
code_column
, max(case when RowNumber = 1 then concat(data_for_cells1, ' ', data_for_cells2) end) as pivcol1
, max(case when RowNumber = 2 then concat(data_for_cells1, ' ', data_for_cells2) end) as pivcol2
, max(case when RowNumber = 3 then concat(data_for_cells1, ' ', data_for_cells2) end) as pivcol3
, max(case when RowNumber = 4 then concat(data_for_cells1, ' ', data_for_cells2) end) as pivcol4
from (
select *
, #counter :=IF(#prev=code_column,#counter+1,1)AS RowNumber
, #prev := code_column
from YourQueryHere
cross join (select #counter:=0, #prev:= '') vars
order by
code_column, date_column
) t
group by
code_column
order by
code_column
;
Results:
| code_column | pivcol1 | pivcol2 | pivcol3 | pivcol4 |
|-------------|-------------|-------------|-------------|---------|
| Bintang | 09:00 10:30 | 11:00 12:30 | 14:00 17:00 | (null) |
| Sapporo | 11:30 14:00 | 14:30 15:00 | (null) | (null) |
| Tiger | 08:00 09:30 | 11:00 12:00 | (null) | (null) |

Finding duplicate records in a specific date range

I have a table where I have 4 columns
Serial(nvarchar), SID(nvarchar), DateCreated(Date), CID(unique and int)
I want to find the records where there is duplicate serial and SID and where the 2 duplicate serial fall between date range of 180 days.
please help
Sample Data
Serial SID DateCreated CID
02302-25-0036 HONMD01 2017-05-01 00:00:00.000 1
02302-25-0036 HONMD01 2017-05-01 00:00:00.000 3
0264607 HONMD01 2017-05-01 00:00:00.000 65
0264607 HONMD01 2016-05-01 00:00:00.000 45
03118-09-0366 PRIVA00 2016-05-20 00:00:00.000 34
03118-09-0366 PRIVA00 2016-05-20 00:00:00.000 87
0969130 140439 2017-05-09 00:00:00.000 32
0969130 140439 2017-05-09 00:00:00.000 23
1049567 INIIL00 2017-04-12 00:00:00.000 76
create table #Test (Serial nvarchar(20), [SID] nvarchar(10), DateCreated datetime, CID int)
Insert into #Test values ('02302-25-0036', 'HONMD01', '2017-05-01 00:00:00.000', 1)
, ('02302-25-0036', 'HONMD01', '2017-05-01 00:00:00.000', 3)
, ('0264607', 'HONMD01', '2017-05-01 00:00:00.000', 65)
, ('0264607', 'HONMD01', '2016-05-01 00:00:00.000', 45)
, ('03118-09-0366', 'PRIVA00', '2016-05-20 00:00:00.000', 34)
, ('03118-09-0366', 'PRIVA00', '2016-05-20 00:00:00.000', 87)
, ('0969130', '140439', '2017-05-09 00:00:00.000', 32)
, ('0969130', '140439', '2017-05-09 00:00:00.000', 23)
, ('1049567', 'INIIL00', '2017-04-12 00:00:00.000', 76)
select distinct a.*
from
(
select t.*
from #Test t
inner join (
Select Serial, [SID]
from #Test
group by Serial, [SID]
Having count(*)>=2
) d on d.Serial = t.Serial and t.SID= t.SID
) a
full outer join
(
select t.*
from #Test t
inner join (
Select Serial, [SID]
from #Test
group by Serial, [SID]
Having count(*)>=2
) d on d.Serial = t.Serial and t.SID= t.SID
) b on a.Serial = b.Serial and a.SID= b.SID
where datediff(d,a.DateCreated, b.DateCreated)<180
Try to do this:
with cte as (
select
serial,
sid,
dateCreated,
cid,
coalesce(max(dateCreated) over(partition by serial, sid order by cid, dateCreated asc rows between unbounded preceding and 1 preceding), '1900-01-01') as last,
coalesce(min(dateCreated) over(partition by serial, sid order by cid, dateCreated asc rows between 1 following and unbounded following), '5999-01-01') as next
from table_name
)
select *
from cte
where
datediff(day, last, dateCreated) >= 180
and datediff(day, dateCreated, next) >= 180
This was a challenging question ! I have left final output with *(PreviousDate, rno) for easy understanding. Here is my way to solve :
Create table #t(Serial nvarchar(100),SID nvarchar(100),DateCreated date,CID int)
Insert into #t values
('02302-25-0036', 'HONMD01', '2017-05-01 00:00:00.000', 1),
('02302-25-0036', 'HONMD01', '2017-05-01 00:00:00.000', 3),
('0264607', 'HONMD01', '2017-05-01 00:00:00.000', 65),
('0264607', 'HONMD01', '2016-05-01 00:00:00.000', 45),
('03118-09-0366', 'PRIVA00', '2016-05-20 00:00:00.000', 34),
('03118-09-0366', 'PRIVA00', '2016-05-20 00:00:00.000', 87),
('0969130', '140439', '2017-05-09 00:00:00.000', 32),
('0969130', '140439', '2017-05-09 00:00:00.000', 23),
('1049567', 'INIIL00', '2017-04-12 00:00:00.000', 76)
Select iq2.*
FROM
(Select iq.Serial, iq.SID, iq.DateCreated, iq.CID, iq.PreviousDate,
ROW_NUMBER() OVER (PARTITION BY iq.Serial,iq.SID, CASE WHEN DATEDIFF(day, iq.DateCreated, iq.PreviousDate) <= 180 THEN 1 ELSE 0 END
ORDER BY Serial,SID) rno
FROM
(select Serial,SID,DateCreated,CID,
MAX(DateCreated) OVER (PARTITION BY Serial,SID ORDER BY Serial,SID) maxDate,
DATEADD(day,-180,MAX(DateCreated) OVER (PARTITION BY Serial,SID ORDER BY Serial,SID)) PreviousDate
from #t
)iq
)iq2
where iq2.rno <> 1
output :
Serial SID DateCreated CID PreviousDate rno
---------- ------- ---------- ---- ----------- ----
02302-25-0036 HONMD01 2017-05-01 3 2016-11-02 2
03118-09-0366 PRIVA00 2016-05-20 87 2015-11-22 2
0969130 140439 2017-05-09 23 2016-11-10 2
PS : PreviousDate is MAX PreviousDate

Need help in sql query of Time Management system

I have 2 tables 1 is EMployeeMaster and another is Attendance
***EmployeeMaster***
EmployeeId EmployeeName DepartmentId
1 ABC 1
2 XYZ 2
3 PQR 2
4 WXY 1
Now i have another table Attendance
***Attendance***
AttendanceId EmployeeId Date InTime OutTime
1 1 2011-04-04 00:00:00 10:00 AM 6:30 PM
2 2 2011-04-04 00:00:00 09:45 AM 7:10 PM
Once employee comes in office and put his finger on device his entry will be go to Attendance table with InTime ,EMployeeId and Date.
So the employee who is not come into office ,his entry will not exist in the Attendance table.
Now i want to generate daily report..It should show attendance of all the employee of company along with their Intime/outTime by date.
All the employees who are absent they also should be displayed in this report.
So i want :
EmployeeId EMployeeName DepartmentId Date InTime OutTime
1 ABC 1 2011-04-04 00:00:00 10:00 AM 6:30 PM
2 XYZ 2 2011-04-04 00:00:00 09:45 AM 7:10 PM
3 PQR 2 NULL/- NULL/- NULL/-
4 WXY 1 NULL/- NULL/- NULL/-
Can you tell me what should be query???
You should use a left outer join.
declare #E table (EmployeeId int, EmployeeName varchar(50), DepartmentId int)
declare #A table (AttendanceId int, EmployeeId int, [Date] date, InTime time, OutTime time)
insert into #E values
(1, 'ABC', 1),
(2, 'XYZ', 2),
(3, 'PQR', 2),
(4, 'WXY', 1)
insert into #A values
(1, 1, '2011-04-04 00:00:00', '10:00 AM', '6:30 PM'),
(2, 2, '2011-04-04 00:00:00', '09:45 AM', '7:10 PM')
select
E.EmployeeId,
E.EmployeeName,
E.DepartmentId,
A.[Date],
A.InTime,
A.OutTime
from #E as E
left outer join #A as A
on E.EmployeeId = A.EmployeeId and
A.[Date] = '2011-04-04 00:00:00'
Result
EmployeeId EmployeeName DepartmentId Date InTime OutTime
----------- -------------------------------------------------- ------------ ---------- ---------------- ----------------
1 ABC 1 2011-04-04 10:00:00.0000000 18:30:00.0000000
2 XYZ 2 2011-04-04 09:45:00.0000000 19:10:00.0000000
3 PQR 2 NULL NULL NULL
4 WXY 1 NULL NULL NULL
Edit 1
If you need to do this for one month I would use some kind of number table or calendar table. Here I have used a cte to build the calendar using #FromDate and #ToDate
declare #E table (EmployeeId int, EmployeeName varchar(15), DepartmentId int)
declare #A table (AttendanceId int, EmployeeId int, [Date] date, InTime time, OutTime time)
insert into #E values
(1, 'ABC', 1),
(2, 'XYZ', 2),
(3, 'PQR', 2),
(4, 'WXY', 1)
insert into #A values
(1, 1, '2011-04-02', '04:00 AM', '4:30 PM'),
(2, 2, '2011-04-02', '05:00 AM', '5:30 PM'),
(3, 1, '2011-04-03', '06:00 AM', '6:30 PM'),
(4, 2, '2011-04-03', '07:00 AM', '7:30 PM'),
(5, 1, '2011-04-04', '08:00 AM', '8:30 PM'),
(6, 2, '2011-04-05', '09:00 AM', '9:10 PM')
-- Set FromDate to first day of month
declare #FromDate date = '20110401'
-- Set ToDate to last day of month
declare #ToDate date = '20110405'
-- Create cte with all dates between FromDate and ToDate
;with cteCal as
(
select #FromDate as [Date]
union all
select dateadd(d, 1, [Date]) as [Date]
from cteCal
where [Date] < #ToDate
)
select
E.EmployeeId,
E.EmployeeName,
E.DepartmentId,
C.[Date],
A.InTime,
A.OutTime
from cteCal as C
cross join #E as E
left outer join #A as A
on E.EmployeeId = A.EmployeeId and
C.[Date] = A.[Date]
order by C.[Date], E.EmployeeName
option (maxrecursion 0)
Should look like this:
select
EmployeeId,
EmployeeName,
DepartmentId,
Date,
InTime,
OutTime
from EmployeeMaster em
left join Attendance a on em.EmployeeId=a.EmployeeId and Date='2011-04-04 00:00:00'
select E.EmloyeeId, EmployeeName, DepartmentId, Date, InTime, OutTime
from EmployeeMaster as e
LEFT JOIN Attendance as a ON a.EmloyeeId = e.EmloyeeId