How to change CURRENT_TIMESTAMP temporarily to another time in MySQL - mysql

I have a fairly large and complex mysql query to blackbox test and this query is time sensitive, it has a lot of conditions based on current_timestamp.
My goal is to make some tests so it always passes or fails. I'm thinking of mocking the value of current_timestamp temporarily to a fixed date before running the query and set it back to original value after the query.
Is it something thats doable?
I cannot modify the query itself (i.e.: find replace current_timestamp to something else)
Thanks

By setting system variable 'timestamp'. To restore it to current timestamp again, set it to DEFAULT.
mysql> SET TIMESTAMP = UNIX_TIMESTAMP('2015-01-01');
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT NOW();
+---------------------+
| NOW() |
+---------------------+
| 2015-01-01 00:00:00 |
+---------------------+
1 row in set (0.00 sec)
mysql> SET TIMESTAMP = DEFAULT;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT NOW();
+---------------------+
| NOW() |
+---------------------+
| 2016-03-08 09:50:16 |
+---------------------+
1 row in set (0.00 sec)

Related

Why is MySQL ignoring the ORDER BY on this condition?

I want to show rows that have updated_at more than 3 hours ago. MySQL seems to be completely ignoring the ORDER BY clause. Any idea why?
Edit: as pointed out by Sebastian, this only occurs in certain timezones, like GMT+5 or GMT+8.
mysql> SET time_zone='+08:00';
Query OK, 0 rows affected (0.00 sec)
mysql> CREATE DATABASE test1; USE test1;
Query OK, 1 row affected (0.01 sec)
Database changed
mysql> CREATE TABLE `boxes` (
-> `box_id` int unsigned NOT NULL AUTO_INCREMENT,
-> `updated_at` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
-> PRIMARY KEY (`box_id`)
-> ) ENGINE=InnoDB;
Query OK, 0 rows affected (0.01 sec)
mysql> INSERT INTO `boxes` (`box_id`, `updated_at`) VALUES
-> (1, '2020-08-22 05:25:35'),
-> (2, '2020-08-26 18:49:05'),
-> (3, '2020-08-23 03:28:30'),
-> (4, '2020-08-23 03:32:55');
Query OK, 4 rows affected (0.00 sec)
Records: 4 Duplicates: 0 Warnings: 0
mysql> SELECT NOW();
+---------------------+
| NOW() |
+---------------------+
| 2020-08-26 20:49:59 |
+---------------------+
1 row in set (0.00 sec)
mysql> SELECT b.box_id, updated_at, (b.updated_at < NOW() - INTERVAL 3 HOUR) AS more_than_3hr
-> FROM boxes b
-> ORDER BY more_than_3hr DESC;
+--------+---------------------+---------------+
| box_id | updated_at | more_than_3hr |
+--------+---------------------+---------------+
| 1 | 2020-08-22 05:25:35 | 1 |
| 2 | 2020-08-26 18:49:05 | 0 | <--- WHY IS THIS HERE???
| 3 | 2020-08-23 03:28:30 | 1 |
| 4 | 2020-08-23 03:32:55 | 1 |
+--------+---------------------+---------------+
4 rows in set (0.00 sec)
Expectation: the rows with "1" should show up first.
Actual results: ORDER BY is ignored, and the resultset is sorted by primary key
I have a hunch it has something to do with MySQL storing timestamps as UTC and displaying them in the current timezone. My current timezone is GMT+8. However, it still doesn't make sense -- I am sorting the results based on the aliased expression, and the expression's value is clearly shown in the resultset.
MySQL version 8.0.21.
I also tried moving the expression to the ORDER BY clause, and the results are the same.
I don't know why but it compares wrong timezones in the background and thus values at the end are correct, but comparisons are invalid (for specific timezones).
When you query a TIMESTAMP value, MySQL converts the UTC value back to
your connection’s time zone. Note that this conversion does not take
place for other temporal data types such as DATETIME.
https://www.mysqltutorial.org/mysql-timestamp.aspx/
Changing type from TIMESTAMP to DATETIME fixes problem.
Other solution may be casting to the decimal number.
SELECT b.box_id, updated_at, FORMAT((b.updated_at < NOW() - INTERVAL 3 HOUR),0) AS more_than_3hr
FROM boxes b
ORDER BY more_than_3hr DESC;
From the documentation:
https://dev.mysql.com/doc/refman/8.0/en/user-variables.html
HAVING, GROUP BY, and ORDER BY, when referring to a variable that is assigned a value in the select expression list do not work as expected because the expression is evaluated on the client and thus can use stale column values from a previous row.
Basically, you can't use a variable name you created with "AS" in your sorting.
The solution is to use the verbose statement you used for the AS in sorting. Yeah, it's verbose. 🤷‍♂️ It is what it is.

