I have a dob field in my MySQL table that's of type date. Just a small, trivial example is something like this:
mysql> select dob from players limit 5;
+------------+
| dob |
+------------+
| 1983-12-02 |
| 1979-01-01 |
| 1989-05-11 |
| 1976-03-24 |
| 1989-09-12 |
+------------+
I am trying to calculate ages with decimal points by using today's date. So technically if your birthday is June 1, 1981 or 1981-06-01, that makes you 33 and today is June 7.. you'd be 33.(6/365) or 33.02 years old. What's the easiest way to calculate this using SQL?
Usually DOB calculation is pretty easy in mysql when you want to calculate the years without any fraction something as
mysql> select timestampdiff(YEAR,'1981-06-01',now());
+----------------------------------------+
| timestampdiff(YEAR,'1981-06-01',now()) |
+----------------------------------------+
| 33 |
+----------------------------------------+
But since you need the fraction also then this should do the trick
mysql> select format(datediff(curdate(),'1981-06-01') / 365.25,2);
+-----------------------------------------------------+
| format(datediff(curdate(),'1981-06-01') / 365.25,2) |
+-----------------------------------------------------+
| 33.02 |
+-----------------------------------------------------+
Year is considered as 365.25 days.
So in your case you may have the query as
select
format(datediff(curdate(),dob) / 365.25,2) as dob
from players limit 5;
You can use the to_days function to calculate the days between the year zero and someone's birthday. Then subtract from today that number of days. That should give you the birthday as if someone was born in the year zero:
select year(subdate(now(), to_days(dob)))
Example at SQL Fiddle.
There are a couple of issues. you are using 365 as the number of days in a year and ignoring leap years.
The easiest way to do this would be
select datediff( now(), '1981-06-01' )/365.25;
When you use 365.25 it will spread the leap year and you should be fine. MySQL says this value is 33.0157. when you round it off to two decimals you should be fine.
Your best bet is use of DATEDIFF() as follows:
select datediff(NOW(),date("1983-12-02"))/365;
where the quoted date is your date field, so your example would translate to:
select datediff(NOW(),date(dob))/365 from players limit 5;
The result is returned would be the number of years since the birth date to 4 decimal places.
If you prefer two decimal places use truncate() as follows:
truncate(datediff(NOW(),date("1983-12-02"))/365,2);
Related
LEFT or DATE_FORMAT, which is faster to re-format date in SELECT query in mysql?
I'll show an example of this problem.
Info of PERSON table
Column name
Type
NAME
VARCHAR(20)
YEAR
DATETIME
PERSON
NAME
YEAR
Travis
2020-01-01
Sam
2021-01-01
If execute 'SELECT YEAR FROM PERSON' query, can see below result.
YEAR
2020-01-01 00:00:00
2021-01-01 00:00:00
But I want result like below.
YEAR
2020-01-01
2021-01-01
So, I wanted use one of below queries.
SELECT LEFT(YEAR,10) FROM PERSON
SELECT DATE_FORMAT(YEAR, '%Y-%m-%d')
However, I wonder what query perform better.
Please, help me..
Technically, LEFT() is nearly four times faster, based on this test on my M1 Macbook. Your result might vary.
mysql> select benchmark(100000000, left(year, 10)) from person;
+--------------------------------------+
| benchmark(100000000, left(year, 10)) |
+--------------------------------------+
| 0 |
+--------------------------------------+
1 row in set (1.75 sec)
mysql> select benchmark(100000000, date_format(year, '%Y-%m-%d')) from person;
+-----------------------------------------------------+
| benchmark(100000000, date_format(year, '%Y-%m-%d')) |
+-----------------------------------------------------+
| 0 |
+-----------------------------------------------------+
1 row in set (6.81 sec)
But given that I had to execute both expressions 100 million times to observe a significant difference, both of them are so fast that I wouldn't worry about it. It's likely that other parts of the query will be of far greater influence on performance.
Worrying about which of these two functions has better performance is like worrying if it's better to use one finger or two fingers to lift a 100kg barbell.
I want to count how many days of a start and end-date are inside another start and end-date. Basically: How many days of the date 01.06.2020 - 06.06.2020 are inside the date 03.06.2020 - 31.12.2020. In this case the answer would be 4. The 3rd, 4th, 5th and 6th of June 2020.
I know that I can use TIMESTAMPDIFF to do calculations between 2 dates, but I can't wrap my head around a simple solution to do it with 2 dates inside 2 dates.
I can't believe nobody ever asked that question, but I can't find any solution to this.
Simple table:
+-------------+--------------+
| Start_date | End_date |
+-------------+--------------+
| 2020-06-03 | 2020-12-31 |
| 2014-09-08 | 2015-09-07 |
| 2015-01-15 | 2015-02-01 |
+-------------+--------------+
I look for something along the lines of:
SELECT * FROM available_dates WHERE TIMESTAMPDIFF(DAY,'2020-06-01','2020-06-06', Start_date, End_date) > 5
You can do this by taking the intersection of the two ranges using GREATEST on the start dates and LEAST on the end dates. Intersecting ranges will return a positive number, non-intersecting a negative one. So we use GREATEST again to zero out negative values to get the overlap. For example:
CREATE TABLE test (
`Start_date` DATE,
`End_date` DATE
);
INSERT INTO test
(`Start_date`, `End_date`)
VALUES
('2020-06-03', '2020-12-31'),
('2014-09-08', '2015-09-07'),
('2015-01-15', '2015-02-01');
SET #start = '2015-01-20';
SET #end = '2015=02-04';
SELECT GREATEST(TIMESTAMPDIFF(DAY, GREATEST(Start_Date, #start), LEAST(End_Date, #end))+1, 0) AS overlap
FROM test;
Output:
overlap
0
16
13
Demo on dbfiddle
if i want to get the total_consumption over a range of dates, how would i do that?
I thought i could do:
SELECT id, SUM(consumption)
FROM consumption_info
WHERE date_time BETWEEN 2013-09-15 AND 2013-09-16
GROUP BY id;
however this returns: Empty set, 2 warnings(0.00 sec)
---------------------------------------
id | consumption | date_time |
=======================================|
1 | 5 | 2013-09-15 21:35:03 |
2 | 5 | 2013-09-15 24:35:03 |
3 | 7 | 2013-09-16 11:25:23 |
4 | 3 | 2013-09-16 20:15:23 |
----------------------------------------
any ideas what i'm doing wrong here?
thanks in advance
You're missing quotes around the date strings: the WHERE clause should actually be written as...
BETWEEN '2013-09-15' AND '2013-09-16'
The irony is that 2013-09-15 is a valid SQL expression - it means 2013 minus 09 minus 15. Obviously, there's no date lying in between the corresponding results; hence an empty set in return
Yet there might be another, more subtle error here: you probably should have used this clause...
BETWEEN '2013-09-15 00:00:00' AND '2013-09-16 23:59:59'
... instead. Without setting the time explicitly it'll be set to '00:00:00' on both dates (as DATETIME values are compared here).
While it's obviously ok for the starting date, it's not so for the ending one - unless, of course, exclusion of all the records for any time of that day but midnight is actually the desired outcome.
SELECT SUM(consumption)
FROM consumption_info
WHERE date_time >= 2013-09-15 AND date_time <= 2013-09-16;
or
SELECT SUM(consumption)
FROM consumption_info
WHERE date_time BETWEEN 2013-09-15 AND 2013-09-16;
Its better to use CAST when comparing the date function.
SELECT id, SUM(consumption)
FROM consumption_info
WHERE date_time
BETWEEN CAST('2013-09-15' AS DATETIME)
AND CAST('2013-09-16' AS DATETIME)
GROUP BY id;
It's not actually a problem I'm having, but imagine someone's building a website about the medieval times and wants to store dates, how would they go about it?
The spec for MySQLs DATE says it won't go below the year 1000. Which makes sense when the format is YYYY-MM-DD. How can you store information about the death of Kenneth II of Scotland in 995? Of course you can store it as a string, but are there real date-type options?
Actually, you can store dates below year 1000 in MySQL despite even documentation clarification:
mysql> describe test;
+-------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| birth | date | YES | | NULL | |
+-------+---------+------+-----+---------+-------+
-you still need to input year in YYYY format:
mysql> insert into test values (1, '0995-03-05');
Query OK, 1 row affected (0.02 sec)
mysql> select * from test;
+------+------------+
| id | birth |
+------+------------+
| 1 | 0995-03-05 |
+------+------------+
1 row in set (0.00 sec)
-and you'll be able to operate with this as a date:
mysql> select birth + interval 5 day from test;
+------------------------+
| birth + interval 5 day |
+------------------------+
| 0995-03-10 |
+------------------------+
1 row in set (0.03 sec)
As for safety. I've never faced a case when this will not work in MySQL 5.x (that, of cause, does not mean that it will 100% work, but at least it is reliable with certain probability)
About BC dates (below Christ). I think that is simple - in MySQL there's no way to store negative dates as well. I.e. you will need to store year separately as a signed integer field:
mysql> select '0001-05-04' - interval 1 year as above_bc, '0001-05-04' - interval 2 year as below_bc;
+------------+----------+
| above_bc | below_bc |
+------------+----------+
| 0000-05-04 | NULL |
+------------+----------+
1 row in set, 1 warning (0.00 sec)
mysql> show warnings;
+---------+------+--------------------------------------------+
| Level | Code | Message |
+---------+------+--------------------------------------------+
| Warning | 1441 | Datetime function: datetime field overflow |
+---------+------+--------------------------------------------+
1 row in set (0.00 sec)
But I think, in any case (below/above year 0) it's better to store date parts as integers in that case - this will not rely to undocumented feature. However, you will need to operate with those 3 fields not as the dates (so, in some sense that is not a solution to your problem)
Choose a dbms that supports what you want to do. Among other free database management systems, PostgreSQL supports a timestamp range from 4713 BC to 294276 AD.
If you break up the date into separate columns for year, month, and day, you also need more tables and constraints to guarantee that values in those columns represent actual dates. If those columns let you store the value {2013, 2, 29}, your table is broken. A dbms that supports dates in your range entirely avoids this kind of problem.
Other problems you might run into
Incorrect date arithmetic on dates that are out of range.
Incorrect locale-specific formatting on dates that are out of range.
Surprising behavior from date and time functions on dates that are out of range.
Gregorian calendar weirdness.
Gregorian calendar weirdness? In Great Britain, the day after Sep 2, 1752 is Sep 14, 1752. PostgreSQL documents their rationale for ignoring that as follows.
PostgreSQL uses Julian dates for all date/time calculations. This has
the useful property of correctly calculating dates from 4713 BC to far
into the future, using the assumption that the length of the year is
365.2425 days.
Date conventions before the 19th century make for interesting reading,
but are not consistent enough to warrant coding into a date/time
handler.
Sadly, I think that currently the easiest option is to store year, month and day in separate fields with year as smallint.
To quote from http://dev.mysql.com/doc/refman/5.6/en/datetime.html
For the DATE and DATETIME range descriptions, “supported” means that although earlier values might work, there is no guarantee.
So there's a good change that a wider range will work given a sufficiently configured MySQL installation.
Make sure not to use TIMESTAMP, which seems to have a non-negative range.
The TIMESTAMP data type is used for values that contain both date and time parts. TIMESTAMP has a range of '1970-01-01 00:00:01' UTC to '2038-01-19 03:14:07' UTC.
Here is a JavaScript example how far before the UNIX epoch(1) you can get with 2^36 seconds * -1000 (to get to milliseconds for Javascript).
d = new Date((Math.pow(2, 36) - 1) * -1000)
Sun May 13 -208 18:27:45 GMT+0200 (Westeuropäische Sommerzeit)
So I would suggest to store historical dates as BIGINT relative to the epoch.
See http://dev.mysql.com/doc/refman/5.6/en/integer-types.html for MxSQL 5.6.
(1)
epoch = new Date(0)
Thu Jan 01 1970 01:00:00 GMT+0100 (Westeuropäische Normalzeit)
epoch.toUTCString()
"Thu, 01 Jan 1970 00:00:00 GMT"
I have this certain problem about mysql date functions.
I'm trying to compare the value of THIS MONTH to the given timestamp in database.
For example, month today is june, and the timestamp is 1369967316
And I'm trying to determine if that timestamp is in month of june.
$query = db_query("SELECT * FROM users WHERE MONTH(FROM_UNIXTIME(CURDATE())) = MONTH(1369967316)");
//count total members this mont
$members_month = $query->rowCount();
so if I used the rowCount, the $members_month should have the value of 1.
Unfortunately it doesn't work.
Any help would be appreciated.
Well I saw some answers that some kind of relevant to mine but it doesn't hit the spot or I didn't applied it well.
mysql get month from timestamp not working
how to use curdate() in where clause against unixtimestamp (bigint) column
This works for me:
mysql> SELECT MONTH(FROM_UNIXTIME(1369967316));
+----------------------------------+
| MONTH(FROM_UNIXTIME(1369967316)) |
+----------------------------------+
| 5 |
+----------------------------------+
Your issue is likely coming from the fact that 1369967316 is May 30th, not June (as you expect), thus resulting in an inequality with MONTH(CURDATE()).
mysql> SELECT FROM_UNIXTIME(1369967316);
+---------------------------+
| FROM_UNIXTIME(1369967316) |
+---------------------------+
| 2013-05-30 22:28:36 |
+---------------------------+