mysql join rows with from only 1 id - mysql

I am attempting to query two tables to create a monthly employee time report. When I join the tables using INNER JOIN (emp_nr is the same in both tables) then I get the following. Please excuse me if I have not the correct formatting.
Table employee
id | emp_nr | name | vorname | address | city .....bla bla bla
Table zeit (szeit and ezeit are datetime Y-m-d H:i:s)
id | emp_nr | szeit | ezeit |
my query
$result = mysqli_query($conn,"
SELECT e.vorname
, e.name
, e.emp_nr
, a.szeit
, a.ezeit
FROM employee e
JOIN zeit a
WHERE a.szeit BETWEEN '$jahr-$monat-01' AND '$jahr-$monat-01' + INTERVAL 1 MONTH - INTERVAL 1 DAY
AND e.emp_nr = 57
ORDER
BY e.emp_nr
, a.szeit
");
my results are the following: Here I get all rows of all employees with the name and emp_nr from only one
Juli Zimmerm 57 2018-02-01 09:45:37 2018-02-01 14:15:08
Juli Zimmerm 57 2018-02-01 18:00:00 2018-02-01 22:30:00
Juli Zimmerm 57 2018-02-01 19:33:00 2018-02-01 19:44:00
Juli Zimmerm 57 2018-02-02 10:31:25 2018-02-02 14:30:25
In the code when I change
ON e.emp_nr = 57
to
ON a.emp_nr = 57
I get the following results.
Everyone listed individually but all the times are the same and the requested emp_nr is not there.
Frank Cas 01 2018-02-01 19:33:00 2018-02-01 19:44:00
Julie Cas 03 2018-02-01 19:33:00 2018-02-01 19:44:00
Lisa Ket 15 2018-02-01 19:33:00 2018-02-01 19:44:00
Anja Fis 22 2018-02-01 19:33:00 2018-02-01 19:44:00
What I am trying to get is a report where only the correct ID
with the correct name comes out.
for the simplest SELECT
SELECT emp_nr, szeit, ezeit
FROM zeit
WHERE szeit BETWEEN '2018-02-01' AND '2018-02-01' +
INTERVAL 1 MONTH - INTERVAL 1 DAY
AND emp_nr = 57
The result is without the corresponding name from table employee
57 2018-02-01 19:33:00 2018-02-01 19:44:00

When using JOINS you need to specify the relationship between the 2 tables using ON clause
SELECT e.vorname, e.name, e.emp_nr, a.szeit, a.ezeit
FROM zeit a
JOIN employee e ON a.emp_nr = e.emp_nr
WHERE a.szeit BETWEEN '2018-02-01' AND '2018-02-01' +
INTERVAL 1 MONTH - INTERVAL 1 DAY
AND e.emp_nr = 57
ORDER BY e.emp_nr, a.szeit

Related

Get all the Attendance and Fullname even no attendance on date with daterange mysql

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

MySQL - How to find records which date is between start date to end date

I have two tables and trying to find records between start_date & end_date.
campaign table
campaign_id campaign_name start_date end_date
*********** ************* ********** ********
1 Deacon Navarro 2015-10-28 00:00:00 2015-10-31 00:00:00
2 Emily Oliver 2015-10-29 00:00:00 2015-11-04 00:00:00
statistic table
id campaign_id comments likes created_date
** *********** ******** ***** ************
1 1 14 24 2015-10-28 00:00:00
2 1 34 12 2015-10-29 00:00:00
3 1 23 12 2015-10-30 00:00:00
4 1 23 24 2015-10-31 00:00:00
5 1 21 45 2015-11-01 00:00:00
6 2 12 17 2015-10-31 00:00:00
7 2 23 12 2015-11-01 00:00:00
Now I want to find all records from statistic table where campaign_id=1 and created_date is between created_date to end_date from campaign table.
I need this output:
1 1 14 24 2015-10-28 00:00:00
2 1 34 12 2015-10-29 00:00:00
3 1 23 12 2015-10-30 00:00:00
4 1 23 24 2015-10-31 00:00:00
I have written very basic select query to find start_date & end_date from campaign table
SELECT start_date, end_date FROM campaign WHERE campaign_id = '1'
and I got this result:
start_date end_date
********** ********
2015-10-28 00:00:00 2015-10-31 00:00:00
but now I don't know how to find records from statistic table where created_date is between above start_date & end_date
Hope you are understand. I am not good with MySQL because I have just stared to learning so I need help If possible :)
Thanks.
Try this :
SELECT statistic.*
FROM statistic, campaign
WHERE
campaign.campaign_id = '1'
AND campaign.campaign_id = statistic.campaign_id
AND statistic.created_date BETWEEN campaign.start_date AND campaign.end_date
try this way
SELECT statistic.*
FROM statistic
INNER JOIN campaign ON statistic.campaign_id= campaign.campaign_id;
WHERE statistic.created_date BETWEEN campaign.start_date AND campaign.end_date
Try this :
SELECT s.*
FROM statistic AS s
INNER JOIN campaign AS c ON s.campaign_id = c.campaign_id;
WHERE c.campaign_id = 1
AND s.created_date BETWEEN c.start_date AND c.end_date
For more information about JOIN syntax here.

