get primary keys of underlaying tables in a view - mysql

I know of show tables and show columns from table TABLE_NAME which gets me the primary key (Key is PRI). But how do I get the primary key of a view or more specific the keys of the underlying tables? In that case show columns from VIEW_NAME all the Key values are empty.

A new INFORMATION_SCHEMA table exists to map views to the underlying table(s). It's called VIEW_TABLE_USAGE.
mysql> create table mytable (id serial primary key);
Query OK, 0 rows affected (0.01 sec)
mysql> create view v as select * from mytable;
Query OK, 0 rows affected (0.00 sec)
mysql> select table_schema, table_name, column_name, ordinal_position
from information_schema.view_table_usage
join information_schema.key_column_usage using (table_schema, table_name)
where table_name='mytable' and constraint_name='PRIMARY';
+--------------+------------+-------------+------------------+
| TABLE_SCHEMA | TABLE_NAME | COLUMN_NAME | ORDINAL_POSITION |
+--------------+------------+-------------+------------------+
| test2 | mytable | id | 1 |
+--------------+------------+-------------+------------------+
This table is new in MySQL 8.0.13.
If you use an older version of MySQL (or any version of MariaDB), there is no such information_schema table, so you can't get this information by any query. You would need to come up with a way to parse the view definition, which is a complex task because it could have a wide variety of SELECT syntax.
mysql> select view_definition from information_schema.views where table_name='v';
+--------------------------------------------------------------+
| VIEW_DEFINITION |
+--------------------------------------------------------------+
| select `test2`.`mytable`.`id` AS `id` from `test2`.`mytable` |
+--------------------------------------------------------------+

Related

How to create tables with a prefix in a more maintainable way

I need to maintain a SQL script (for MySQL) which creates many tables with same prefix, like wp_parent, wp_teacher.
I don't want to write CREATE TABLE wp_parent. In that case, if I need to change the prefix, I have to modify many lines.
I have tried to use MySQL user variable, but it didn't work.
Is there any way to make prefix be a variable and then use the variable in other statement?
Or is there any other method to make the script more maintainable?
Your can use this command to add the prefix to the tables in database
SELECT Concat('ALTER TABLE ', TABLE_NAME, ' RENAME TO dr_', TABLE_NAME, ';')
FROM INFORMATION_SCHEMA.TABLES;
Give it a Try !!!
mysql> drop database if exists prefixdb;
Query OK, 4 rows affected (0.01 sec)
mysql> create database prefixdb;
Query OK, 1 row affected (0.00 sec)
mysql> use prefixdb
Database changed
mysql> create table tab1 (num int) ENGINE=MyISAM;
Query OK, 0 rows affected (0.05 sec)
mysql> create table tab2 like tab1;
Query OK, 0 rows affected (0.05 sec)
mysql> create table tab3 like tab1;
Query OK, 0 rows affected (0.05 sec)
mysql> create table tab4 like tab1;
Query OK, 0 rows affected (0.04 sec)
mysql> show tables;
+--------------------+
| Tables_in_prefixdb |
+--------------------+
| tab1 |
| tab2 |
| tab3 |
| tab4 |
+--------------------+
4 rows in set (0.00 sec)
The query to generate it would be
select
concat('alter table ',db,'.',tb,' rename ',db,'.',prfx,tb,';')
from
(select table_schema db,table_name tb
from information_schema.tables where
table_schema='prefixdb') A,
(SELECT 'dr_' prfx) B
;
Running it at the command line I get this:
mysql> select concat('alter table ',db,'.',tb,' rename ',db,'.',prfx,tb,';') from (select table_schema db,table_name tb from information_schema.tables where table_schema='prefixdb') A,(SELECT 'dr_' prfx) B;
+----------------------------------------------------------------+
| concat('alter table ',db,'.',tb,' rename ',db,'.',prfx,tb,';') |
+----------------------------------------------------------------+
| alter table prefixdb.tab1 rename prefixdb.dr_tab1; |
| alter table prefixdb.tab2 rename prefixdb.dr_tab2; |
| alter table prefixdb.tab3 rename prefixdb.dr_tab3; |
| alter table prefixdb.tab4 rename prefixdb.dr_tab4; |
+----------------------------------------------------------------+
4 rows in set (0.00 sec)
Pass the result back into mysql like this:
mysql -hhostip -uuser -pass -AN -e"select concat('alter table ',db,'.',tb,' rename ',db,'.',prfx,tb,';') from (select table_schema db,table_name tb from information_schema.tables where table_schema='prefixdb') A,(SELECT 'dr_' prfx) B" | mysql -hhostip -uuser -ppass -AN
With regard to your question if you want to rename all tables, do not touch information_schema and mysql databases. Use this query instead:
select
concat('alter table ',db,'.',tb,' rename ',db,'.',prfx,tb,';')
from
(select table_schema db,table_name tb
from information_schema.tables where
table_schema not in ('information_schema','mysql')) A,
(SELECT 'dr_' prfx) B ;

