mysql/mariadb information_schema view creation time - mysql

I want to find all tables and views created before a certain timestamp. For tables it is easy, just
SELECT * FROM information_schema.tables
WHERE table_type = 'TABLE' AND create_time < :ts
But for views it is not so simple. The create_time column is null for all the views in information_chema.tables. e.g.
MariaDB [MYDB]> SELECT IF(create_time IS NULL, 'Null', 'Not Null') AS has_create_ts
, COUNT(1)
FROM information_schema.tables
WHERE table_type = 'VIEW' GROUP BY has_create_ts;
+---------------+----------+
| has_create_ts | COUNT(1) |
+---------------+----------+
| Null | 70 |
+---------------+----------+
1 row in set, 10 warnings (0.371 sec)
And the information_schema.views table does not have any timestamp columns.
So how can I find out when a view was created? Or is it just not possible.
If it matters database version is:
MariaDB [MYDB]> SELECT VERSION();
+--------------------+
| VERSION() |
+--------------------+
| 10.3.7-MariaDB-log |
+--------------------+
1 row in set (0.392 sec)

So how can I find out when a view was created? Or is it just not possible.
No, this is not possible.
A view does not actually contains data, it just made of a definition, ie a SQL statement that references data contained in real (physical) tables : hence this is all that INFORMATION_SCHEMA.VIEWS can show you.

Related

Strange behaviour with MySql case sensitive table name in windows

In my windows machine when I select table names from mysql using the following query I'm getting table names as case sensitive.
mysql> select table_schema, table_name
from information_schema.tables where table_schema='test';
+--------------+------------+
| table_schema | table_name |
+--------------+------------+
| test | TableOne |
| test | TableTwo |
+--------------+------------+
2 rows in set (0.00 sec)
But when I select by table name i'm getting different result.
mysql> select table_schema, table_name from information_schema.tables
where table_schema='test' and table_name = 'TableOne';
+--------------+------------+
| table_schema | table_name |
+--------------+------------+
| test | tableone |
+--------------+------------+
1 row in set (0.00 sec)
What made it even stranger is this.
mysql> select table_schema, table_name from information_schema.tables
where table_schema='test' and table_name like 'TableOne';
+--------------+------------+
| table_schema | table_name |
+--------------+------------+
| test | TableOne |
+--------------+------------+
1 row in set (0.00 sec)
There is a MySql variable called lower_case_table_names. When this is set to 0 table names are case sensitive. But this is not advised on a case insensitive machine like windows. The next option is 1. In this option all table names are converted to lower case even before it is stored. You'll always get lower case table names in this case. In my case the value of this variable is set to 2. And in this case MySql stores table names as such, but when we compare table names, it'll convert them to lower case and compare.
So In the first case table name is not compared, hence we get the original value.
In the second case we are comparing the table name, so mysql convert table name to lower case for comparison. But strangely they are returning the converted value, not the original.
And finally in the third case we are using like operator which itself is case insensitive, hence mysql doesn't bother to convert table name to lower case, and we get the original result.

Combine two separate update queries into a single compound query

I have two separate update queries, i.e., "Update Query #1" & "Udpate Query #2". I would like to combine these two queries into a single compound query.
QUERY #1:
/remove bad dates in [addr.stuupd]/
UPDATE addr
SET addr.STUUPD= NULL
Where addr.STUUPD='0000-00-00 00:00:00'
QUERY #2:
/remove bad dates in [loan.DDBTUPD] date field/
UPDATE loan
SET loan.DDBTUPD= NULL
Where loan.DDBTUPD='0000-00-00 00:00:00'
I have taken the liberty of assuming that the real problem here is to ensure that both tables are updated at the same time, and neither update fails. In that case I would use a transaction.
Compound queries are normally only referred to with "select" statements. Reliable handling of multiple updates is provided by "transactions" because they support rollback if one part of the update fails.
NB: transactions only work with InnoDB tables.
You can change your tables to InnoDB with
mysql> alter table `addr` engine = InnoDB
mysql> alter table `loan` engine = InnoDB
Before...
mysql> select * from loan ;
+--------+---------------------+
| loanid | DDBTUPD |
+--------+---------------------+
| 1 | 0000-00-00 00:00:00 |
| 2 | 0000-00-00 00:00:00 |
| 3 | 0000-00-00 00:00:00 |
+--------+---------------------+
The transaction...
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> UPDATE addr SET addr.STUUPD= NULL Where addr.STUUPD='0000-00-00 00:00:00' ;
Query OK, 4 rows affected (0.01 sec)
mysql> UPDATE loan SET loan.DDBTUPD= NULL Where loan.DDBTUPD='0000-00-00 00:00:00' ;
Query OK, 3 rows affected (0.00 sec)
At this point, you will be able to see the results of the update, but other users will not
mysql> select * from loan ;
+--------+---------+
| loanid | DDBTUPD |
+--------+---------+
| 1 | NULL |
| 2 | NULL |
| 3 | NULL |
+--------+---------+
You will need to commit the transaction
mysql> COMMIT ;
Query OK, 0 rows affected (0.01 sec)
I suspect that peterm's solution below will run into problems if one of the tables does not have dates equal to '0000-00-00 00:00:00' and the other table does. peterm's query might not null out everything as expected. I'm happy to be proven wrong.
Although it's technically possible with a query like this
UPDATE addr a JOIN loan l
ON a.stuupd = '0000-00-00 00:00:00'
AND l.ddbtupd = '0000-00-00 00:00:00'
SET a.stuupd = NULL,
l.ddbtupd = NULL
Here is SQLFiddle demo
the question remains for what practical reason?