mysql complex query for monthly report

employee makes entry in the following table when starting new task
from home or office
[tablename=CHECK]
c_id c_sdate c_emp c_task
-------------------------------------------------
1 2013-05-01 01:01:00 1 26 //date 01 from home-----
2 2013-05-01 08:11:00 1 27 //date 01 from office--- Present
3 2013-05-02 03:41:00 1 28 //date 02 from home---
4 2013-05-02 09:12:00 1 29 //date 02 from office-
5 2013-05-02 22:32:00 1 30 //date 02 from home---Present
6 2013-05-03 01:43:00 1 31 //date 03 from home
7 2013-06-03 23:25:00 1 32 //date 03 from home----------Homework
8 2013-06-03 02:15:00 2 33 //other employee
an employe will be considered as present if there 1 or many records where time between 8am and 8pm
an employe will be considered as workedFromHome if there 1 or many records where time NOT between 8am and 8pm, and not present on that day
note: do not count a day as workedFromHome if there is any record time between 8am and 8pm (means workedFromHome is only counted if he is not resent on that day)
I want to display monthly report of a employee eg. c_emp=1 for month eg. 5
like this in 1 query
c_emp presentCount HW_Count
1 3 1
or separatly query 1
c_emp presentCount
1 3
and query 2
c_emp HW_Count
1 1
I have tried for counting present working fine
select count(distinct(date_format(c_sdate,'%e'))) as count
from ita_check
where date_format(c_sdate,'%m')=5
and c_emp=1
and date_format(c_sdate,'%H%i')>=800
and date_format(c_sdate,'%H%i')<=2000
and for counting fromHome giving wrong count
select count(distinct(date_format(c_sdate,'%e'))) as count
from ita_check
where date_format(c_sdate,'%m')=5
and c_eid=1
and c_id not in (
select c_id
from ita_check
where date_format(c_sdate,'%m')=5
and c_eid=1
and (date_format(c_sdate,'%H%i')<=800 or date_format(c_sdate,'%H%i')>=2000)
)
and date_format(c_sdate,'%H%i')<800
or date_format(c_sdate,'%H%i')>2000
in above query for counting Working
the sub query returns 1 and 2
while the outer eliminate c_id=2 but not c_id=1
Try this query
SELECT c_emp,
sum(if(cnt>=1,1,0)) as Office,
count(*)-sum(if(cnt>=1,1,0)) as WFH from (
select c_emp, Date(c_sdate),
sum(if(c_sdate BETWEEN Date(c_sdate) + interval 8 hour
AND Date(c_sdate) + interval 20 hour, 1, 0)) as cnt
from table1
group by c_emp, Date(c_sdate)) tmp
group by c_emp
SQL FIDDLE:
| C_EMP | OFFICE | WFH |
------------------------
| 1 | 2 | 2 |
| 2 | 0 | 1 |
For monthly report
SELECT c_emp, date_format(c_date, '%c %Y') as Mnth,
sum(if(cnt>=1,1,0)) as Office,
count(*)-sum(if(cnt>=1,1,0)) as WFH from (
select c_emp, Date(c_sdate) as c_date,
sum(if(c_sdate BETWEEN Date(c_sdate) + interval 8 hour
AND Date(c_sdate) + interval 20 hour, 1, 0)) as cnt
from table1
group by c_emp, Date(c_sdate)) tmp
group by c_emp,Mnth
SQL FIDDLE:
| C_EMP | MNTH | OFFICE | WFH |
---------------------------------
| 1 | 5 2013 | 2 | 1 |
| 1 | 6 2013 | 0 | 1 |
| 2 | 6 2013 | 0 | 1 |