mySQL - maximum value for a timestamp

I am using a mySQL database to store a range of inputs that feed a model. I have a number of different dates that are stored as TIMESTAMP. However, some of the values can be hundreds of years in the future. When I look in the DB, they are stored as '0000-00-00 00:00:00' when the actual timestamp should be something like '2850-12-01 00:00:00'.
While searching on Google, I noticed that the maximum value is sometime in 2038. Has anyone found a work-around for longer-dated TIMESTAMPs?
You can convert them to DATETIME, it will store what you want. Compare:
MariaDB [test]> create table t (t timestamp, d datetime);
Query OK, 0 rows affected (0.59 sec)
MariaDB [test]> insert into t values ('2850-12-01 00:00:00','2850-12-01 00:00:00');
Query OK, 1 row affected, 1 warning (0.08 sec)
MariaDB [test]> select * from t;
+---------------------+---------------------+
| t | d |
+---------------------+---------------------+
| 0000-00-00 00:00:00 | 2850-12-01 00:00:00 |
+---------------------+---------------------+
1 row in set (0.00 sec)

MySQL - TIMESTAMP field type automatically converts to UTC?

What is the behavior of the TIMESTAMP field type in relation to timezones?
Is any timestamp value inserted to that field inserted as is?
Or does it assume that the timezone of a timestamp value that is inserted is in server local time and converts it UTC?
EDIT:
Here is my test
I ran both PHP date() and MySQL's SELECT NOW() and they are outputting roughly equal timestamps. The results of both is not in UTC time.
I tried inserting to a test table with the value for the TIMESTAMP field by gotten from PHP date()
The value from PHP date() SHOULD have been converted to UTC. However, what I see in the database is not UTC. The value for the TIMESTAMP field is inserted as is.
TIMESTAMP value is always saved as UTC.
MySQL converts TIMESTAMP from current timezone to UTC for storage and back from UTC to the current time zone for retrieval.
The default timezone will be the server timezone and can be set on a connection. See this.
For more details see MySQL Doc
I can explain this through an example. Please execute the queries in mysql console:
mysql> CREATE TABLE `testtable` (
`date_timestamp` TIMESTAMP NOT NULL,
`date_datetime` DATETIME NOT NULL
)
ENGINE = InnoDB;
Query OK, 0 rows affected (0.06 sec)
mysql> SET time_zone = '+00:00';
Query OK, 0 rows affected (0.00 sec)
mysql> insert into testtable values(now(),now());
Query OK, 1 row affected (0.03 sec)
mysql> select * from testtable;
+---------------------+---------------------+
| date_timestamp | date_datetime |
+---------------------+---------------------+
| 2012-10-19 05:01:38 | 2012-10-19 05:01:38 |
+---------------------+---------------------+
1 row in set (0.00 sec)
mysql> SET time_zone = '+05:30';
Query OK, 0 rows affected (0.00 sec)
mysql>
mysql> insert into testtable values(now(),now());
Query OK, 1 row affected (0.03 sec)
mysql> select * from testtable;
+---------------------+---------------------+
| date_timestamp | date_datetime |
+---------------------+---------------------+
| 2012-10-19 10:31:38 | 2012-10-19 05:01:38 |
| 2012-10-19 10:31:47 | 2012-10-19 10:31:47 |
+---------------------+---------------------+
2 rows in set (0.00 sec)
The TIMESTAMPT value is inserted AS IS.

Set Default values in mysql id and timestamp column?

