mysql select last x days between range - php

This query returns all of the selected values from today's date to 90 days ago:
SELECT max(cases_visits.created_dt), users_profiles.account_num,
concat(cases.patient_first_initial,
cases.patient_middle_initial, cases.patient_last_initial) AS initials,
concat(users.first_name, ' ',users.last_name) as name
FROM cases
JOIN users_profiles
ON users_profiles.user_id=cases.doctor_id
JOIN cases_visits
ON cases.id=cases_visits.case_id
join users on users.id = cases.doctor_id
WHERE cases_visits.patient_visit_type = 'NP' && cases_visits.created_dt BETWEEN curdate() - INTERVAL 90 DAY AND SYSDATE()
group by users.first_name
I'd like to find a query that will now select the exact same thing, but only if records DO not exist in the previous query. EXAMPLE: return records from > 90 days ago, that do not have records in the past 90 days.
I have tried to do this: (note, 2013-07-03 in the query was 90 days from the first time i ran)
SELECT cases_visits.created_dt, users_profiles.account_num,
concat(cases.patient_first_initial,
cases.patient_middle_initial, cases.patient_last_initial) AS initials,
concat(users.first_name, ' ',users.last_name) as name
FROM cases
JOIN users_profiles
ON users_profiles.user_id=cases.doctor_id
JOIN cases_visits
ON cases.id=cases_visits.case_id
join users on users.id = cases.doctor_id
WHERE cases_visits.created_dt < '2013-07-03'
group by users.first_name
This does not give me the proper data, I think because i need to somehow exclude records that exist from the past 90 days.
THIS IS WHAT IM TRYING TO DO: Select a records with a a value = to 'NP' for the past 90 days, then i need to select records where there is not a np value for greater than 90 days, but these records should be completely unique from the first query (i.e the individual could have a case from within the 90 days, and one 180 days ago, i would not need his records.)
EDIT: I forgot to mention I have tried this query with an error near 'in':
SELECT cases_visits.created_dt, users_profiles.account_num,
concat(cases.patient_first_initial,
cases.patient_middle_initial, cases.patient_last_initial) AS initials,
concat(users.first_name, ' ',users.last_name) as name
FROM cases
JOIN users_profiles
ON users_profiles.user_id=cases.doctor_id
JOIN cases_visits
ON cases.id=cases_visits.case_id
join users on users.id = cases.doctor_id
WHERE cases_visits.created_dt < '2013-07-03'
and cases_visits.patient_visit_type = 'NP'
and not in (select created_dt from cases_visits where cases_visits.patient_visit_type = 'NP' && cases_visits.created_dt BETWEEN curdate() - INTERVAL 90 DAY AND SYSDATE())
group by users.first_name

You could use a subquery:
select * from table where ID NOT IN (select id from table where a=1);
This would essentially select the records from the table that don't match the records the inner query did match.

SELECT * FROM table WHERE cases_visits.created_dt < '2013-07-03'
AND case_visist.SOME_UNIQUE_ID NOT IN
(SELECT case_visist.SOME_UNIQUE_ID FROM table WHERE cases_visits.patient_visit_type = 'NP' && cases_visits.created_dt BETWEEN curdate() - INTERVAL 90 DAY AND SYSDATE() )
You can use NOT IN statement and enter a SELECT statement that select all the records that should be excluded. A some more info in this thread

This set should be visits for the same doctor, same cases but a different visit?
SELECT max(cases_visits.created_dt), users_profiles.account_num,
concat(cases.patient_first_initial,
cases.patient_middle_initial, cases.patient_last_initial) AS initials,
concat(users.first_name, ' ',users.last_name) as name
FROM cases
JOIN users_profiles
ON users_profiles.user_id=cases.doctor_id
JOIN cases_visits
ON cases.id=cases_visits.case_id
JOIN users
ON users.id = cases.doctor_id
WHERE cases_visits.patient_visit_type = 'NP' && cases_visits.created_dt <= curdate() - INTERVAL 90 DAY
AND EXISTS(SELECT *
FROM case_visits AS inside
WHERE inside.case_id = cases.id
AND inside.patient_visit_type = 'NP'
AND inside.created_dt BETWEEN curdate() - INTERVAL 90 DAY AND SYSDATE())
GROUP BY account_num, initials, name

Related

Check if unix timestamp is within the current month in MySQL