MySql subquerying to pull data from another table

I am currently using phpMyAdmin supporting MySql 5.0.
I have the following two tables:
Master Facebook Insights
------------------------
Client ID Client Name Date Top Likes By Gender
--------- ----------- ------ --------------------
1 Client1 2012-01-01
1 Client1 2012-02-01
2 Client2 2012-01-01
2 Client2 2012-02-01
...etc. (the dates are always the beginning of each month for however many months
& clients exist)
Likes By Demographic
----------------
Date Demographic1 Demographic2 Demographic3 Client
------ ------------ ------------ ------------ ------
0000-00-00 M.35-44 M.45-54 M.25-34 1
2012-01-01 53 44 28 1
2012-01-02 53 46 29 1
...etc.
0000-00-00 M.18-24 M.35-44 M.25-34 1
2012-02-01 374 221 194 1
2012-02-02 374 222 195 1
...etc.
0000-00-00 F.35-44 F.25-34 M.35-44 2
2012-01-01 194 182 83 2
2012-01-02 194 182 83 2
...etc.
0000-00-00 F.35-44 F.25-34 M.35-44 2
2012-02-01 196 180 83 2
2012-02-02 197 180 83 2
...etc.
For the Likes By Demographic table:
All days of each month are listed per client; when a new month begins,
there is a new set of demographics that may or may NOT be listed exactly the same
as the previous month. This is because the data was imported from a CSV that put the demographics in order of highest 'likes,' so this usually changes month to month. This is also the reason that the individual demographics are not the column headers (because they are inconsistent).
My problem is the following:
I wish to list the top 3 demographics per month for each client in the 'Top Likes By Gender' column of the first table, like so:
Client ID Client Name Date Top Likes By Gender
--------- ----------- ------ --------------------
1 Client1 2012-01-01 M.35-44, M.45-54, M.25-34
1 Client1 2012-02-01 M.18-24, M.35-44, M.25-34
2 Client2 2012-01-01 F.35-44, F.25-34, M.35-44
2 Client2 2012-02-01 F.35-44, F.25-34, M.35-44
The use of subqueries is confusing me. The following is the (incorrect) code I have been trying to fix. The problem is that is just extracts the first three demographics for the first client (M.35-44, M.45-54, M.25-34) and repeats them down the entire column for all clients and dates.
Am I on the right track, or is there a much simpler/more correct way to do this?
UPDATE `Master Facebook Insights`
SET `Top Likes By Gender` =
(select * from
(SELECT DISTINCT CONCAT(`Demographic1`,', ',`Demographic2`,', ',`Demographic3`)
FROM `Likes By Demographic` t1
JOIN `Master Facebook Insights` t2
ON t1.`Client` = t2.`Client ID`
WHERE t1.`Date` = '0000-00-00'
AND t2.`Date` =
(
SELECT MIN(`Date`)
FROM `Likes By Demographic`
WHERE `Date` > '0000-00-00'
AND `Client` = t2.`Client ID`
GROUP BY `Date`
ORDER BY `Date` ASC
LIMIT 1
)
)inner1 limit 1)
Thank you so much for the help!
Just using the original Likes_By_Demographic table, you can do:
select
client_id,
date,
concat(demo1,', ',demo2,', ',demo3) `top_likes`
from likes_by_demographic
group by client_id, date
order by client_id, date;
...which will yield (pseudo-data):
| CLIENT_ID | DATE | TOP_LIKES |
-----------------------------------------------------------------------
| 1 | January, 01 2012 00:00:00-0800 | M.35-44, M.25-34, 195 |
| 1 | February, 01 2012 00:00:00-0800 | M.25-34, M.35-44, 195 |
| 1 | March, 01 2012 00:00:00-0800 | M.25-34, 195, 169 |
| 2 | January, 01 2012 00:00:00-0800 | F.45-54, 210, 195 |
| 2 | February, 01 2012 00:00:00-0800 | F.45-54, 210, 75 |

