I have table with start_date and end_date. I need to find the duration (end_date-start_date). Can someone suggest how I can do so in the query? Will I get a new variable with this somehow like duration=end_date-start_date in the query?
EDIT
If I use mminus it gives me:
2012-07-01 minus 2012-01-01 = 600
How can it be 600 days in 6 months as 2011-07-27 - 2011-07-06 = 21? So i assume it's days?
Is there function to get actually how many months it even if date is in middle of month.
e.g. like "3rd june" and "27 july" is 2 month
use PERIOD_DIFF
Returns the number of months between periods P1 and P2. P1 and P2 should be in the format YYMM or YYYYMM. Note that the period arguments P1 and P2 are not date values. so try
SELECT PERIOD_DIFF(
DATE_FORMAT('2011-07-27','%Y%m'),
DATE_FORMAT('2011-06-03','%Y%m')
) AS durationInMonths
If you want to know DateTime different, you can use TimeDiff
select
(Hour(Duration) / 24)/365 as Year,
(Hour(Duration) / 24)%365 as Day,
(Hour(Duration) % 24) as Hours,
MINUTE(Duration) as Minutes,
SECOND(Duration) as Seconds
from
(
SELECT ADDTIME(NOW(),'1000:27:50') as end_datetime, NOW() as start_datetime,
TIMEDIFF(ADDTIME(NOW(),'1000:27:50'), NOW()) AS Duration
) x;
If you want to know Date different, you can use DateDiff()
select DATEDIFF('2011-06-06','2011-05-01');
You can use * but to get duration, you need to add extra column after it
select *, DATEDIFF(updated_at, created_at) from users;
TO have the best practice, it is better naming u for user, duration for extra column.
So you can see Duration title at the top. It is good if you export your sql procedure to excel.
select u.*, DATEDIFF(updated_at, created_at) as duration from users u;
Also from your php or rails code, you can call that given variable name. In rails,
users =User.find_by_sql("select u.*, DATEDIFF(updated_at, created_at) as duration from users u")
users.first.duration
SELECT end_date - start_date AS duration FROM table ...
Related
I am using PHP to access a MySQL database. I have a table built up like this:
Table headers:
id (INT, auto increment), profileid, timestamp
Table content:
1, 12345678, 1513814399 (= 12/21/2017)
2, 13451983, 1513814400 (= 12/21/2017)
3, 12345678, 1513944000 (= 12/22/2017)
4, 12345678, 1513944001 (= 12/22/2017)
The table shows which profileids have been called by a website visitor at which time.
So my question is now, how is it possible to show for example:
"Give me the number of entries for profile no. 12345678 called on 12/22/2017", which would be "2" in this case.
I tried it with this query:
SELECT COUNT(profileid), from_unixtime(timestamp, '%d') AS day, from_unixtime(timestamp, '%m') as month, from_unixtime(timestamp, '%Y') as year WHERE profileid='12345678' AND day=22 AND month=12 AND year=2017;
But it is not possible to access the columns "day", "month" and "year" because they to not exist in the table.
Can someone give me a tip how to do this? Another way would be to create three new columns (timestamp_day, timestamp_month and timestamp_year), but that's not a nice solution.
Thank you in advance!
teha
Just produce the date. I think you want:
SELECT COUNT(profileid)
FROM t
WHERE DATE(from_unixtime(timestamp)) = '2017-12-22' AND
profileid = '12345678';
I would be more inclined to write this as:
SELECT COUNT(profileid)
FROM t
WHERE profileid = '12345678' AND
timestamp >= UNIX_TIMESTAMP('2017-12-22') AND
timestamp < UNIX_TIMETAMP('2017-12-23');
This allows the query to make full use of an index on t(profileid, timestamp).
You can use MySQLs DAY, MONTH, and YEAR functions combined with FROM_UNIXTIME.
SELECT COUNT(profileid)
WHERE profileid='12345678'
AND DAY(FROM_UNIXTIME(timestamp))=22
AND MONTH(FROM_UNIXTIME(timestamp))=12
AND YEAR(FROM_UNIXTIME(timestamp))=2017;
A few things here.
You can convert your raw timestamp to a MySQL TIMESTAMP object with FROM_UNIXTIME(timestamp). You already know that.
Once you have a TIMESTAMP you can use all sorts of date functions on it.
You can convert the other direction with UNIX_TIMESTAMP()
When you're looking up records for one day you can do date range searching.
So your query should maybe be
SELECT COUNT(*) cnt
FROM t
WHERE profileid = '12345678'
AND timestamp >= UNIX_TIMESTAMP('2017-12-22')
AND timestamp < UNIX_TIMESTAMP('2017-12-23')
That will pick up every timestamp value on the day you want, up to but not including midnight on the next day. If you have an index on (profileid, timestamp) this kind of query will be fast.
Note you can also do
SELECT COUNT(*) cnt, profileid
FROM t
WHERE timestamp >= UNIX_TIMESTAMP('2017-12-22')
AND timestamp < UNIX_TIMESTAMP('2017-12-23')
GROUP BY profileid
and get a result set showing the counts for all profile ids for that day. And, you can do
SELECT COUNT(*) cnt, profileid, DATE(FROM_UNIXTIME(timestamp)) day
FROM t
WHERE timestamp >= UNIX_TIMESTAMP('2017-11-01')
AND timestamp < UNIX_TIMESTAMP('2017-12-01')
GROUP BY profileid, DATE(FROM_UNIXTIME(timestamp))
and get everything for November.
You can do this
SELECT COUNT(*) cnt, profileid, LAST_DAY(FROM_UNIXTIME(timestamp)) month_ending
FROM t
WHERE timestamp >= UNIX_TIMESTAMP('2017-01-01')
AND timestamp < UNIX_TIMESTAMP('2018-01-01')
GROUP BY profileid, LAST_DAY(FROM_UNIXTIME(timestamp))
and get a month-by-month summary for a whole year.
Date arithmetic is useful. That's why many table designs use actual datestamp-like fields, like DATETIME and TIMESTAMP, rather than raw integer timestamps.
I have the following code and query:
//$month is an array of datetime objects
foreach($month as $key => $indMonth){
$formattedMonth[] = $month[$key]->format('Y-m-d');
}
$formattedMonths = implode("','",$formattedMonth);
$query = "SELECT id,date FROM table WHERE date in ('$formattedMonths') ORDER by date DESC";
The database holds dates for the past 450 days, but it is imperfect and there are some missing days. The point of the script is to retrieve data from the current day of the month and then the corresponding day on the five previous months, but I need a failsafe for when a date happens to be missing.
How can I modify this query so it picks either the date in the "where in" portion of the query or it finds the date nearest to that particular date in the array?
Is this best to do in the query, or am I better off returning a more complete data set, then using PHP to find out if the date I want is available?
MySQL offers some decent date arithmetic. For example, if you have the date '2015-11-10' (10-Nov-2015) you can get the same day three months prior with this expression:
'2015-11-10` - INTERVAL 3 MONTH
That will kick back '2015-08-10', which is what you want.
This date arithmetic works predictably even with longer and shorter months, and with leap years. For example,
'2015-03-31' - INTERVAL 1 MONTH, '2016-03-31' - INTERVAL 1 MONTH
gives back '2015-02-28', '2016-02-29' as you might expect. And
'2015-03-31' - INTERVAL 2 MONTH, '2016-03-31' - INTERVAL 2 MONTH
gives back '2015-03-31', '2016-03-31'. Perfect.
Now, only you can decide whether this predictable behavior is correct for your application: only you know what you want to do with the previous five months of data, when the day in question is near the end of the month.
Let's assume it's correct and move on. Here is a subquery that can be used to generate a sequence of six dates, one day per month ending today.
SELECT CURDATE() - INTERVAL seq.seq MONTH day_of_month
FROM ( SELECT 0 AS seq UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL
SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 6) seq
We can use this little query as a subquery, and LEFT JOIN it to your data. That would work like this:
SELECT id, day_of_month
FROM (
SELECT CURDATE() - INTERVAL seq.seq MONTH day_of_month
FROM ( SELECT 0 AS seq UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL
SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 6) seq
) days
LEFT JOIN table ON table.date = days.day_of_month
This is a cool way to do it because you'll always get at least one row in the resultset for each date in the list, even if there's nothing matching in table.
The closest date gets a little hairier. It's possible to write a query like that. But MySQL lacks a WITH clause so the query is ridiculously repetitive.
I have two tables, one called check_ins and another called holidays.
check_ins has a datetime_start and datetime_end columns (in addition to other stuff that isn't needed for this question). The holidays table has a date range of two columns for the start and end of the holiday.
I need to figure out who was in the day prior to and the day directly after the holiday range to determine who gets holiday pay. In other words, I need only results from the table that the same employee was in one day before and one day after, ignoring the rest.
I've been racking my brain all day trying to figure out a way to do this and have found nothing. Am I barking up the wrong tree here? Should I do this via PHP?
Thanks!
Edit: this is what I used and though I had it until I realized that their might be multiple check-ins in a single day:
SELECT DISTINCT count(check_ins.Employee_ID), check_ins.ShiftStart_Datetime, check_ins.ShiftEnd_Datetime, holidays.* FROM check_ins, holidays WHERE holidays.ID = 2 AND DATE(DATE_ADD(Datefrom, INTERVAL -1 DAY)) = DATE(ShiftStart_Datetime) GROUP BY Employee_ID HAVING count(check_ins.Employee_ID) >1 UNION SELECT DISTINCT check_ins.Employee_ID, check_ins.ShiftStart_Datetime, check_ins.ShiftEnd_Datetime, holidays.* FROM check_ins, holidays WHERE DATE(DATE_SUB(Dateto, INTERVAL -1 DAY)) = DATE(ShiftStart_Datetime) GROUP BY Employee_ID HAVING count(check_ins.Employee_ID) >1
You can (inner) join the check_ins table twice. Once for the day before the start of the holiday and once for the day after.
If the datetime_start and datetime_end may have different dates, you need to use BETWEEN. Cast both of them to a date instead of a datetime, since you don't care about the time.
Add a GROUP BY in case the employee has multiple check ins on one day.
SELECT holidays.id AS holiday_id, ci_before.employee_id FROM holidays
INNER JOIN check_ins ci_before ON holidays.holiday_start - INTERVAL 1 DAY
BETWEEN DATE(ci_before.datetime_start) AND DATE(ci_before.datetime_end)
INNER JOIN check_ins ci_after ON holidays.holiday_end + INTERVAL 1 DAY
BETWEEN DATE(ci_after.datetime_start) AND DATE(ci_after.datetime_end)
AND ci_before.employee_id = ci_after.employee_id
GROUP BY ci_before.employee_id
See the SQL fiddle here
The used tables are
CREATE TABLE `holidays` (id int, holiday_start date, holiday_end date);
CREATE TABLE `check_ins` (employee_id int, datetime_start datetime, datetime_end datetime);
holidays.id is an ID for a holiday, not an employee.
you can do it via php in 2 queries, but why would you... this'll be fun!
SELECT * FROM check_ins
LEFT JOIN holidays AS holi_before
ON (
datediff(holi_before.datetime_end,check_ins.datetime_end) <= 1
)
LEFT JOIN holidays AS holi_after
ON (
holi_before.id = holi_after.id
AND datediff(holi_after.datetime_end,check_ins.datetime_end) >= 1
)
That should about do it (might need to tweak the datediff comparison a bit to get it exactly right.)
haven't test it, but i hope it gives you the right push :-)
I have the following tables (simplified):
hours hour_rates
- user_id - user_id
- date - date
- hours - hourly_rate
Hours table example:
1 - 2012-03-19 - 8
This means that user with id=1, at 2012-03-19 worked 8 hours in total.
The hourly rate for a person can change in time, so I have the second table:
hour_rates table Example
1 - 2011-12-01 - 20
1 - 2011-12-20 - 25
So for user with id=1, we set a hourly rate of 20$ at 2011-12-01.
We changed his hourly rate at 2011-12-20, to 25$.
What I want is, to calculate how much I have to pay for a given user (ex. id=1) for a given period (ex. 2012-01-01 -> 2012-02-01).
Can I calculate this simply mysql side?
If not, how to do it in an efficient way?
In hour_rates table you should have two dates: start_date and end_date this means from start_date to end_date the emplyer has been paid x$ per hour.
Then use the same query proposed by bpgergo modified like this:
select sum(h.hours * hr.hourly_rate) as pay
from hours h, hour_rates hr
where h.user_id = :user_id --here you will set the user id parameter
and h.user_id = hr.user_id and (h.date BETWEEN hr.start_date AND hr.end_date
and h.date between STR_TO_DATE('01,1,2012','%d,%m,%Y') and STR_TO_DATE('01,1,2011','%d,%m,%Y')
Can I calculate this simply mysql side?
Yes, this is the SQL
select sum(outer_h.hours *
(select inner_hr.hourly_rate
from hour_rates inner_hr
where inner_hr.user_id = outer_h.user_id
and inner_hr.date >= outer_h.date
order by inner_hr.date asc
limit 1)
) as pay
from hours outer_h
where outer_h.user_id = :user_id --here you will set the user id parameter
and outer_h.date between STR_TO_DATE('01,1,2012','%d,%m,%Y') and STR_TO_DATE('01,1,2011','%d,%m,%Y')
EDIT: So, sorry but what do you need 2 tables for that? They are totally equal, if every day can have its own hourly_rate, you only need to store it like this:
hours: user_id, date, hours, hourly_rate
e.g. 1 | 2012-03-19 | 8 | 25
You use
SELECT user_id, date, hours, hourly_rate FROM hours WHERE (user_id=$var_of_user_id AND date ...)
and then multiply for each row hours*hourly_rate and add it to $sum, e.g.
while {...
$sum=$sum+($row['hours']*$row['hourly_rate']);
}
what does the hourly_rate depend on? probably you don't want to have the field date in the hour_rates table.
if hourly_rate is different for each job, you want to have only one table with user_id, start_date, end_date (or hours worked) and hourly_rate.
if hourly_rate depends only on user_id, then you want to have two tables:
hours: user_id, start_date, end_date (or hours worked)
hourly_rate: user_id, hourly_rate
and join the tables on user_id. If you already have a table users, you could store the hourly_rate there too, in the second case.
Then use php to simply multiply the hours worked with the hourly rate where user_id... etc.
This query should work:
SELECT SUM( h.hours*COALESCE(hr.hourly_rate,0) ) AS salary
FROM hours h
LEFT JOIN hour_rates hr
ON h.user_id = hr.user_id
AND hr.date = ( SELECT MAX(hd.date) FROM hour_rates hd
WHERE hd.user_id = h.user_id AND hd.date <= h.date )
WHERE h.user_id = 1
AND h.date BETWEEN '2012-01-01' AND '2012-04-01'
The coalesce part is there just in case the hourly pay is not defined for a given user and date, you can put a default rate there instead of 0. Also make sure that every pair (user_id,date) in your hour_rates table is unique.
This query has baffled me... I've searched the web work over a day now and I have tried numerous things.
I want to get the avg number of orders for every day of the week from my db. I can pull the total # with COUNT just fine. But I just can't figure out how to get the AVG of COUNT on a GROUP BY. I've tried subqueries... functions... everything... nothing works... maybe someone can throw me a bone.
Here is the query I started with below. I know AVG(COUNT(*)) won't work but I'll leave it at that because it shows what I want to do.
SELECT
AVG(COUNT(*)) AS avgorders,
SUM(total) AS ordertotal,
DAYNAME(STR_TO_DATE(order_time,'%m/%d/%Y %H:%i')) AS day
FROM data
GROUP BY day
ORDER BY DAYOFWEEK(STR_TO_DATE(order_time,'%m/%d/%Y %H:%i')) ASC
To get the average you don't need the grand totals for each day, you need multiple daily totals for each day.
Day | Count
__________________
Monday 5
Tuesday 4
Monday 6
Tuesday 3
... ...
Then you can average those numbers. I.e (5+6)/2 for Monday.
Something like this should work:
SELECT day_of_week, AVG(order_count) average_order FROM
(
SELECT DAYNAME(order_date) day_of_week,
DAYOFWEEK(order_date) day_num,
TO_DAYS(order_date) date,
count(*) order_count
FROM data
GROUP BY date
) temp
GROUP BY day_of_week
ORDER BY day_num
UPDATE: I was originally wrong. Group the inner SELECT by the actual date to get the correct daily totals. For instance, you need to get how many orders happened Monday (2/1/10) and Monday (2/8/10) separately. Then average those totals by the day of the week.
This will do, assuming that order_time is date or datetime field ( everyone would be hapier this way ;) ). Of course there is some approximation, because oldest order can be in Friday and newest in Monday, so amount of every day of week isn't equal, but creating separate variable for every day of week will be pain in the ass. Anyway I hope it will be helpful for now.
SET #total_weeks = (
SELECT
TIMESTAMPDIFF(
WEEK,
MIN(order_time),
MAX(order_time)
)
FROM data
);
SELECT
DAYNAME(order_time) AS day_of_week,
( COUNT(*) / #total_weeks ) AS avgorders,
COUNT(*) AS total_orders
FROM
data
GROUP BY
DAYOFWEEK(order_time)
I know this is old, but i was searching for a similar solution hoping to find something someone else had used. In hopes of not doing a sub query, i came up with the below and would love any feed back!
SELECT dayofweek(`timestamp`) as 'Day',count(`OrderID`)/count(DISTINCT day(`timestamp`)) as 'Average' FROM `Data` GROUP BY dayofweek(`timestamp`)
The idea is to divide the total orders on a given day of the week, by the total number of "Mondays" or whatever day it is. What this does not account for would be any days that had zero orders would not lower the average. That may or may not be desired depending on the application.
What you are asking doesn't make sense to me... AVG is an aggregate function and so is COUNT. What's wrong with the query above but just use: COUNT(*) AS avgorders?
Lets say you had 3 rows for day1, 2 rows for day2, 5 rows for day3, and 9 rows for day4... do you want to get back a single row result that tells you:
avgorders = (3 + 2 + 2 + 5 + 9) / 5 = 21 / 5 = 4.2
ordertotal = (3 + 2 + 2 + 5 + 9) = 21
I don't think you can get that in a single query, and you'd be better off doing the second round of aggregation in a server side language like PHP operating on the results of the first aggregation.