I have records of questions stored in database. One of the column in question is the ask_date.
Example stored in is: 1549923808.
I want to select records whose ask_date is within the current month.
Please how do I calculate that in the WHERE clause of my SQL ?
//Select questions with the heighest or Top votes or answers this month( current month)
$SQL = "SELECT * FROM(
SELECT Q.*,
(SELECT COUNT(Q.question_id) FROM $questions_table Q ) num_of_rows,
(SELECT COUNT(v.vote_id) FROM $votes_table v WHERE Q.question_id=v.ask_id AND v.vote_type=0 ) votes_down,
(SELECT COUNT(a.answer_id) FROM $answers_table a WHERE question_id=a.ask_id ) total_answers,
(SELECT COUNT(v.vote_id) FROM $votes_table v WHERE Q.question_id=v.ask_id ) votes_up,
CONCAT(m.firstname,' ',m.lastname) author_name,
m.username u_name FROM $questions_table Q
LEFT JOIN $main_table m ON Q.user_id=m.user_id
WHERE ............
) A LEFT JOIN $votes_table V ON A.question_id=V.ask_id
GROUP BY A.question_id ORDER BY (
SELECT COUNT(V.vote_id) vote
FROM $votes_table V
WHERE V.ask_id=A.question_id
) DESC LIMIT {$maxPageRecords} OFFSET {$offset}";
A simple way is:
where date_format(from_unixtime(ask_date), '%Y-%m') = date_format(now(), '%Y-%m')
However, that precludes the use of indexes. So, this might be better:
where ask_date >= unix_timestamp(date(concat_ws('-', year(now()), month(now()), 1)))) and
ask_date < unix_timestamp(date(concat_ws('-', year(now()), month(now()), 1))) + interval 1 month)
select records whose ask_date is within the current month :
This simple (and index-friendly) expression should get the job done :
WHERE ask_date >= UNIX_TIMESTAMP(DATE_FORMAT(NOW(), '%Y-%m-01'))
Using SQL Tuples:
WHERE (year(ask_date),month(ask_date)) = (year(now()),month(now()))

get monthly record of employees in one query using mysql

I am using this query for getting monthly record of employees those are present and absent.
However i am getting the result for one employee by using this query but for all employees it doesn't seems to work.
SELECT
m.emp_id AS `Empid`,
d.dt AS `AbsentDate`,
(CASE
WHEN p.punch_status IS NULL THEN 'A'
ELSE p.punch_status
END) s
FROM
(SELECT
DATE(t.added_date) AS dt
FROM
pmc_attendance t
WHERE
DATE(t.added_date) >= '2018-08-01'
AND DATE(t.added_date) < DATE_ADD('2018-08-31', INTERVAL 1 DAY)
GROUP BY DATE(t.added_date)
ORDER BY DATE(t.added_date)) d
CROSS JOIN
tbl_admin_users m
LEFT JOIN
pmc_attendance p ON DATE(p.added_date) >= d.dt
AND DATE(p.added_date) < d.dt + INTERVAL 1 DAY
AND p.emp_id = m.emp_id
WHERE
p.emp_id IS NULL AND m.emp_id = '000838'
GROUP BY d.dt
ORDER BY m.emp_id , d.dt
I am using two tables 1. tbl_admin_users- employee data stored 2. pmc_attendance- present records of employees.
in query if i have passed the and m.emp_id='000838' it works fines but i want to show all records for all employees. any suggestions how i can optimize this query.
There are a couple of ways to structure this query. I can see what yuo are doing, and I think the only issue is with your group by clauses. You dont need them as everything should be distinct. Your status will always be 'A' as you are only getting rows where there is no punch for the employee for the day, so you can also take out the case statement.
SELECT
m.emp_id AS Empid,
d.dt AS AbsentDate,
'A' s
FROM
(
SELECT distinct DATE(t.added_date) AS dt
FROM pmc_attendance t
WHERE t.added_date >= '2018-08-01' AND DATE(t.added_date) < DATE_ADD('2018-08-31', INTERVAL 1 DAY)
) d
CROSS JOIN tbl_admin_users m
LEFT JOIN pmc_attendance p ON p.emp_id = m.emp_id and DATE(p.added_date) >= d.dt AND DATE(p.added_date) < d.dt + INTERVAL 1 DAY
WHERE p.emp_id IS NULL
ORDER BY m.emp_id , d.dt
If you want to include both present and absent, you would need to put your case statement back in, and remove your check WHERE p.emp_id IS NULL
If you have multiple punchs for the day, then you need to resolve it down to a single entry with a MIN/MAX so you only get one row per person per day, and add back in your group by Emp_ID, d.dt

Min value from Database in MySQL