strict mode to avoid type conversion

Is there any sql mode that will return an error instead of implicitly converting the string to integer?
mysql> select * from todel ;
+------+--------+
| id | name |
+------+--------+
| 1 | abc |
| 2 | xyz |
| 0 | ABCxyz |
+------+--------+
3 rows in set (0.00 sec)
I expect an error message instead of a row with id 0
mysql> select * from todel where id = 'abc';
+------+--------+
| id | name |
+------+--------+
| 0 | ABCxyz |
+------+--------+
1 row in set, 1 warning (0.00 sec)
mysql> show warnings;
+---------+------+-----------------------------------------+
| Level | Code | Message |
+---------+------+-----------------------------------------+
| Warning | 1292 | Truncated incorrect DOUBLE value: 'abc' |
+---------+------+-----------------------------------------+
1 row in set (0.01 sec)
I understand your concerns, but it's for this very reason you should never have an id set to 0. In the long run I think you should reconsider your table rows before the behavior which isn't a problem in ideal situations. I haven't found anything relevant to this through a little searches, and that's probably because it's probably not a problem unless you make it one.
Apart from that, you could read relevant column data and act accordingly in php/whatev. From the table COLUMNS in information_schema, you can filter by TABLE_SCHEMA (database), TABLE_NAME and COLUMN_NAME to get DATATYPE (double). If the column you're changing has a certain DATATYPE, let the script give error before running the MySQL query.
Another way to do it would simply be to convert input before parsing:
if ( ! is_numeric($id))
$id = 'NULL';
To prevent incorrect INSERTs or UPDATEs, you already have that mode.
In the end I can't come up with many practical ways that this strict mode you're after would benefit the MySQL users.
You can use STRICT_ALL_TABLES sql mode:
set ##GLOBAL.sql_mode = "STRICT_ALL_TABLES";
set ##SESSION.sql_mode = "STRICT_ALL_TABLES";
However it works just on write operations:
MariaDB [(none)]> insert into test.test values ( "abc", "lol" );
--------------
insert into test.test values ( "abc", "lol" )
--------------
ERROR 1366 (22007): Incorrect integer value: 'abc' for column 'id' at row 1
There is no such thing to disable implicit conversions for read queries; instead you can just check if there are warnings and if yes, just free the result, abort the statement, and threat those warnings as errors.

How can I search and replace a string from an undetermined number of columns and tables using MySQL?

Using purely MySQL, I want to search and replace 'oldString' with 'newString' from and undetermined number of tables and columns. A search for my table prefix in information_schema, reveals all the tables that I need to look for.
I have the following trials:
Querying DB table prefix on information_schema
SELECT TABLE_SCHEMA, TABLE_NAME
FROM information_schema.TABLES WHERE table_name LIKE '%mysite_www%'
Results:
+-----------------+----------------------------+
| TABLE_SCHEMA | TABLE_NAME |
+-----------------+----------------------------+
| myDB | mysite_www_moredata |
| myDB | mysite_www_data |
| myDB | mysite_www_urls |
| myDB | mysite_www_pages |
| myDB | mysite_www_feedback |
| myDB | mysite_www_comments |
| myDB | mysite_www_links |
+-----------------+----------------------------+
Query results yields about 200 tables or so.
I want to take the results, filter it for a particular string and replace it with a new one. Replace 'oldString' with 'newString'.
For each TABLE_NAME, search for any column WHERE column LIKE '%oldString%'.
WHERE CONCAT(table1, table2, ... tableN) LIKE 'oldString';
For each column result, update 'oldString' to 'newString'.
UPDATE tableN SET columnN = REPLACE (columnN, '%oldString%', 'newString') WHERE URL LIKE '%oldString%';
I need to do this in pure MySQL as it will be a store procedure. Any assistance or tips is greatly appreciated.
So by using information_Schema you can query both the tables and columns to get a result. I have done something similar in this SO answer.
Essentially query the information_schema and let MySQL construct the SQL for you. The above link should get you going.
UPDATE:
You can use the following query to construct all the Update statements
SELECT CONCAT('UPDATE ',TABLENAME, 'SET ', COLUMN_NAME,'= REPLACE (',COLUMN_NAME, '''%oldString%'', ''newString'') WHERE URL LIKE ''%oldString%''')
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME LIKE '%somecriteria%' AND COLUMN_NAME LIKE '%somecriteria%'
Insert these resulting string into a temp table then loop through the rows executing the string by using the technique described here

How can I tell when a MySQL table was last updated?

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?
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.
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.
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.
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.
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
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
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
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.
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';
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
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
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.
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.
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.
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;
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'];
}
?>
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.