Mysql Query to Merge Two Condition Into One Row - mysql

I just get confused. Already tried to search this whole site or google but didn't find the 'nearest' solution.
Ok let's say I have this table structure.
id date finger_id finger_time is_enter
1 2017-03-30 2 09:00 1
2 2017-03-30 2 17:13 0
3 2017-03-31 4 09:10 1
4 2017-03-31 3 09:01 1
5. 2017-03-31 3 17:00 0
I want to make the table to be like below.
date finger_id enter_time exit_time
2017-03-30 2 09:00 17:13
2017-03-30 4 09:10
2017-03-31 3 09:10 17:00
I have made sql statement but it turns like this.
date finger_id enter_time exit_time
2017-03-30 2 09:00
2017-03-30 2 17:13
2017-03-31 4 09:10
2017-03-31 3 09:01
2017-03-31 3 17:00
I just want to know how to merge the is_enter 1 with is_enter 0 on the same date by the finger_id column.
Here's my sql query for the reference.
SELECT *
FROM `tbl_fingerprint`
LEFT JOIN `tbl_employee` ON `tbl_employee`.`fingerprint_id`=`tbl_fingerprint`.`fingerprint_id`
LEFT JOIN `tbl_position` ON `tbl_position`.`position_id`=`tbl_employee`.`position_id`
WHERE `fingerprint_date` >= '2017-03-01'
AND `fingerprint_date` <= '2017-04-01'
GROUP BY `tbl_fingerprint`.`fingerprint_id`,
`tbl_fingerprint`.`fingerprint_date`,
`tbl_fingerprint`.`is_enter`
ORDER BY `fingerprint_date` ASC LIMIT 30
Thanks for your help guys.