Am trying to find the min value from past 30 days, in my table there is one entry for every day, am using this query
SELECT MIN(low), date, low
FROM historical_data
WHERE name = 'bitcoin'
ORDER BY STR_TO_DATE(date,'%d-%m-%Y') DESC
LIMIT 7
But this value not returing the correct value. The structure of my table is
Table structure
And table data which is store is like this
Table data style
Now what i need is to get the minimum low value. But my query not working it give me wrong value which even did not exist in table as well.
Updates:
Here is my updated Table Structure.
enter image description here
And here is my data in this table which look like this
enter image description here
Now if you look at the data, i want to check the name of token omisego and fatch the low value from past 7 days which will be from 2017-12-25 to 2017-12-19
and in this cast the low value is 9.67, but my current query and the query suggested by my some member did not brings the right answer.
Update 2:
http://rextester.com/TDBSV28042
Here it is, basically i have more then 1400 coins and token historical data, which means that there will me more then 1400 entries for same date like 2017-12-25 but having different name, total i have more then 650000 records. so every date have many entries with different names.
To get the lowest row per group you could use following
SELECT a.*
FROM historical_data a
LEFT JOIN historical_data b ON a.name = b.name
AND a.low > b.low
WHERE b.name IS NULL
AND DATE(a.date) >= '2017-12-19' AND DATE(a.date) <= '2017-12-25'
AND a.name = 'omisego'
or
SELECT a.*
FROM historical_data a
JOIN (
SELECT name,MIN(low) low
FROM historical_data
GROUP BY name
) b USING(name,low)
WHERE DATE(a.date) >= '2017-12-19' AND DATE(a.date) <= '2017-12-25'
AND a.name = 'omisego'
DEMO
For last 30 day of 7 days or n days you could write above query as
SELECT a.*, DATE(a.`date`)
FROM historical_data2 a
LEFT JOIN historical_data2 b ON a.name = b.name
AND DATE(b.`date`) >= CURRENT_DATE() - INTERVAL 30 DAY
AND DATE(b.`date`) <= CURRENT_DATE()
AND a.low > b.low
WHERE b.name IS NULL
AND a.name = 'omisego'
AND DATE(a.`date`) >= CURRENT_DATE() - INTERVAL 30 DAY
AND DATE(a.`date`) <= CURRENT_DATE()
;
DEMO
But note it may return more than one records where low value is same, to choose 1 row among these you have specify another criteria to on different attribute
Consider grouping the same and running the clauses
SELECT name, date, MIN(low)
FROM historical_data
GROUP BY name
HAVING name = 'bitcoin'
AND STR_TO_DATE(date, '%M %d,%Y') > DATE_SUB(NOW(), INTERVAL 30 DAY);
Given the structure, the above query should get you your results.
// Try this code ..
SELECT MIN(`date`) AS date1,low
FROM historical_data
WHERE `date` BETWEEN now() - interval 1 month
AND now() ORDER by low ASC;

reach memory limit with loop inside loop inside loop