2 servers, 2 memory tables, different sizes

I have got two servers both running a MySQL instance. The first one, server1, is running MySQL 5.0.22. The other one, server2, is running MySQL 5.1.58.
When I create a memory table on server1 and I add a row its size is instantly 8,190.0 KiB.
When I create a memory table on server2 and I add a row its size is still only some bytes, though.
Is this caused by the difference in MySQL version or (hopefully) is this due to some setting I can change?
EDIT:
I haven't found the reason for this behaviour yet, but I did found a workaround. So, for future references, this is what fixed it for me:
All my memory tables are made once and are read-only from thereon. When you specify to MySQL the maximum number of rows your table will have, its size will shrink. The following query will do that for you.
ALTER TABLE table_name MAX_ROWS = N
Factor of 2?
OK, the problem likely is caused by the UTF-8 vs latin1
:- http://dev.mysql.com/doc/refman/5.0/en/storage-requirements.html
You can check the database connection, database default character set for both servers.
here is the testing I have just done :-
mysql> create table test ( name varchar(10) ) engine
-> =memory;
Query OK, 0 rows affected (0.03 sec)
mysql> show create table test;
+-------+------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+------------------------------------------------------------------------------------------------+
| test | CREATE TABLE `test` (
`name` varchar(10) DEFAULT NULL
) ENGINE=MEMORY DEFAULT CHARSET=latin1 |
+-------+------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> insert into test values ( 1 );
mysql> set names utf8;
Query OK, 0 rows affected (0.01 sec)
mysql> create table test2 ( name varchar(10) ) engine =memory default charset = utf8;
Query OK, 0 rows affected (0.01 sec)
Query OK, 0 rows affected (0.01 sec)
mysql> insert into test2 values ( convert(1 using utf8) );
Query OK, 1 row affected (0.01 sec)
mysql> select table_name, avg_row_length from information_schema.tables where TABLE_NAME in( 'test2', 'test');
+------------+----------------+
| table_name | avg_row_length |
+------------+----------------+
| test | 12 |
| test2 | 32 |
+------------+----------------+
2 rows in set (0.01 sec)

How to add prefix of all tables in mysql

