I have 2 tables with structure as
Emp Table
id name
001 Smith
002 Jerry
Leave
sr.no reason from_date to_date request_by status
1 PL 2011-12-11 2011-12-15 001 Declined
2 PL 2011-11-13 2011-11-13 001 Approved
3 PL 2011-10-02 2011-10-05 002 Declined
Now I have written this query
select DATEDIFF(Leave.from_date,Leave.to_date)as cdate,
Emp.id as emp
from Leave left join Emp
on Leave.request_by=Emp.id
gives me difference between these 2 dates like...
cdate emp
-4 001
0 001
-3 002
The first thing about this output difference between '2011-12-11 & 2011-12-15 ' need to be 5 as for 5 consecutive days employee is absent. That we achieve it.
But I need this cdate in date format like('%Y%m%d') and + if date difference is say -4 then 4 records should be displayed for that.
So I want to write a query which gives output like this......
cdate emp
2011-12-11 001
2011-12-12 001
2011-12-13 001
2011-12-14 001
2011-12-15 001
2011-11-13 001
2011-10-02 002
2011-10-03 002
2011-10-04 002
2011-10-05 002
So can anybody tell me what how should I need to write my query to get this output?
Try this query -
CREATE TABLE temp_days(d INT(11));
INSERT INTO temp_days VALUES
(0),(1),(2),(3),(4),(5),
(6),(7),(8),(9),(10),
(11),(12),(13),(14),(15); -- maximum day difference, add more days here
SELECT l.from_date + INTERVAL td.d DAY cdate, e.id emp
FROM
`leave` l
LEFT JOIN Emp e
ON l.request_by = e.id
JOIN temp_days td
ON DATEDIFF(l.to_date, l.from_date) >= td.d
ORDER BY
e.id
Related
I have the following table:
id
date
type
001
2022-01-01
A
001
2022-01-03
B
001
2022-01-02
B
001
2022-02-02
A
002
2022-01-01
A
002
2022-01-03
B
004
2022-01-01
A
004
2022-01-03
B
And I need to sort the dates decending, group by ID and Type and get the time between the dates by ID and Type, either in seconds or months days
id
date
type
time diff
001
2022-01-01
A
0
001
2022-01-02
B
1
001
2022-01-03
B
1
001
2022-02-02
A
31
002
2022-01-01
A
0
002
2022-01-03
B
2
004
2022-01-01
A
0
004
2022-01-03
B
2
We can use DATEDIFF() here along with the LAG() analytic function:
SELECT id, date, type,
DATEDIFF(date, LAG(date, 1, date) OVER (PARTITION BY id ORDER BY date)) AS diff
FROM yourTable
ORDER BY id, date;
If you are using MySQL 8.0,window functions are strongly recommended. Otherwise,you might have to go a long way. Here is the query written and tested in workbench using 5.7 :
select tb1.id,tb1.date,tb1.type,ifnull(datediff(tb1.date,tb2.date),0) as 'time diff'
from
(select id,date,type, #row_id:=#row_id+1 as row_id
from
(select id,date,type
from test
group by id,date,type
order by id,date)t1,
(select #row_id:=0) t
) tb1
left join
(select id,date,type, #row_num:=#row_num+1 as row_num
from
(select id,date,type
from test
group by id,date,type
order by id,date)t2,
(select #row_num:=0) t
) tb2
on tb1.id=tb2.id and tb1.row_id-tb2.row_num=1
order by tb1.id,tb1.date
;
hello please help me to solve this,, i am using mysql.. and I think, needing a full outer join to solve it,.
this is my query:
SELECT
e.NIK,
e.name,
a.dt
FROM
employee e
LEFT JOIN
attandence a
ON e.NIK=a.NIK
WHERE
month(a.dt)=12 AND year(a.dt)=2021
GROUP BY
e.NIK
this is the result:
NIK
name
dt
001
ana
23/12/2021
001
ana
24/12/2021
001
ana
26/12/2021
001
ana
27/12/2021
002
susi
23/12/2021
002
susi
24/12/2021
002
susi
25/12/2021
002
susi
26/12/2021
but i need to join it one more time with this table :
holidayTable
id
mark
dt
1
off_day
22/12/2021
2
chrismast
25/12/2021
I've tried using left/right/cross join, but it doesn't work
The result I want is like this:
NIK
name
dtWork
holiday
001
ana
null
22/12/2021
001
ana
23/12/2021
null
001
ana
24/12/2021
null
001
ana
null
25/12/2021
001
ana
26/12/2021
null
001
ana
27/12/2021
null
002
susi
null
22/12/2021
002
susi
23/12/2021
null
002
susi
24/12/2021
null
002
susi
25/12/2021
25/12/2021
002
susi
26/12/2021
null
You need a calendar table to achieve the output.
As of now I used union between holidaytable and attendance as Calendar.
You can achieve your desired result with below query.
SELECT e.nik,
e.name,
a.dt AS dtWork,
h.dt AS holiday
FROM employee e
CROSS JOIN(SELECT dt
FROM holidaytable
UNION
SELECT dt
FROM attendance)cal
LEFT JOIN attendance a
ON e.nik = a.nik
AND a.dt = cal.dt
LEFT JOIN holidaytable h
ON h.dt = cal.dt
WHERE a.dt IS NOT NULL
OR h.dt IS NOT NULL
ORDER BY nik ASC,
cal.dt ASC
SQLFiddle: Try it here
A union where the first part gets attendence and the second gets holidays (with as you thought a cross join) including a dummy column to assist ordering
select e.id,employeelastname,a.dt attendence,null holiday,a.dt as orderdt
from employees e
left join attendence a on a.nik = e.id
union
select e.id,employeelastname,null,h.dt holiday,h.dt
from employees e
cross join holidays h
order by id,orderdt;
How to do this in Mysql to get all users even no records or absent on that selected date range?
attendance_tbl
ID
user_id
time_in
time_out
created_at
1
001
2022-01-01 08:00:00
2022-01-01 17:00:00
2022-01-03 08:00:00
2
002
2022-01-01 08:15:24
2022-01-01 17:00:00
2022-01-03 08:15:24
3
003
2022-01-02 08:44:55
2022-01-02 17:00:00
2022-01-04 08:44:55
4
004
2022-01-03 08:40:22
2022-01-03 17:00:00
2022-01-04 08:40:22
users_tbl
ID
user_id
f_name
1
001
John Doe
2
002
Jane Doe
3
003
Ronal Black
4
004
Lucy White
Expected Output Daterange : from 2022-01-01 to 2022-01-03
Will get all the Users Fullname
ID
user_id
Date
f_name
time_in
time_out
created_at
1
001
Jan 1 2022
John Doe
2022-01-01 08:00:00
2022-01-01 17:00:00
2022-01-03 08:00:00
2
002
Jan 1 2022
Jane Doe
2022-01-01 08:15:24
2022-01-01 08:15:24
2022-01-03 08:00:00
3
003
Jan 1 2022
Ronal Black
4
004
Jan 1 2022
Lucy White
5
001
Jan 2 2022
John Doe
6
002
Jan 2 2022
Jane Doe
7
003
Jan 2 2022
Ronal Black
2022-01-02 17:00:00
2022-01-02 17:00:00
2022-01-02 17:00:00
8
004
Jan 2 2022
Lucy White
9
001
Jan 3 2022
John Doe
10
002
Jan 3 2022
Jane Doe
11
003
Jan 3 2022
Ronal Black
12
004
Jan 3 2022
Lucy White
2022-01-04 17:00:00
2022-01-04 17:00:00
2022-01-04 17:00:00
Given that you want to include the absent data, we need to start by getting the date range for the desired period. Using a user variable to store and increment a counter value is a performant way of doing this -
SELECT
'2022-01-01' + INTERVAL #row_number DAY `date`,
#row_number := #row_number + 1
FROM `attendance_tbl`, (SELECT #row_number := 0) AS `x`
LIMIT 31 /* 31 days in January */
If you have a table with a contiguous integer sequence (auto-incremented PK without deletes), you could use that instead -
SELECT '2022-01-01' + INTERVAL (`id` - 1) DAY `date`
FROM `attendance_tbl`
WHERE `id` <= 31 /* 31 days in January */
ORDER BY `id` ASC
We then add a cross join to build the full set of dates and users -
SELECT *
FROM (
SELECT '2022-01-01' + INTERVAL (`id` - 1) DAY `date`
FROM `attendance_tbl`
WHERE `id` <= 31
ORDER BY `id` ASC
) d
CROSS JOIN `users_tbl` `u`
By cross joining between these two tables we get the cartesian product (all combinations of the two sets). We then just take it a step further by using a left join to the attendance data -
SELECT
`u`.`user_id`,
DATE_FORMAT(`d`.`date`, '%b %e %Y') `date`,
`u`.`f_name`,
`a`.`time_in`,
`a`.`time_out`
FROM (
SELECT
'2022-01-01' + INTERVAL (`id` - 1) DAY `date`,
(SELECT TIMESTAMP(`date`, '00:00:00')) `begin`,
(SELECT TIMESTAMP(`date`, '23:59:59')) `end`
FROM `attendance_tbl`
WHERE `id` <= 31
ORDER BY `id` ASC
) d
CROSS JOIN `users_tbl` `u`
LEFT JOIN `attendance_tbl` `a`
ON `u`.`user_id` = `a`.`user_id`
AND `a`.`time_in` BETWEEN `d`.`begin` AND `d`.`end`
ORDER BY `d`.`date`, `u`.`user_id`
If your attendance_tbl can have more than 1 row per user per day then you will need to add GROUP BY d.date, u.user_id and aggregate_functions in the select list.
I have added begin and end to the derived table. This is to allow for index use for the join. This is not important while the attendance_tbl is small but will matter more as the table grows. Adding an index on (user_id, time_in) will make a huge difference to performance in the longer term.
Here's a db<>fiddle for you to play with.
To run this from PHP using PDO you could do something like this -
<?php
$pdo = new PDO($dsn, $user, $password);
$sql = "SELECT
`u`.`user_id`,
DATE_FORMAT(`d`.`date`, '%b %e %Y') `date`,
`u`.`f_name`,
`a`.`time_in`,
`a`.`time_out`
FROM (
SELECT
:START_DATE + INTERVAL (`id` - 1) DAY `date`,
(SELECT TIMESTAMP(`date`, '00:00:00')) `begin`,
(SELECT TIMESTAMP(`date`, '23:59:59')) `end`
FROM `attendance_tbl`
WHERE `id` <= :DAYS_RANGE
ORDER BY `id` ASC
) d
CROSS JOIN `users_tbl` `u`
LEFT JOIN `attendance_tbl` `a`
ON `u`.`user_id` = `a`.`user_id`
AND `a`.`time_in` BETWEEN `d`.`begin` AND `d`.`end`
ORDER BY `d`.`date`, `u`.`user_id`";
$stmt = $pdo->prepare($sql);
$startDate = new DateTime('2022-01-01');
$endDate = new DateTime('2022-02-01');
$interval = $startDate->diff($endDate, true);
$daysRange = $interval->days + 1;
// Execute the statement
$stmt->execute([
':START_DATE' => $startDate->format('Y-m-d'),
':DAYS_RANGE' => $daysRange]
);
$attendance = $stmt->fetchAll(PDO::FETCH_OBJ);
Check this. In here I call the attendance_tbl twice, one for creating a list of date and users and the other for fetching the data (time in and time out). And by using BETWEEN as #nnichols suggested to filter the selected range you prefer which I just realized earlier.
select u.`user_id`, date(a.time_in) as `date`, u.`f_name`, b.`time_in`, b.`time_out`, b.created_at from attendance_tbl a
join users_tbl u
left join attendance_tbl b on b.`user_id`=u.`user_id` and date(b.`time_in`)=date(a.`time_in`)
WHERE DATE(a.time_in) BETWEEN '2022-01-01' AND '2022-01-31'
GROUP BY `date`, u.user_id;
RESULT
user_id date f_name time_in time_out created_at
------- ---------- ----------- ------------------- ------------------- ---------------------
001 2022-01-01 John Doe 2022-01-01 08:00:00 2022-01-01 17:00:00 2022-01-03 08:00:00
002 2022-01-01 Jane Doe 2022-01-01 08:15:24 2022-01-01 17:00:00 2022-01-03 08:15:24
003 2022-01-01 Ronal Black (NULL) (NULL) (NULL)
004 2022-01-01 Lucy White (NULL) (NULL) (NULL)
001 2022-01-02 John Doe (NULL) (NULL) (NULL)
002 2022-01-02 Jane Doe (NULL) (NULL) (NULL)
003 2022-01-02 Ronal Black 2022-01-02 08:44:55 2022-01-02 17:00:00 2022-01-04 08:44:55
004 2022-01-02 Lucy White (NULL) (NULL) (NULL)
001 2022-01-03 John Doe (NULL) (NULL) (NULL)
002 2022-01-03 Jane Doe (NULL) (NULL) (NULL)
003 2022-01-03 Ronal Black (NULL) (NULL) (NULL)
004 2022-01-03 Lucy White 2022-01-03 08:40:22 2022-01-03 17:00:00 2022-01-04 08:40:22
For the ID column just create a table with AUTO_INCREMENT id and insert your selected data.
To format your date (if you really really need to) like the one in your example result, just change the DATE(a.time_in) to DATE_format(a.time_in, '%b %d %Y').
SQL Fiddle Example
I have example data table from attendance machine system like this:
ID Name In
1 John 2015-04-17 08:00:00
1 John 2015-04-17 16:30:00
1 John 2015-04-20 10:01:00
1 John 2015-04-21 10:00:00
1 John 2015-04-21 19:00:00
Here my query:
SELECT a.id AS ID, a.nama AS `Name`, DATE_FORMAT(a.att, '%d-%b-%y') AS `Date`,
DATE_FORMAT(a.att, '%T') as `IN`, DATE_FORMAT(b.att, '%T') AS `OUT`
FROM attendance a
INNER JOIN attendance b
on date(a.att) = date(b.att) AND a.id = b.id
WHERE b.att > a.att
With above query I can split the In (column) time into 2 column become In and out like this:
Id Name Date In Out
1 John 17-Apr-15 08:00:00 16:30:00
1 John 21-Apr-15 10:00:00 19:00:00
The problem is if user John forget to use finger print where he out, for example on date 20 April, the data is not display. So, I want to my output become like this:
Id Name Date In Out
1 John 17-Apr-15 08:00:00 16:30:00
1 John 20-Apr-15 10:01:00 NULL
1 John 21-Apr-15 10:00:00 19:00:00
You should use a LEFT JOIN instead of an INNER JOIN. Or you can use this query instead:
SELECT
Id,
Name,
DATE(att) AS `Date`,
DATE_FORMAT(MIN(att), '%T') AS `In`,
CASE WHEN MAX(att)<>MIN(att) THEN DATE_FORMAT(MAX(att), '%T') END AS `Out`
FROM
attendance
GROUP BY
Id, Name, DATE(att)
Please see a fiddle here.
I have a table that looks like this:
posid sales eid
1 20 001
1 20 002
1 30 001
2 30 001
1 30 002
2 30 001
1 30 002
2 20 002
2 10 002
I want to write a query that would give me sum of sales for each employee on particular pos. the result needs to be like this.
pos id emp id sales
1 001 50
1 002 80
2 001 60
2 002 30
How would I do this?
Use group by:
select t.posid
, t.eid
, sum(t.sales) as sales_by_posid
from mytable t
group by t.posid, t.eid
order by sales_by_posid desc
SELECT
posID, empID, sum(sales)
FROM your_table
GROUP BY posID, empID
ORDER BY posID, empID
Here's a group by tutorial: http://www.tizag.com/mysqlTutorial/mysqlgroupby.php