210

In the footer of my page, I would like to add something like "last updated the xx/xx/200x" with this date being the last time a certain mySQL table has been updated.

What is the best way to do that? Is there a function to retrieve the last updated date? Should I access to the database every time I need this value?

Eric Leschinski
  • 146,994
  • 96
  • 417
  • 335
RaGE
  • 7,857
  • 4
  • 20
  • 8
  • 1
    Related: http://stackoverflow.com/questions/12563706/is-there-a-mysql-option-feature-to-track-history-of-changes-to-records – Pekka Jun 01 '15 at 05:19

17 Answers17

333

In later versions of MySQL you can use the information_schema database to tell you when another table was updated:

SELECT UPDATE_TIME
FROM   information_schema.tables
WHERE  TABLE_SCHEMA = 'dbname'
   AND TABLE_NAME = 'tabname'

This does of course mean opening a connection to the database.


An alternative option would be to "touch" a particular file whenever the MySQL table is updated:

On database updates:

  • Open your timestamp file in O_RDRW mode
  • close it again

or alternatively

  • use touch(), the PHP equivalent of the utimes() function, to change the file timestamp.

On page display:

  • use stat() to read back the file modification time.
Reed
  • 14,703
  • 8
  • 66
  • 110
Alnitak
  • 334,560
  • 70
  • 407
  • 495
  • are i can manipulate it. means if i want to change the value MySQL give me when i run this query –  Apr 12 '11 at 02:50
  • 9
    For details including InnoDB limitations see http://dev.mysql.com/doc/refman/5.5/en/show-table-status.html (`show table status` uses `information_schema.tables`) – KCD May 09 '12 at 21:12
  • Does `UPDATE_TIME` here related to time of changes in Information Schema e.g. only structural changes?? Does this not consider the changes if new rows are inserted/updated/deleted?? – Aamir Rind May 10 '12 at 12:40
  • 14
    Both the `UPDATE_TIME` method and the `show table status` method below are available only with the MyISAM engine, not InnoDB. Although this is listed as a [bug](http://bugs.mysql.com/bug.php?id=15438), it is mentioned in the [MySQL 5.5 Reference](http://dev.mysql.com/doc/refman/5.5/en/show-table-status.html), which also says that the `file_per_table` mode is an unreliable indicator of modification time. – idoimaging Sep 25 '12 at 21:49
  • 6
    Again, doesn't work on InnoDB because mysql is f**king buggy: http://bugs.mysql.com/bug.php?id=14374 – Tomas Sep 10 '14 at 07:44
  • 9
    For MySQL 5.7.2+ it also [works for InnoDB](https://dev.mysql.com/doc/refman/5.7/en/tables-table.html): "Beginning with MySQL 5.7.2, UPDATE_TIME displays a timestamp value for the last UPDATE, INSERT, or DELETE performed on InnoDB tables that are not partitioned. Previously, UPDATE_TIME displayed a NULL value for InnoDB tables." – Peter V. Mørch Aug 25 '15 at 23:23
  • 2
    This does not seem to persist on restart for me (on version 5.7.11). –  Jun 29 '16 at 20:40
  • This work fine for me so far.My only problem is that the update time resets to null every day.Any ideas? – Alator Feb 01 '18 at 11:00
  • 2
    Any way to get the information for InnoDB tables in 2018? – rain_ May 03 '18 at 11:22
  • 1
    @rain_ InnoDB fixes the `update_time` column in MySQL 5.7.2, but is not persistent across server restarts (see https://bugs.mysql.com/bug.php?id=14374) – Bill Karwin Jan 29 '19 at 17:07
  • Is there any way that I can know the time of last update of a particular field in the table? – m-2127 Jul 18 '19 at 11:53
65

I'm surprised no one has suggested tracking last update time per row:

mysql> CREATE TABLE foo (
  id INT PRIMARY KEY
  x INT,
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP 
                     ON UPDATE CURRENT_TIMESTAMP,
  KEY (updated_at)
);

mysql> INSERT INTO foo VALUES (1, NOW() - INTERVAL 3 DAY), (2, NOW());

mysql> SELECT * FROM foo;
+----+------+---------------------+
| id | x    | updated_at          |
+----+------+---------------------+
|  1 | NULL | 2013-08-18 03:26:28 |
|  2 | NULL | 2013-08-21 03:26:28 |
+----+------+---------------------+

mysql> UPDATE foo SET x = 1234 WHERE id = 1;

This updates the timestamp even though we didn't mention it in the UPDATE.

mysql> SELECT * FROM foo;
+----+------+---------------------+
| id | x    | updated_at          |
+----+------+---------------------+
|  1 | 1235 | 2013-08-21 03:30:20 | <-- this row has been updated
|  2 | NULL | 2013-08-21 03:26:28 |
+----+------+---------------------+

Now you can query for the MAX():

mysql> SELECT MAX(updated_at) FROM foo;
+---------------------+
| MAX(updated_at)     |
+---------------------+
| 2013-08-21 03:30:20 |
+---------------------+

Admittedly, this requires more storage (4 bytes per row for TIMESTAMP).
But this works for InnoDB tables before 5.7.15 version of MySQL, which INFORMATION_SCHEMA.TABLES.UPDATE_TIME doesn't.

Meloman
  • 3,558
  • 3
  • 41
  • 51
Bill Karwin
  • 538,548
  • 86
  • 673
  • 828
  • 11
    +1 As far as I'm concerned, this is the only right answer to this question. The question really wants to know when relevant data is updated, and not when a table (which is irrelevant to the user) may or may not have been changed for whatever reason. – siride Aug 22 '13 at 02:49
  • 28
    But if you delete a record with a lower update time, MAX(updated_at) will not work. – Ammamon Mar 31 '14 at 08:46
  • 3
    @Ammamon, true, if you also need to account for deletions, this solution doesn't reflect that. A trigger to update a summary table may be the only comprehensive solution, but that would create a bottleneck. – Bill Karwin Mar 31 '14 at 18:00
  • Yes. I think we can also update a persisted global state variable with new DateTime() whenever our table is inserted, updated or deleted. By displaying this global state variable at the bottom of your page, you can avoid repeated table checks. – Ammamon Apr 02 '14 at 06:13
  • I am mentioning about the footer display, because that's what the questioner needed. – Ammamon Apr 02 '14 at 06:27
  • 4
    for most instances knowing deletion wouldn't be as relevant, and many higher level aps don't truly delete from many tables but simply unset a flag for the equivalent. – Samuel Fullman Apr 23 '14 at 23:21
  • For convenience, here's the update statement `ALTER TABLE 'foo' ADD 'updated_at' TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP; ALTER TABLE 'foo' ADD INDEX ('updated_at');` – Joshua Aug 19 '14 at 04:42
  • Hey @BillKarwin What's the difference between your update statement compared to this update statement: `ALTER TABLE my_table ADD ModifiedTime TIMESTAMP;` taken from this article: https://www.marcus-povey.co.uk/2013/03/11/automatic-create-and-modified-timestamps-in-mysql/ – Pavan May 14 '15 at 12:59
  • @Pavan, "By default, the *first* TIMESTAMP column has both DEFAULT CURRENT_TIMESTAMP and ON UPDATE CURRENT_TIMESTAMP if neither is specified explicitly." https://dev.mysql.com/doc/refman/5.6/en/timestamp-initialization.html – Bill Karwin May 14 '15 at 20:50
  • @BillKarwin don't understand the difference? My one seems easier to implement and it will still update the timestamp on update – Pavan May 15 '15 at 02:54
  • 1
    @Pavan, there is no difference, you're just relying on the default behavior, and my example spells out the option explicitly. But the option is identical to the default behavior. – Bill Karwin May 15 '15 at 04:43
  • @BillKarwin after version 5.7.15 of MySQL, innoDB works now with `UPDATE_TIME` field in `information_schema`.`tables` table. – Meloman Oct 13 '17 at 11:32
  • 1
    @Meloman, thanks for the tip, I had missed that. I found the release notes, it was actually in 5.7.2. See https://dev.mysql.com/doc/relnotes/mysql/5.7/en/news-5-7-2.html and https://bugs.mysql.com/bug.php?id=14374 – Bill Karwin Oct 13 '17 at 14:59
  • SELECT MAX ... could be slow with large tables. A Table with 2.8 million needs 2,5 seconds to identify MAX(...). – Peter Dec 06 '17 at 17:07
  • @Peter, No, InnoDB will read the index on updated_at to get the highest value. It doesn't need to read the table at all, and it doesn't matter how large the table is. Try the `EXPLAIN SELECT MAX(updated_at) ...` and you'll see "Select tables optimized away". Read about that optimization here: https://dev.mysql.com/doc/refman/5.7/en/explain-output.html – Bill Karwin Dec 06 '17 at 17:30
  • @BillKarwin: Don't know what you mean with EXPLAIN ... |||| But I agree, adding an Index makes SELECT MAX very fast. But does the created Index have a performance impact while inserting, e.g. Insert 2.8 million rows at once? – Peter Dec 06 '17 at 19:51
  • 1
    @Peter, Yes, of course indexes have some overhead. You have to decide if you want to optimize for INSERTs or optimize for queries that use the index. Usually it's a good tradeoff to create indexes. But we should not maintain indexes that none of our queries use. – Bill Karwin Dec 06 '17 at 20:35
  • @BillKarwin for deleted records I simple use a `COUNT` to return the number of rows and also check that. If a record is deleted and no updates have been made, then the rows count will be different and then if a new row will be inserted and matches the previous count after a row has been deleted, we'll get the latest timestamp for the new row created. My query is `SELECT COUNT(*) AS total_rows, UNIX_TIMESTAMP(MAX('updated_at')) AS updated FROM 'table'` and then I check both to set an `isDirty` flag. – Christos Lytras May 30 '19 at 22:39
  • Same answer here, with hints on performance: https://stackoverflow.com/a/35362997/411189 – isync Feb 25 '20 at 19:47
65

I don't have information_schema database, using mysql version 4.1.16, so in this case you can query this:

SHOW TABLE STATUS FROM your_database LIKE 'your_table';

It will return these columns:

| Name      | Engine | Version | Row_format | Rows | Avg_row_length 
| Data_length | Max_data_length | Index_length | Data_free | Auto_increment
| Create_time | Update_time | Check_time | Collation
| Checksum | Create_options | Comment |

As you can see there is a column called: "Update_time" that shows you the last update time for your_table.

Radu Maris
  • 5,648
  • 4
  • 39
  • 54
  • 2
    It also perform faster (4x) then SELECT statement – jmav Jan 23 '13 at 21:16
  • 1
    In fact, most SHOW commands like this are simply mapped to Information_schema queries internally, so this answer gives exactly the same data as the answer from Alnitak above. And ajacian81 is correct - it does not work for MySQL's default storage engine, InnoDB. – Bill Karwin Feb 17 '14 at 13:02
19

The simplest thing would be to check the timestamp of the table files on the disk. For example, You can check under your data directory

cd /var/lib/mysql/<mydatabase>
ls -lhtr *.ibd

This should give you the list of all tables with the table when it was last modified the oldest time, first.

lakshayg
  • 2,053
  • 2
  • 20
  • 34
user2654744
  • 438
  • 5
  • 8
  • Really great answer since UPDATE_TIME is NULL for all tables in my (MariaDb) case – Alexander Vasiljev Nov 14 '18 at 09:16
  • ibd files only store table structure. By default, the data is stored in ibdata1 so the ibd file timestamp will not update when data is changed in the table. [https://dev.mysql.com/doc/refman/8.0/en/show-table-status.html](https://dev.mysql.com/doc/refman/8.0/en/show-table-status.html) – Stack Underflow May 19 '20 at 19:14
  • 1
    @StackUnderflow, What you've said is not accurate. The ibd files store data and indexes for a tablespace, which contains one or more tables. If the data is stored in ibdata1, there will be no ibd file. Storing data in one file per table has been the default since MySQL 5.6 in 2013. – Bill Karwin Jul 20 '20 at 03:33
18

For a list of recent table changes use this:

SELECT UPDATE_TIME, TABLE_SCHEMA, TABLE_NAME
FROM information_schema.tables
ORDER BY UPDATE_TIME DESC, TABLE_SCHEMA, TABLE_NAME
Pablo Bianchi
  • 1,824
  • 1
  • 26
  • 30
Francois Bourgeois
  • 3,650
  • 5
  • 30
  • 41
5

Although there is an accepted answer I don't feel that it is the right one. It is the simplest way to achieve what is needed, but even if already enabled in InnoDB (actually docs tell you that you still should get NULL ...), if you read MySQL docs, even in current version (8.0) using UPDATE_TIME is not the right option, because:

Timestamps are not persisted when the server is restarted or when the table is evicted from the InnoDB data dictionary cache.

If I understand correctly (can't verify it on a server right now), timestamp gets reset after server restart.

As for real (and, well, costly) solutions, you have Bill Karwin's solution with CURRENT_TIMESTAMP and I'd like to propose a different one, that is based on triggers (I'm using that one).

You start by creating a separate table (or maybe you have some other table that can be used for this purpose) which will work like a storage for global variables (here timestamps). You need to store two fields - table name (or whatever value you'd like to keep here as table id) and timestamp. After you have it, you should initialize it with this table id + starting date (NOW() is a good choice :) ).

Now, you move to tables you want to observe and add triggers AFTER INSERT/UPDATE/DELETE with this or similar procedure:

CREATE PROCEDURE `timestamp_update` ()
BEGIN
    UPDATE `SCHEMA_NAME`.`TIMESTAMPS_TABLE_NAME`
    SET `timestamp_column`=DATE_FORMAT(NOW(), '%Y-%m-%d %T')
    WHERE `table_name_column`='TABLE_NAME';
END
Soul Reaver
  • 2,012
  • 3
  • 37
  • 52
5

I would create a trigger that catches all updates/inserts/deletes and write timestamp in custom table, something like tablename | timestamp

Just because I don't like the idea to read internal system tables of db server directly

Mikhail
  • 1,583
  • 5
  • 22
  • 34
  • 1
    This seems like a good solution for MS SQL, because there is no `UPDATE_TIME` column like in MySQL. – Darcy Jul 26 '13 at 19:52
3

OS level analysis:

Find where the DB is stored on disk:

grep datadir /etc/my.cnf
datadir=/var/lib/mysql

Check for most recent modifications

cd /var/lib/mysql/{db_name}
ls -lrt

Should work on all database types.

1

a) It will show you all tables and there last update dates

SHOW TABLE STATUS FROM db_name;

then, you can further ask for specific table:

SHOW TABLE STATUS FROM db_name like 'table_name';

b) As in above examples you cannot use sorting on 'Update_time' but using SELECT you can:

SELECT * FROM information_schema.tables WHERE TABLE_SCHEMA='db_name' ORDER BY UPDATE_TIME DESC;

to further ask about particular table:

SELECT * FROM information_schema.tables WHERE TABLE_SCHEMA='db_name' AND table_name='table_name' ORDER BY UPDATE_TIME DESC';
justnajm
  • 4,422
  • 6
  • 36
  • 56
1

I got this to work locally, but not on my shared host for my public website (rights issue I think).

SELECT last_update FROM mysql.innodb_table_stats WHERE table_name = 'yourTblName';

'2020-10-09 08:25:10'

MySQL 5.7.20-log on Win 8.1

Simas Joneliunas
  • 2,890
  • 20
  • 28
  • 35
  • 1
    This is the last time MySQL updated the table stats row, not that table data itself: https://dev.mysql.com/doc/refman/5.6/en/innodb-persistent-stats.html – Yoni Sade Oct 31 '22 at 13:51
0

Just grab the file date modified from file system. In my language that is:

 tbl_updated = file.update_time(
        "C:\ProgramData\MySQL\MySQL Server 5.5\data\mydb\person.frm")

Output:

1/25/2013 06:04:10 AM
Matthias Robbers
  • 15,689
  • 6
  • 63
  • 73
Steve Wood
  • 31
  • 3
0

If you are running Linux you can use inotify to look at the table or the database directory. inotify is available from PHP, node.js, perl and I suspect most other languages. Of course you must have installed inotify or had your ISP install it. A lot of ISP will not.

bartonlp
  • 123
  • 1
  • 6
0

Not sure if this would be of any interest. Using mysqlproxy in between mysql and clients, and making use of a lua script to update a key value in memcached according to interesting table changes UPDATE,DELETE,INSERT was the solution which I did quite recently. If the wrapper supported hooks or triggers in php, this could have been eaiser. None of the wrappers as of now does this.

0

i made a column by name : update-at in phpMyAdmin and got the current time from Date() method in my code (nodejs) . with every change in table this column hold the time of changes.

saeed
  • 1
  • 1
    Welcome to StackOverflow! Please [edit your answer](https://stackoverflow.com/posts/58823229/edit) to include some of the code you used, as well as an explanation of *why* you chose to do it this way. This question is almost *eleven years old*, and already has an accepted, well-upvoted answer. Your answer, without some code or an explanation, will likely be downvoted or deleted. Editing it to include those things will help justify its place on this post. – Das_Geek Nov 12 '19 at 17:13
0

Same as others, but with some conditions i've used, to save time:

SELECT
  UPDATE_TIME,
  TABLE_SCHEMA,
  TABLE_NAME
FROM
  information_schema.tables
WHERE
  1 = 1
  AND UPDATE_TIME > '2021-11-09 00:00:00'
  AND TABLE_SCHEMA = 'db_name_here'
  AND TABLE_NAME not in ('table_name_here',)
ORDER BY
  UPDATE_TIME DESC,
  TABLE_SCHEMA,
  TABLE_NAME;
Manohar Reddy Poreddy
  • 25,399
  • 9
  • 157
  • 140
-1

This is what I did, I hope it helps.

<?php
    mysql_connect("localhost", "USER", "PASSWORD") or die(mysql_error());
    mysql_select_db("information_schema") or die(mysql_error());
    $query1 = "SELECT `UPDATE_TIME` FROM `TABLES` WHERE
        `TABLE_SCHEMA` LIKE 'DataBaseName' AND `TABLE_NAME` LIKE 'TableName'";
    $result1 = mysql_query($query1) or die(mysql_error());
    while($row = mysql_fetch_array($result1)) {
        echo "<strong>1r tr.: </strong>".$row['UPDATE_TIME'];
    }
?>
Andrés Chandía
  • 999
  • 1
  • 16
  • 32
-9

Cache the query in a global variable when it is not available.

Create a webpage to force the cache to be reloaded when you update it.

Add a call to the reloading page into your deployment scripts.

Kieveli
  • 10,944
  • 6
  • 56
  • 81
  • 2
    you can't "cache" variables between independent invocations of a PHP page without outside assistance. – Alnitak Nov 21 '08 at 01:20