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
Related
People travel in pairs. How to find the maximum and minimum number of days between trips every user?
People:
id
user
1
Harry
2
George
3
Thomas
4
Jacob
5
Jack
6
Oliver
Travels:
id
date
user1
user2
1
2005-10-03
2
3
2
2005-10-04
1
4
3
2005-10-05
5
6
4
2005-10-06
1
3
5
2005-10-07
2
4
6
2005-10-08
3
5
7
2005-10-10
1
4
8
2005-10-11
5
2
9
2005-10-15
6
4
I tried to solve this problem in the following way, but I still do not understand how to solve this problem:
select People.id,People.user, count(*)
from People
INNER join
(SELECT MIN(TIMESTAMPDIFF(day, t1.date, t2.date)) as mintime,max(TIMESTAMPDIFF(day, t1.date, t2.date))
from Travels as t1
join Travels as t2 on t1.PERSON_1 = t2.PERSON_1
WHERE t1.date< t2.date
GROUP BY t1.PERSON_1) as t3
group by People.id
There is an idea to use the position function to iterate over each user, and then, as a result, look at the dates and find the minimum and maximum, but I still don't understand how to do this
Best is to do it in steps with subqueries, as below (comments are in the query):
select user, max(dateDiff)
from (
select
user,
-- get the diff between previous and current row date to get diff between trips
datediff(date, lag(date) over (partition by user order by date)) dateDiff
from (
-- full flat list of all users and trip dates
select date, user1 `user`
from testtbl
union all
select date, user2
from testtbl
) a
) a group by user
SQL fiddle
Note that I used windowed function which are not avaiable in MySql 5 and below.
I have the following table that I would like to compare
I have to compare dates per employee and if the date range is less than 60 days it has to extract the employee.
example:
the employee with number 4 has 3 records (4479,4192,1982)
The combination of these 3 records (4479-4192, 4479-1982, 4192-1982) the dates must be compared and if one of them is less than 60 days, the employee must be extracted.
below the example table:
http://sqlfiddle.com/#!9/b44d4e
id
idEmployee
date
1228
2
2020-06-11 21:10:53
3382
2
2020-06-11 12:37:04
2223
2
2020-08-17 21:10:57
4479
4
2020-08-17 12:37:08
4192
4
2020-07-29 12:37:08
1982
4
2020-07-29 21:10:56
2627
8
2020-04-16 12:37:02
474
8
2020-04-16 21:10:49
1002
10
2020-05-29 21:10:52
3150
10
2020-05-29 12:37:04
pd: mysql database
Any help or suggestion how should I make the query?
Using an exists approach:
SELECT DISTINCT idEmployee
FROM yourTable t1
WHERE EXISTS (SELECT 1 FROM yourTable t2
WHERE t2.idEmployee = t1.idEmployee AND
DATEDIFF(t2.date, t1.date) > 60);
In plain English, the above query says to return any employee record for which we can find another record of the same employee more than 60 days apart. The distinct select removes duplicates.
You can use a self-join
SELECT DISTINCT a.idEmployee
FROM employees AS a
JOIN employees AS b ON a.idEmployee = b.idEmployee AND DATEDIFF(a.date, b.date) > 60
But instead of comparing all with all, you really only need to compare the most extreme dates with each other. If they're less than 60 days apart, all the other dates must also be less.
SELECT idEmployee
FROM employees
GROUP BY idEmployee
HAVING DATEDIFF(MAX(a.date), MIN(a.date)) > 60
one way is to use exists
select * from table t1
where exists (
select 1 from table t2
where t1.idEmployee = t2.idEmployee
and t2.Id <> t1.id
and abs(datediff(t2.date, t1.date)) < 60)
How to get those entries which have more than 1 records?
If it doesn't make sense... let me explain:
From the below table I want to access the sum of the commission of all rows where type is joining and "they have more than 1 entry with same downmem_id".
I have this query but it doesn't consider more entries scenario...
$search = "SELECT sum(commission) as income FROM `$database`.`$memcom` where type='joining'";
Here's the table:
id mem_id commission downmem_id type time
2 1 3250 2 joining 2019-09-22 13:24:40
3 45 500 2 egbvegr new time
4 32 20 2 vnsjkdv other time
5 23 2222 2 vfdvfvf some other time
6 43 42 3 joining time
7 32 353 5 joining time
8 54 35 5 vsdvsdd time
Here's the expected result: it should be the sum of the id no 2, 7 only
ie. 3250+353=whatever.
It shouldn't include id no 6 because it has only 1 row with the same downmem_id.
Please help me to make this query.
Another approach is two levels of aggregation:
select sum(t.commission) income
from (select sum(case when type = 'joining' then commission end) as commission
from t
group by downmem_id
having count(*) > 1
) t;
The main advantage to this approach is that this more readily supports more complex conditions on the other members of each group -- such as at most one "joining" record or both "joining" records and no more than two "vnsjkdv" records.
Use EXISTS:
select sum(t.commission) income
from tablename t
where t.type = 'joining'
and exists (
select 1 from tablename
where id <> t.id and downmem_id = t.downmem_id
)
See the demo.
Results:
| income |
| ----- |
| 3603 |
You can use subquery that will find all downmem_id having more than one occurrence in the table.
SELECT Sum(commission) AS income
FROM tablename
WHERE type = 'joining'
AND downmem_id IN (SELECT downmem_id
FROM tablename t
GROUP BY downmem_id
HAVING Count(id) > 1);
DEMO
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
I have a table with columns similar to below , but with about 30 date columns and 500+ records
id | forcast_date | actual_date
1 10/01/2013 12/01/2013
2 03/01/2013 06/01/2013
3 05/01/2013 05/01/2013
4 10/01/2013 09/01/2013
and what I need to do is get a query with output similar to
week_no | count_forcast | count_actual
1 4 6
2 5 7
3 2 1
etc
My query is
SELECT weekofyear(forcast_date) as week_num,
COUNT(forcast_date) AS count_forcast ,
COUNT(actual_date) AS count_actual
FROM
table
GROUP BY
week_num
but what I am getting is the forcast_date counts repeated in each column, i.e.
week_no | count_forcast | count_actual
1 4 4
2 5 5
3 2 2
Can any one please tell me the best way to formulate the query to get what I need??
Thanks
try:
SELECT weekofyear(forcast_date) AS week_forcast,
COUNT(forcast_date) AS count_forcast, t2.count_actual
FROM
t t1 LEFT JOIN (
SELECT weekofyear(actual_date) AS week_actual,
COUNT(forcast_date) AS count_actual
FROM t
GROUP BY weekOfYear(actual_date)
) AS t2 ON weekofyear(forcast_date)=week_actual
GROUP BY
weekofyear(forcast_date), t2.count_actual
sqlFiddle
You have to write about 30 (your date columns) left join, and the requirement is that your first date column shouldn'd have empty week (with a count of 0) or the joins will miss.
Try:
SELECT WeekInYear, ForecastCount, ActualCount
FROM ( SELECT A.WeekInYear, A.ForecastCount, B.ActualCount FROM (
SELECT weekofyear(forecast_date) as WeekInYear,
COUNT(forecast_date) as ForecastCount, 0 as ActualCount
FROM TableWeeks
GROUP BY weekofyear(forecast_date)
) A
INNER JOIN
( SELECT * FROM
(
SELECT weekofyear(forecast_date) as WeekInYear,
0 as ForecastCount, COUNT(actual_date) as ActualCount
FROM TableWeeks
GROUP BY weekofyear(actual_date)
) ActualTable ) B
ON A.WeekInYear = B.WeekInYear)
AllTable
GROUP BY WeekInYear;
Here's my Fiddle Demo
Just in case someone else comes along with the same question:
Instead of trying to use some amazing query, I ended up creating an array of date_columns_names and a loop in the program that was calling this query, and for each date_column_name, performing teh asme query. It is a bit slower, but it does work