How to get available time for doctor from todays appointments?

I am working on a project in a company. I am trying to solve this query but I could not.
My tables are:
Appointments:
doctorId patientId patientName fromDateTime toDateTime
-------- --------- ----------- --------------------- ---------------------
56 1 fghfgh 3/23/2012 12:15:00 PM 3/23/2012 01:15:00 PM
56 2 asdadf 3/23/2012 01:15:00 PM 3/23/2012 02:15:00 PM
56 3 werwr 3/23/2012 09:15:00 AM 3/23/2012 10:15:00 AM
57 4 uiyui 3/23/2012 09:15:00 AM 3/23/2012 10:15:00 AM
57 5 asdad 3/23/2012 01:15:00 PM 3/23/2012 02:15:00 PM
This is my timeSchedule table:
id startTime endTime
-- ------------ ------------
1 08:00:00.000 09:00:00.000
2 09:00:00.000 10:00:00.000
3 11:00:00.000 12:00:00.000
4 12:00:00.000 13:00:00.000
5 13:00:00.000 14:00:00.000
6 14:00:00.000 15:00:00.000
7 15:00:00.000 16:00:00.000
Actually there are more values but I think these are enough to solve the problem.
I am comparing patient appointments with this timeSchedule table.
Now suppose if I pass parameter doctorId as 56 and consider today is 23 March then output should be like this:
id startTime endTime
-- --------- --------
1 08:00 AM 09:00 AM
3 11:00 AM 12:00 PM
6 02:00 PM 03:00 PM
7 03:00 PM 04:00 PM
How can I achieve the above result?
Assuming that timeSchedule.startTime and timeSchedule.endTime are both Time data types then it would be something like this...: (if not, you could cast as such).
DECLARE #pDoctorID Int = 56
DECLARE #pDate Date = '3/23/2012'
SELECT * FROM timeSchedule WHERE
NOT Exists (
SELECT doctorid FROM Appointments
WHERE doctorid = #pDoctorID AND
CAST(fromDatetime as Date) = #pDate AND
(
(CAST(fromDatetime as Time) >= timeSchedule.startTime AND
CAST(fromDatetime as Time) <= timeSchedule.endTime)
OR
(CAST(toDatetime as Time) >= timeSchedule.startTime AND
CAST(toDatetime as Time) <= timeSchedule.endTime)
OR
(CAST(toDatetime as Time) <= timeSchedule.startTime AND
CAST(fromDatetime as Time) >= timeSchedule.endTime)
)
)
Which with your sample data returns this:
1 | 08:00:00.00 | 09:00:00.00
4 | 11:00:00.00 | 12:00:00.00
8 | 15:00:00.00 | 16:00:00.00
In essence the query is saying give me any appointment for this doctor where existing appoints do not start or end between the time frames, or start before and end after any of the time slots defined by the timeSchedule table.
Formatting the return times is also a simple matter. See the table in this link for all your options.