I want to set my id in mysql table to default value say '000001' or'TodaysDate followed by 000001'..and same should be also auto_incremented.
how can we do this?
and also how set default value in TIMESTAMP column not by using 'CURRENT_TIMESTAMP'
such as '2012-04-01' and when update trigger will get fire it should get updated with todays date.
How to do this?
I want to set my id in mysql table to default value say '000001'.
If I were you I will leave it like id int, auto increment and when make the select I'll use the lpad function:
mysql> select lpad('1',6,'0');
+-----------------+
| lpad('1',6,'0') |
+-----------------+
| 000001 |
+-----------------+
1 row in set (0.00 sec)
about the timestamp I'll let that someone else answer, because what I'm thinking is do the same, use the current_timestamp and with mysql function convert to it how you want to:
mysql> select left(now(),10);
+----------------+
| left(now(),10) |
+----------------+
| 2012-06-01 |
+----------------+
1 row in set (0.00 sec)'
EDIT:
mysql> select concat(replace(left(now(),10),'-',''),lpad('1',6,'0'));
+--------------------------------------------------------+
| concat(replace(left(now(),10),'-',''),lpad('1',6,'0')) |
+--------------------------------------------------------+
| 20120601000001 |
+--------------------------------------------------------+
1 row in set (0.00 sec)
It looks like you answered your own question: specifically you want a 'before insert/update' trigger that sets the value for you.
CREATE TRIGGER my_autoinc BEFORE INSERT ON test1
FOR EACH ROW BEGIN
INSERT INTO test1 SET NEW.id_column = concat(today(), <some value>);
END;

Should OPTIONAL User Settings be Lazy-Initialized into the DB or always created with a new registration?

Simple question: Should OPTIONAL User Settings be Lazy-Initialized into the DB or always created with a new registration?
A user can set additional settings which are optional, so should a row for that optional setting be created for every user upon registration or only created when a user makes use of those settings for the first time?
Lazy-Initialization saves space, so I'm leaning towards doing it this way, but I'm not sure if there are any drawbacks.
Do the lazy initialization. Here's why.. if you instead used default values for settings the user didn't actively choose, and then later the business rules changed to set the default to something else, you'd have users with what would appear to be non-default settings that they didn't actively choose.
Here is one particular drawback I ran into when using lazy initilization in MySQL. It all boils down to this quote from the MySQL docs:
User-defined variables are connection-specific.
First: The setup
Connect to your DB (in my case MySQL) Then create a simple function SetBarProperty() that sets the #bar property EXPLICITLY to the value passed to the function. The function itself simply returns the value passed to it.
$ mysql
mysql> DROP FUNCTION IF EXISTS SetBarProperty;
Query OK, 0 rows affected (0.01 sec)
mysql> DELIMITER $$
mysql> CREATE FUNCTION SetBarProperty( myValue INT(30) )
-> RETURNS INT(30)
-> DETERMINISTIC
-> READS SQL DATA
-> BEGIN
-> SET #bar := myValue;
-> RETURN myValue;
-> END $$
Query OK, 0 rows affected (0.01 sec)
mysql> DELIMITER ;
Now if we query for the #bar property, it will be NULL just as expected.
mysql> SELECT #bar;
+------+
| #bar |
+------+
| NULL |
+------+
1 row in set (0.00 sec)
Again, if we call the SetBarProperty(3); the result is just as expected.
mysql> SELECT SetBarProperty(3);
+-------------------+
| SetBarProperty(3) |
+-------------------+
| 3 |
+-------------------+
1 row in set (0.00 sec)
What do you think?
What do you think happens if we call the following query?
SELECT SetBarProperty(4), #bar;
You've guessed right! #bar is 4.
mysql> SELECT SetBarProperty(4), #bar;
+-------------------+------+
| SetBarProperty(4) | #bar |
+-------------------+------+
| 4 | 4 |
+-------------------+------+
1 row in set (0.00 sec)
And now the tricky part...
Disconnect from your DB server (this is very important):
Ctrl+C
Now re-connect and call the same query as before:
$ mysql
mysql> SELECT SetBarProperty(4), #bar;
+-------------------+------+
| SetBarProperty(4) | #bar |
+-------------------+------+
| 4 | NULL |
+-------------------+------+
1 row in set (0.00 sec)
Did you see what just has happened?
#bar was NULL and this is because it hasn't been initialized in the caller space of the function SetBarProperty().
If you disconnect from your DB and explicitly initialize the property BEFORE calling the function (that itself initializes it explicity) your query works just as expected:
Ctrl+C
$ mysql
mysql> SET #bar := NULL;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT SetBarProperty(4), #bar;
+-------------------+------+
| SetBarProperty(4) | #bar |
+-------------------+------+
| 4 | 4 |
+-------------------+------+
1 row in set (0.00 sec)
Recommendation
If you immediately use a property in a query just after you've called a function that modifies the same property you should initialize the property before calling the function.
I hope this helps!
Regards,
Konrad