Time difference between CURRENT_TIMESTAMP(), CURRENT_DATE() + 0 and CURRENT_DATE() - mysql

When I perform this query:
SELECT CURRENT_TIMESTAMP(),CURRENT_DATE()+0,CURRENT_DATE;
I get this result:
CURRENT_TIMESTAMP(), CURRENT_DATE()+0, CURRENT_DATE
2019-03-25 08:54:33, 20190325 , 2019-03-24
Why is the difference between CURRENT_DATE and CURRENT_DATE + 0?
My server version is: 15.1 Distrib 10.1.37-MariaDB, for debian-linux-gnu (x86_64) using readline 5.2
Thank you for any suggestion :)

I agree that the result can be somewhat surprising. You add zero to a date and expect it to stay this date. This, however, would imply that you add zero "something", e.g. zero hours or zero days (otherwise: would would +1 mean to a date?).
There are DBMS that do exactly this. In Oracle for example somedate + 1 means somedate + interval '1' day. MySQL, however, doesn't. MySQL rather adds the number (zero in your case) to the internal numeric representation of the date. Why they are doing this, is beyond me. Why would we want to see what MySQL uses internally to store a date? It shouldn't matter to us. So what MySQL does is convert CURRENT_DATE to a numeric value and then add zero, which is why you see a number in your result.
What you can learn from this: Don't add a number to a date, as it has no meaning really. What is October 3, 2008 plus 23 (opposed to "plus 23 days" or "plus 23 weeks" or whatever)? It just has no meaning. So treat this like a flaw in the DBMS to even allow this and just never use it.
You can of course always add an interval to a date, e.g.
select current_date + interval 0 day;

If you want to add some number of days to a MySQL date, then you should use INTERVAL, e.g.
CURRENT_DATE() + INTERVAL 0 DAY
But, since you actually an integer to a MySQL date, here is an explanation. CURRENT_DATE() is a date, while CURRENT_DATE() + 0 ends being a number. Your current query:
SELECT CURRENT_DATE() + 0;
is actually being evaluated as this:
SELECT (CAST CURRENT_DATE() AS UNSIGNED) + 0;
The cast to integer is necessary because you are adding zero, an integer, to the result of CURRENT_DATE().
Convince yourself of this by running:
SELECT (CAST CURRENT_DATE() AS UNSIGNED);
This will return 20180325 as a result.

I think I found the answer - the problem lies in dataGrip desktop client I am using. I am not sure what exactly happens to data, but it seems, that a) dataGrip somehow makes the connection with incorrect timezone or b) incorrectly converts data when receiving. And before you asked, I tried the same query via terminal, MySQL workbench and on the MySQL server itself and I get the correct answer. :)

Related

How to convert datediff & curdate() from MySql to PostgreSQL for a weekly selection range

I have the following SQL query which works fine in MySQL:
SELECT floor(datediff(users.created_at, curdate()) / 7) AS weeks_ago,
I'm want to convert this from MySQL to PostgreSQL. How can I get this query working?
Something like this:
SELECT TRUNC(DATE_PART('day', CURRENT_DATE - users.created_at )/7) AS weeks_ago
If we subtract two DATE or TIMESTAMP in PostgreSQL, we get an interval "ddd days hh:mi:ss"
We can use DATE_PART to extract just the ddd value. (Note that the first argument 'day' is a string enclosed in single quotes, not a keyword.)
Since the MySQL expression appears to be counting weeks as intervals of 7 days, we can do the same thing in PostgreSQL, divide by 7 and take the integer portion.
(n.b. not tested)
Reference:
https://www.postgresql.org/docs/8.0/functions-datetime.html#FUNCTIONS-DATETIME-CURRENT

Unable to create computed field with date_add in SQL

In my database, I have a table called 'fine', in that table I have three fields, issue_date, expiry_date and fine_amount. I want the expiry_date to be computed from the issue date. The expiry date should always have 20 days more than the issue_date, So I wrote the query as:
ALTER TABLE fine ADD
expiry_date AS DATE_ADD(CURRENT_DATE,INTERVAL 20 DAY)
But there is a syntax error. I can't seem to find the solution.
Also I want the fine_amount to be 10 * (difference in days between current date and expiry date if current days exceeds expiry date). How do I go about doing that?
You can't implement the fine logic using a computed column because the formula involves the current time which is non deterministic. From the MySQL documentation:
Literals, deterministic built-in functions, and operators are permitted. A function is deterministic if, given the same data in tables, multiple invocations produce the same result, independently of the connected user. Examples of functions that fail this definition: CONNECTION_ID(), CURRENT_USER(), NOW().
So your best bet probably is to just compute values for these columns at the time you actually select. For example:
SELECT issue_date,
DATE_ADD(issue_date, INTERVAL 20 DAY) AS expiry_date,
CASE WHEN NOW() > DATE_ADD(issue_date, INTERVAL 20 DAY)
THEN 10*DATEDIFF(NOW(), DATE_ADD(issue_date, INTERVAL 20 DAY))
ELSE 0 END AS fine_amount
FROM fine