Disclaimer: I am aware that I have in my code deprecated mysql functions. That is on my todo list.
I have a MySql select giving me seasons for diferent items in a house booking system.
Kind of:
Low season: 2010-01-01, 2010-03-01, 100 //meaning start,end,price
This comes in my first sql:
while($season_row=mysql_fetch_assoc($season_res)){
$seasonsArray[$season_row['id_item']][] = array(
$season_row['season_start'],
$season_row['season_end'],
$season_row['daily_price']
);
}
The dates are defined here (arriving to the function as YYYY-mm-dd):
function seasonPrice($from,$to,$requested_item){
$start = round(strtotime($from)/86400)*86400; // like 2008-01-01
$end = round(strtotime($to)/86400)*86400; // to 2015-01-01
$formattedStart = date('Y-m-d', $start);
$formattedEnd = date('Y-m-d', $end);
Now I need to loop between 2 dates, between the items of the $seasonsArray and then check the price of that item in that specific day.
I did this with:
foreach($seasonsArray as $item=>$value){
for( $thisDay = $start; $thisDay < $end; $thisDay = $thisDay + 86400){
foreach($value as $innerValue){
$season_start = roundToSeconds($innerValue[0]);
$season_end = roundToSeconds($innerValue[1]);
if($thisDay >= $season_start && $thisDay <= $season_end) {
$foundPrice[] = round($innerValue[2]);
}
}
$thisSerie[] = array($thisDay * 1000, isset($foundPrice) ? $foundPrice[0] : 0);
// security check to avoid double assigned seasons to same day
if(count($foundPrice) > 1){ die('There is double bookings in item: '.$item);}
unset($foundPrice);
}
$seasonPrices[] = array(
'data'=> $thisSerie,
'label'=> 'House ID: '.$item,
);
}
But I get: Fatal error: Allowed memory size of 100663296 bytes exhausted
Any suggestion on where my code can be improved to not need so much memory? Or is there a bug and I don't see it?
I'd generate a range of days and join against your seasons table, and use a single query to get the desired resulset, e.g.:
SELECT dates.Date,
coalesce(s.price, 0) AS price
FROM
(SELECT a.Date
FROM
( SELECT curdate() - INTERVAL (a.a + (10 * b.a) + (100 * c.a)) DAY AS Date, '0' AS price
FROM
(SELECT 0 AS a
UNION ALL SELECT 1
UNION ALL SELECT 2
UNION ALL SELECT 3
UNION ALL SELECT 4
UNION ALL SELECT 5
UNION ALL SELECT 6
UNION ALL SELECT 7
UNION ALL SELECT 8
UNION ALL SELECT 9) AS a
CROSS JOIN
(SELECT 0 AS a
UNION ALL SELECT 1
UNION ALL SELECT 2
UNION ALL SELECT 3
UNION ALL SELECT 4
UNION ALL SELECT 5
UNION ALL SELECT 6
UNION ALL SELECT 7
UNION ALL SELECT 8
UNION ALL SELECT 9) AS b
CROSS JOIN
(SELECT 0 AS a
UNION ALL SELECT 1
UNION ALL SELECT 2
UNION ALL SELECT 3
UNION ALL SELECT 4
UNION ALL SELECT 5
UNION ALL SELECT 6
UNION ALL SELECT 7
UNION ALL SELECT 8
UNION ALL SELECT 9) AS c) a
WHERE a.Date BETWEEN '$from' AND '$to'
ORDER BY a.Date) dates
LEFT JOIN seasons s ON dates.Date BETWEEN s.start AND s.END
The complicated inner query avoids the creation of a temp table (taken from generate days from date range) and works for up to 1000 days, but creating a temp table would be fine.
It looks to be like you are never leaving the for loop. Where do $start and $end get set. Verify their values by printing them out.
As far as optimization, there is no need to be looping through each day. Skip the for loop that counts days and use $season_start and $season_end to calculate the day in the second foreach loop.
In fact, there is a bug right now unless $session_start and $session_end are always more than a day apart, because sometime the event will happen in between the 24 hour periods you are looping by.

MySQL Query Problem with INTERVAL, need 0 if no data provided

i have the following statement:
SELECT
count(rs.rsc_id) as counter
FROM shots as rs
where rsc_rs_id = 345354
AND YEAR(rs.timestamp) = YEAR(DATE_SUB(CURDATE(), INTERVAL 6 MONTH))
GROUP BY DATE_FORMAT(rs.timestamp,'%Y%m')
rs.timestamp is a unix timestamp
Output would be like for each row / month a numeric like '28'
It Works fine, but if i have inconsistent data, like only for the past three month (not for all six month), i get no return from my Database. I would like to have every time there is not data for this month, 0 returned...
any suggestion?
i thought about some case statements, but this seems not so good...
thanks!!
For only 6 months, a date table seems unnecessary, although this looks complicated (it really isn't!)
SELECT DATE_FORMAT(N.PivotDate,'%Y%m'), count(rs.rsc_id) as counter
FROM (
select ADDDATE(CURDATE(), INTERVAL N MONTH) PivotDate
FROM (
select 0 N union all
select 1 union all
select 2 union all
select 3 union all
select 4 union all
select 5 union all
select 6) N) N
LEFT JOIN shots as rs
ON rsc_rs_id = 345354
AND DATE_FORMAT(N.PivotDate,'%Y%m')=DATE_FORMAT(FROM_UNIXTIME(rs.timestamp),'%Y%m')
GROUP BY DATE_FORMAT(N.PivotDate,'%Y%m')
In such cases it's common to use a table of dates with all dates (e.g. from 1/1/1970 to 31/12/2999) and LEFT JOIN your data to that table.
See an example in the answer here: mysql joins tables creating missing dates
If you create a dates table you can use:
SELECT
DATE_FORMAT(d.date,'%Y%m') AS `month`, count(rs.rsc_id) AS `counter`
FROM dates d
LEFT JOIN shots as rs
ON d.date = FROM_UNIXTIME(rs.timestamp)
AND rs.rsc_rs_id = 345354
WHERE d.date > DATE_SUB(CURDATE(), INTERVAL 5 MONTH)
AND d.date < CURDATE()
GROUP BY DATE_FORMAT(d.date,'%Y%m');

Categories