How can I add prefix to all tables in mysql using query.
For example:
I need to add "dr_" in all tables which are available in mysql database.
Run all queries that you get from running this query:
SELECT Concat('ALTER TABLE `', TABLE_NAME, '` RENAME TO `dr_', TABLE_NAME, '`;') FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '<name of your db>';
For this example, I will create a database called prefixdb with 4 tables:
mysql> drop database if exists prefixdb;
Query OK, 4 rows affected (0.01 sec)
mysql> create database prefixdb;
Query OK, 1 row affected (0.00 sec)
mysql> use prefixdb
Database changed
mysql> create table tab1 (num int) ENGINE=MyISAM;
Query OK, 0 rows affected (0.05 sec)
mysql> create table tab2 like tab1;
Query OK, 0 rows affected (0.05 sec)
mysql> create table tab3 like tab1;
Query OK, 0 rows affected (0.05 sec)
mysql> create table tab4 like tab1;
Query OK, 0 rows affected (0.04 sec)
mysql> show tables;
+--------------------+
| Tables_in_prefixdb |
+--------------------+
| tab1 |
| tab2 |
| tab3 |
| tab4 |
+--------------------+
4 rows in set (0.00 sec)
The query to generate it would be
select
concat('alter table ',db,'.',tb,' rename ',db,'.',prfx,tb,';')
from
(select table_schema db,table_name tb
from information_schema.tables where
table_schema='prefixdb') A,
(SELECT 'dr_' prfx) B
;
Running it at the command line I get this:
mysql> select concat('alter table ',db,'.',tb,' rename ',db,'.',prfx,tb,';') from (select table_schema db,table_name tb from information_schema.tables where table_schema='prefixdb') A,(SELECT 'dr_' prfx) B;
+----------------------------------------------------------------+
| concat('alter table ',db,'.',tb,' rename ',db,'.',prfx,tb,';') |
+----------------------------------------------------------------+
| alter table prefixdb.tab1 rename prefixdb.dr_tab1; |
| alter table prefixdb.tab2 rename prefixdb.dr_tab2; |
| alter table prefixdb.tab3 rename prefixdb.dr_tab3; |
| alter table prefixdb.tab4 rename prefixdb.dr_tab4; |
+----------------------------------------------------------------+
4 rows in set (0.00 sec)
Pass the result back into mysql like this:
mysql -hhostip -uuser -pass -AN -e"select concat('alter table ',db,'.',tb,' rename ',db,'.',prfx,tb,';') from (select table_schema db,table_name tb from information_schema.tables where table_schema='prefixdb') A,(SELECT 'dr_' prfx) B" | mysql -hhostip -uuser -ppass -AN
With regard to your question if you want to rename all tables, do not touch information_schema and mysql databases. Use this query instead:
select
concat('alter table ',db,'.',tb,' rename ',db,'.',prfx,tb,';')
from
(select table_schema db,table_name tb
from information_schema.tables where
table_schema not in ('information_schema','mysql')) A,
(SELECT 'dr_' prfx) B
;
Give it a Try !!!
In case anyone runs into this error:
#1044 - Access denied for user 'user_name'#'localhost' to database 'information_schema'
After executing the queries resulting from the answer posted by #abhay, use the following instead to eliminate the issue:
SELECT Concat('RENAME TABLE `', TABLE_NAME, '` TO `dr_', TABLE_NAME, '`;') FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '<database_name>'
To add to Rolando's excellent and comprehensive answer. If you run the RENAME queries generated by his SELECT CONCAT query (I used phpMyAdmin and just copied the output queries into the SQL box and hit Go) then you shouldn't have any problems with foreign key constraints. I've just done this on a test database in XAMPP and all tables renamed ok, and the constraints remained intact. This may seem obvious to SQL experts but perhaps not so much to us amateurs.
Add a prefix to all of your database tables.
Supposing that your database was called "my_database" and the prefix you want to add is "my_prefix", execute the following query:
SELECT Concat('ALTER TABLE ', TABLE_NAME, ' RENAME TO my_prefix_', TABLE_NAME, ';') FROM information_schema.tables WHERE table_schema = 'my_database'
Your result set will be a bunch of queries that you can copy and paste into your favorite MySQL editor (Sequel Pro, phpMyAdmin, whatever). Just paste those in and execute, and you're all done.

Get storage engine in MySql

how do i get the storage engine used by a specific database in MySql?
Thanks
or by querying information_schema:
SELECT engine
FROM information_schema.tables
WHERE table_schema = 'test'
AND table_name = 'q';
+--------+
| engine |
+--------+
| MyISAM |
+--------+
1 row in set (0.01 sec)
SHOW CREATE TABLE table_name
will work for a given table.

Dynamically choosing a column in MySQL

I know how to pipe one MySQL query into another:
SELECT user_name FROM users WHERE user_id=( SELECT user_id FROM entries WHERE header="foo" );
Out of pure intellectual curiosity, how I dynamically choose a column or a table?
Ex:
SELECT (
SELECT column_name FROM column_names WHERE id = 1
) FROM (
SELECT table_name FROM table_names WHERE id = 1
);
Use a prepared statement:
mysql> SET #sql = CONCAT("SELECT ", (SELECT "NOW()"));
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT #sql;
+--------------+
| #sql |
+--------------+
| SELECT NOW() |
+--------------+
1 row in set (0.00 sec)
mysql> PREPARE stmt FROM #sql;
Query OK, 0 rows affected (0.00 sec)
Statement prepared
mysql> EXECUTE stmt;
+---------------------+
| NOW() |
+---------------------+
| 2009-04-06 23:08:31 |
+---------------------+
1 row in set (0.00 sec)
I'm pretty sure this is impossible with a regular query or view.
In answer to your first question, you should learn about how to do JOIN in SQL. A join is a fundamental operation in the SQL language. It's as important is understanding how to do a loop in other languages.
SELECT DISTINCT users.user_name
FROM users JOIN entries USING (user_id)
WHERE entries.header = 'foo';
Regarding your second question, no, you can't make table names or column names dynamic within a single statement.
However, you can write code in your application to build a SQL statement as a string, based on looking up column names and table names. Then execute the resulting string as a new SQL query.
You can do it by querying the information_schema.columns table.
Do this and check the results. I'm not sure what you're trying to do but that table contains anything related to your columns:
SELECT * FROM information_schema.`COLUMNS` C;
BTW, I don't know any way of doing this in a single query. You should get the columns information and then create a new query in your coding language, whatever that is.