What's the difference between a TimeStamp field and mysql curdate

I was working and my friend told me to use curdate() on mysql query to get the current date of the server... And I told him that I was using Time_Stamp field for date/time.
Now I start to think, is there a huge difference between this two ways ? One is better than the other? Or there is something that makes it a not good practice ? Also there is a now() that can be used too. I just wanted to understand how does it work or wich one is the best and why.
Short version:
NOW() = CONCAT(CURDATE(), ' ', CURTIME());
CURDATE() = DATE(NOW());
Some explanation:
NOW() gets both date (CURDATE()) and time (CURTIME()).
So if we do it the other way round, CURDATE() = DATE(NOW()).
Regarding timestamp, in MySQL Data Types we can see timestampis 3 bytes, while datetime is 8 bytes.

MySQL select rows that are exactly 7 days old FROM TIMESTAMP

first of all, I know that my question is very similar to that one:
MySQL select rows from exactly 7 days ago
the difference is that my dates are stored in the database as a timestamp.
I know that I can use FROM_UNIXTIME to get the date from the timestamp, the thing is, in another answer I read that was very resource consuming (because the timestamp field has to be converted to date in all the records before comparing).
DATE(from_unixtime(timestamp)) = CURRENT_DATE()
Is there any optimized way to do this?
Turn it around: calculate the unix timestamp of the target date first and use that.
WHERE timestamp = UNIX_TIMESTAMP(NOW() - INTERVAL 7 DAY)
MySQL should calculate that value once and use it all the time (needs testing though). If it doesn't, use a variable or code.

MySQL Select rows <= now(), using separated time fields

I have a table 't' with date(yyyy-mm-dd), hour(1-12), minute(00-59), ampm(a/p), and timezone(pst/est) fields.
How can I select the rows that are <= now()? (ie. already happened)
Thank you for your suggestions!
edit: this does it without attention to the hour/minute/ap/tz fields:
SELECT * FROM t.date WHERE date <= now()
Here's one way to do it - combine all your seconds, minutes, etc into a date and compare to NOW(), making sure you do the comparison in the same time-zone. (Untested):
SELECT *
FROM t
LEFT JOIN y ON t.constant=y.constant
WHERE CONVERT_TZ(STR_TO_DATE(CONCAT(date,' ',hour,':',minute,' 'ampm),
'%Y-%m-%d %l:%i %p' ),
timezone,"SYSTEM") < NOW();
If your hour is 01 - 12 not 1-12 then use %h instead of %l in the STR_TO_DATE.
The STR_TO_DATE tries to stick your date and time columns together and convert them into a date.
The CONVERT_TZ(...,timezone,"SYSTEM") converts this date from whatever timezone is specified in the timezone column to system time.
This is then compared to NOW(), which is always in system time.
As an aside, perhaps you should make a single column date using MySQL's date datatype, as it's a lot easier to do arithmetic on that!
For reference, here is a summary of very useful mysql date functions where you can read up on those featuring in this answer.
Good luck!
SELECT * FROM t
WHERE `date`<=DATE_SUB(curdate(), INTERVAL 1 DAY)
OR (
`date`<=DATE_ADD(curdate(), INTERVAL 1 DAY)
AND
CONVERT_TZ(CAST(CONCAT(`date`,' ',IF(`hour`=12 AND ampm='a',0,if(ampm='a',`hour`,`hour`+12)),':',`minute`,':00') AS DATETIME),'GMT',`timezone`)<=NOW()
)
Rationale for date<=DATE_[ADD|SUB](curdate(), INTERVAL 1 DAY):
The fancy conversion is quite an expensive operation, so we don't want it to run on the complete table. This is why we pre-select against an UNCHANGED date field (possibly using an index). In no timezone can an event being more than a day in current timezone's past be in the future, and in no timezone can an event more than a day in the curent timezone's future be in the past.