You can do a group by date and finger_id fields and use conditional expression (case or if()) within an aggregate function to get the expected outcome. The conditional statements within the aggregate function make sure that they return value only if the right value is set in is_enter field. I leave out the employee details, since those do not form part of your question:
SELECT date, fingerprint_id, max(if(is_enter=1,finger_time,null) as enter_time, max(if(is_enter=0,finger_time,null) as exit_time
FROM `tbl_fingerprint`
WHERE `fingerprint_date` >= '2017-03-01'
AND `fingerprint_date` <= '2017-04-01'
GROUP BY `tbl_fingerprint`.`fingerprint_id`,
`tbl_fingerprint`.`fingerprint_date`,
ORDER BY `fingerprint_date` ASC LIMIT 30

SELECT * FROM `tbl_fingerprint`
LEFT JOIN `tbl_employee` ON `tbl_employee`.`fingerprint_id`=`tbl_fingerprint`.`fingerprint_id`
LEFT JOIN `tbl_position` ON `tbl_position`.`position_id`=`tbl_employee`.`position_id`
LEFT JOIN (SELECT * FROM tbl_fingerprint WHERE is_enter = 0) a
ON a.finger_id = tbl_fingerprint.finger_id AND a.date = tbl_fingerprint.date
WHERE `fingerprint_date` >= '2017-03-01' AND `fingerprint_date` <= '2017-04-01' AND tbl_fingerprint.is_enter = 1
GROUP BY `tbl_fingerprint`.`fingerprint_id`, `tbl_fingerprint`.`fingerprint_date`, `tbl_fingerprint`.`is_enter`
ORDER BY `fingerprint_date` ASC LIMIT 30

Try This (This will work if finger_time is of time type):-
SELECT date, finger_id, min(finger_time) enter_time, if (min(finger_time) = max(finger_time), null, max(finger_time)) exit_time FROM xyz group by finger_id, date

SELECT a1.*, a3.time as time_out FROM attendance as a1
INNER JOIN (SELECT MIN(id) as id FROM attendance where is_enter = '1' group by date, f_id ) as a2
ON a2.id = a1.id
LEFT JOIN attendance as a3 ON a3.date = a1.date AND a1.f_id = a3.f_id and a3.is_enter = '0'
you may need to cast the date to not include the time portion or to char with the yyyy-mm-dd format

Related

Get occupancy per every 15-minute slot

We have a room where we can only have XX number of people inside due to current limitations. They come at different times and stay for a different length of time.
I'm trying to get a sum of people currently inside for each 15-min period for a specific date. The server is MySQL 8.0.21 deployed on AWS RDS.
MySQL 8.0 Table: Booking
ID
Name
PartySize
Date
BookedFrom
BookedTo
1
John
2
2021-01-01
2021-01-01 08:30:00
2021-01-01 10:00:00
2
Mary
4
2021-01-01
2021-01-01 09:00:00
2021-01-01 11:00:00
3
Nick
3
2021-01-01
2021-01-01 10:30:00
2021-01-01 12:30:00
I also have a "helper table" with a time slot for each 24 hour 15-min slot
MySQL Table: Timeslot
ID
Time
1
00:00:00
2
00:15:00
3
00:30:00
35
08:30:00
37
09:00:00
38
09:15:00
For example, when I run this query below, I will get the correct count (6 people) for 09:30. What is the most efficient way to get this result for each 15-min slot? Please note that while the BookedTo (datetime field) value may be past midnight, I will always be only making date specific queries.
SELECT
t.id, b.date, t.time, SUM(b.partysize) AS total
FROM
booking b,
timeslot t
WHERE
b.date = '2021-01-01'
AND t.time = '09:15:00'
AND b.bookedfrom <= '2021-01-01 09:15:00'
AND b.bookedto >= '2021-01-01 09:15:00'
Looking for this output for all times (including zeros)
Slot_ID
Date
Time
Total
33
2021-01-01
08:00:00
0
34
2021-01-01
08:15:00
0
35
2021-01-01
08:30:00
2
36
2021-01-01
08:30:00
2
37
2021-01-01
09:00:00
6
38
2021-01-01
09:15:00
6
SELECT
t.id as slot_id,
coalesce(b.date, '2021-01-01') as date,
t.time,
coalesce(sum(b.partysize),0) as total
FROM
timeslot t
LEFT JOIN booking b
ON t.time >= TIME(b.bookedfrom) AND t.time < TIME(b.bookedto) AND b.date = '2021-01-01'
WHERE
t.time BETWEEN '08:00:00' AND '17:00:00'
GROUP BY
t.id,
b.date,
t.time
Now, you have some confusing other requirements, but basically this works because multiple rows of timeslot will match to a single row of booking because of the time range expressed.
The confusing requirements are, you say it's only for 8-5pm, but "bookings might extend to the next day".. does it mean that a booking will start at 4pm and finish at 9am the next day? in which case you might need to adjust the AND b.date = '2021-01-01' to be more like AND (DATE(b.bookedfrom) = '2021-01-01' OR DATE(b.bookedto) = '2021-01-01') ...
Use a CTE that returns the specific date for which you want the results, which may not be the same as the column Date in Booking and CROSS join it to Timeslot.
The result should be LEFT joined to Booking and then aggregate:
WITH cte(Date) AS (SELECT '2021-01-01')
SELECT t.ID, t.time, c.Date,
COALESCE(SUM(b.PartySize), 0) Total
FROM cte c CROSS JOIN Timeslot t
LEFT JOIN Booking b
ON b.BookedFrom <= CONCAT(c.Date, ' ', t.time)
AND b.BookedTo >= CONCAT(c.Date, ' ', ADDTIME(t.time, '00:15:00'))
WHERE t.time BETWEEN '08:00:00' AND '17:00:00'
GROUP BY t.ID, c.Date, t.time
Since BookedFrom and BookedTo may not contain the same date, it is not safe to compare only the time parts of the 2 columns to the column time of Timeslot.
This is why all these conditions in the ON clause are needed.
See the demo.
this query works great ... if you wanna have all dates for all slots .. you will have to have a date table too (ideally within timeslot -> cross join dates and timeslots) ...
use inner join if you wanna get only matching dates and timeslots ..
SELECT t.id as slot_id
, b.date
, t.time as slot
, sum(ifnull(party_size,0)) as total
FROM test.timeslot t
LEFT JOIN test.booking b
ON t.time BETWEEN time(b.booked_from) AND time(b.booked_to)
GROUP BY t.id
, b.date
, t.time;
for all timeslots and selected dates:
https://www.db-fiddle.com/f/gLt2Fs8HTDUakMahZHxcTi/0
for matching timeslots and dates:
SELECT t.id as slot_id
, b.date
, t.time as slot
, sum(ifnull(party_size,0)) as total
FROM test.timeslot t
JOIN test.booking b
ON t.time BETWEEN time(b.booked_from) AND time(b.booked_to)
GROUP BY t.id
, b.date
, t.time;

SQL-Case When Issue

I am trying to sort the transaction dates into an aging policy. When LastDate has been in the location for greater than Aging Days limit policy it should show up as OverAge if not Within referring to the current date.
Here is the current table:
+---------+------+----------+-------------+
|LastDate | Part | Location | Aging Days |
+---------+------+----------+-------------+
12/1/2016 123 VVV 90
8/10/2017 444 RRR 10
8/01/2017 144 PR 21
7/15/2017 12 RRR 10
Here is the query:
select
q.lastdate,
r.part, r.location,
a.agingpolicy as 'Aging Days'
from opsintranexcel r (nolock)
left InventoryAging a (nolock) on r.location=a.location
left join (select part,MAX(trandate) as lastdate from opsintran group by
part) q on r.part=q.part
Here is the extra column I want added in:
+---------+------+----------+------------+---------+
|LastDate | Part | Location | Aging Days | Age |
+---------+------+----------+------------+---------+
12/1/2016 123 VVV 90 Overage
8/10/2017 444 RRR 10 Within
8/01/2017 144 PR 21 Within
7/15/2017 12 RRR 10 Overage
I appreciate your help.
I think below code will be work for you
SELECT
q.lastdate,
r.part,
r.location,
a.agingpolicy as 'Aging Days'
'Age' =
CASE
WHEN DATEDIFF( day, q.LastDate, GETDATE() ) > a.agingpolicy THEN 'Overage'
ELSE THEN 'Within'
END
FROM opsintranexcel r (nolock)
LEFT JOIN InventoryAging a (nolock) on r.location=a.location
LEFT JOIN (
SELECT part,MAX(trandate) as lastdate
FROM opsintran
WHERE trantype='II' and PerPost>='201601'
GROUP BY part) q ON r.part=q.part
you can check the difference of the current date and the lastdate value if over or within the aging days
CASE WHEN DATEDIFF(NOW(), q.lastdate) > a.agingpolicy
THEN 'Overage'
ELSE 'Within'
END AS age
You should modify your query as:
select
q.lastdate,
r.part, r.location,
a.agingpolicy as 'Aging Days',
if(DATEDIFF(NOW(), q.lastdate)) > a.agingpolicy, 'Overage','Within') as 'Age'
from opsintranexcel r (nolock)
left InventoryAging a (nolock) on r.location=a.location
left join (select part,MAX(trandate) as lastdate from opsintran where
trantype='II' and PerPost>='201601' group by part) q on r.part=q.part

MYSQL HELP - Order of Operations and Sub Query

There are two tables I am working with (ft_form_1 & ft_field_options).
ft_form_1
submission_id facility_id admits reporting_date timestamp
1 111A 1 2017-03-31 00:00 2017-03-31 17:53:17
2 222B 3 2017-03-31 00:00 2017-03-31 18:42:20
3 333C 6 2017-03-31 00:00 2017-03-31 19:27:47
4 222B 0 2017-04-01 00:00 2017-04-01 18:12:12
5 333C 4 2017-03-31 00:00 2017-04-01 19:38:25
6 333C 5 2017-04-01 00:00 2017-04-01 20:31:16
ft_field_options
list_id option_order option_value option_name
1 4 111A New York
1 2 222B Chicago
1 1 333C Boston
1 3 444D Miami
What I am currently getting:
facility_id option_name option_order admits reporting_date timestamp
111A New York 3 1 2017-03-31 00:00 2017-03-31 17:53:17
What I want to get:
facility_id option_name option_order admits reporting_date timestamp
111A New York 3 1 2017-03-31 00:00 2017-03-31 17:53:17
222B Chicago 2 3 2017-03-31 00:00 2017-03-31 18:42:20
333C Boston 1 4 2017-03-31 00:00 2017-04-01 19:38:25
With the below query, I am trying to get a list of all submissions from a specified 'reporting_date' for each 'facility_id' with a 'list_id' equal to 1. If multiple submissions are sent on the same 'reporting-date', only the submission with the most recent 'timestamp' will be shown.
Problem: I believe this query is not running in the order I'd like it to. It seems the query is finding the max 'timestamp' for each 'facility_id' in the table and then filtering to only show submissions with a specified 'reporting_date'. I'd like that to occur in reverse order - where the query filters to only show submissions with a specified 'reporting_date' and then narrows the list down to only show the max 'timestamp' for each 'facility_id. As you can see from my above example, Chicago and Boston are being left off the list because they have a more recent 'timestamp' on the following day.
I am a MYSQL newbie, so any help is appreciated! I originally got the idea of the MAX subquery from this link: http://www.w3resource.com/sql/aggregate-functions/max-date.php
SELECT t1.facility_id, t1.admits, t1.reporting_date, t1.timestamp,
t2.option_name, t2.option_order
FROM ft_form_1 t1
LEFT JOIN ft_field_options t2
ON (t1.facility_id = t2.option_value AND t2.list_id = 1)
WHERE (DATE_FORMAT(t1.reporting_date,'%m/%d/%Y') =
DATE_FORMAT(NOW() - INTERVAL 1 DAY,'%m/%d/%Y')) AND (timestamp=(
SELECT MAX(timestamp)
FROM ft_form_1
WHERE facility_id = t1.facility_id))
ORDER BY option_order ASC
*Imagine today is April 1st 2017 for the above query to work.
The answer here is to use the structured part of structured query language. SQL is a declarative, not procedural, language, so it can be confusing to think about the order in which it does things.
First, you are looking for rows with a list_id of 1. Let's do a query to get that. Notice the use of JOIN rather than LEFT JOIN: we don't want the unmatched elements. (http://sqlfiddle.com/#!9/8bdc3c/1/0)
SELECT t1.*
FROM ft_form_1 t1
JOIN ft_field_options t2
ON t1.facility_id = t2.option_value
AND t2.list_id = 1
Next we need to find the timestamp of the last submission on each reporting date for each facility. (http://sqlfiddle.com/#!9/8bdc3c/3/0)
SELECT MAX(timestamp) timestamp,
reporting_date reporting_date,
facility_id
FROM ft_form_1
GROUP BY reporting_date, facility_id
These kinds of subqueries are a good way to go, because they're pretty easy to test.
Finally, we join these two subqueries together to get what we want. (http://sqlfiddle.com/#!9/8bdc3c/6/0)
SELECT a.*
FROM (
SELECT t1.*
FROM ft_form_1 t1
JOIN ft_field_options t2
ON t1.facility_id = t2.option_value
AND t2.list_id = 1
) a
JOIN (
SELECT MAX(timestamp) timestamp,
reporting_date reporting_date,
facility_id
FROM ft_form_1
GROUP BY reporting_date, facility_id
) b ON a.facility_id = b.facility_id
AND a.timestamp = b.timestamp
AND a.reporting_date = b.reporting_date
The trick is in the GROUP BY subquery, that finds the last timestamp for each reporting date.
Now, you have a submission_id in your table. If that submission_id is autoincrementing, then it's likely that the largest submission_id value for reporting data in each facility is the one you want. If that's true you can simplify the query a whole lot.
This gets you the submission_id valued that correspond to the submissions you want
SELECT MAX(submission_id)
FROM ft_form_1
GROUP BY reporting_date, facility_id
You can join that to your main tables like this, to get the result you want. (http://sqlfiddle.com/#!9/8bdc3c/10/0)
SELECT t1.*
FROM ft_form_1 t1
JOIN ft_field_options t2 ON t1.facility_id = t2.option_value
AND t2.list_id = 1
JOIN (
SELECT MAX(submission_id) submission_id
FROM ft_form_1
GROUP BY reporting_date, facility_id
) q ON t1.submission_id = q.submission_id

How to repeat MySQL query for multiple intervals

I've got a query which produces the proper results for a given time interval of 15 minutes.
-- Query for the interval 10:00-10:15
SELECT count(r.id) as nof_reservations_in_interval
FROM reservations r
LEFT JOIN assets a ON r.asset_id = a.id
WHERE r.deleted_at is null
AND a.type_id = 23 --just an ID
AND r.start_utc <= '2017-02-21 10:15:00'
AND r.end_utc >= '2017-02-21 10:00:00'
-- result: 2
If I want to make a 'table/relation' with the results for this query between, lets say, 10:00 and 18:00 on the same day. How could I achieve that?
I could just query the statement from php for every interval; but I hoped there was some kind of smart MySQL function to do this :)
Desired result relation:
interval_start | interval_end | nof_reservations_in_interval
---------------+--------------+------------------------------
10:00 | 10:15 | 2
10:15 | 10:30 | 3
etc etc
A simple way is to define the intervals using a subquery:
SELECT t.time_start, t.time_end, count(r.id) as nof_reservations_in_interval
FROM (SELECT time('10:10:00') as time_start, time('10:15:00') as time_end UNION ALL
SELECT time('10:15:00') as time_start, time('10:30:00') as time_end
) t LEFT JOIN
reservations r
ON r.start_utc <= addtime('2017-02-21', t.time_end) AND
r.end_utc >= addtime('2017-02-21', t.time_start) LEFT JOIN
assets a
ON r.asset_id = a.id AND
a.type_id = 23
WHERE r.deleted_at is null
GROUP BY t.time_start, t.time_end;
Note: I moved the condition on a.type_id to the on clause for the left joins to work.

How to update table after a certain time interval

How can I update a table after some time interval when a condtion is matched?
tb_contest
id contest_id name is_expire
1 101 new 0
2 102 old 0
tb_answer
contest_id answer_id date
101 1 2012-02-02
101 2 2012-09-14
102 5 2012-06-01
I need to update tb_contest after some condition was met and make is_expire=1 after 2 days on basis of the last answer received i:e 2012-03-14, so the tb_contest should be updated on 2012-09-16.
You could use MySQL's event scheduler:
CREATE EVENT expire_contests
ON SCHEDULE EVERY DAY
STARTS CURRENT_DATE
DO UPDATE tb_contest JOIN (
SELECT contest_id, MAX(date) AS latest
FROM tb_answer
GROUP BY contest_id
) t USING (contest_id)
SET tb_contest.is_expire = 1
WHERE tb_contest.is_expire <> 1
AND t.latest <= CURRENT_DATE - INTERVAL 2 DAY
Try this one,
UPDATE tb_contest a INNER JOIN
(
SELECT contest_ID, MAX(`date`) maxDate
FROM tb_answer
GROUP BY contest_ID
) b ON a.contest_ID = b.contest_ID
SET a.is_expire = 1
WHERE DATEDIFF(CURDATE(), b.maxDate) >= 2 AND
a.is_expire = 0
So here it goes, the two tables were joined by contest_ID and having the lastest answered date on tb_answer. By using DATEDIFF() we can know the difference between today's date and the date the contest has been answered.
You can JOIN the contest and an inner-query on the answer table in the UPDATE clause and use MySQL's DATEDIFF to count the number-of-days since the answer was, well, answered:
UPDATE
tb_contest c
JOIN (SELECT contest_id, MAX(date) AS date FROM tb_answer GROUP BY contest_id) AS a
ON a.contest_id = c.id
SET
c.is_expire = 1
WHERE
DATEDIFF(NOW(), a.date) >= 2