I'm reverse-engineering a MySQL database and I'd like to get a list of example values from every column in every table. I'd like to run a query like this:
select
table_name,
column_name,
group_concat(
(select distinct table_name.column_name limit 100)
separator ','
) as examples
from
information_schema.columns
where
table_schema = 'myschema'
;
I'd like the output to look something like this:
table1 column1 (123,124,234)
table1 column2 ('Joe','Sara','Bob')
MySQL won't accept table_name.column_name as valid syntax. What's the right way to write this query?
I think Sam, you are looking for something like that, or at least it would be a better approach:
select
table_name,
column_name,
group_concat((column_name) separator ',') as examples
from
information_schema.columns
where
table_schema = 'test'
GROUP BY table_name
;
Based on rene's suggestion, I wrote a stored procedure which outputs examples of values from each column in every table. It's ugly and slow, but it works. I'd welcome suggestions on how to improve this code.
DELIMITER //
CREATE PROCEDURE column_example_values(
IN db_name VARCHAR(64),
IN tbl VARCHAR(64),
IN col VARCHAR(64),
OUT result MEDIUMTEXT)
BEGIN
SET #s = CONCAT('SELECT GROUP_CONCAT(tbl1.',col,
' separator \',\') FROM (SELECT DISTINCT ',
col,' FROM ',db_name,'.',tbl,
' LIMIT 100) tbl1 INTO #result1');
PREPARE stmt FROM #s;
EXECUTE stmt;
SET result = IFNULL(#result1,'');
END;
//
DELIMITER ;
DELIMITER //
CREATE PROCEDURE all_columns_example_values(IN db_name VARCHAR(64))
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE tbl, col VARCHAR(64);
DECLARE cur1 CURSOR FOR
SELECT
table_name,
column_name
FROM
information_schema.columns
WHERE
table_schema = db_name
ORDER BY
table_name,
column_name;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
CREATE TEMPORARY TABLE results (
tbl VARCHAR(64), col VARCHAR(64), examples MEDIUMTEXT);
OPEN cur1;
read_loop: LOOP
FETCH cur1 INTO tbl, col;
IF done THEN
LEAVE read_loop;
END IF;
CALL column_example_values(db_name,tbl,col,#result);
INSERT INTO results (tbl, col, examples) VALUES (tbl, col, #result);
END LOOP;
CLOSE cur1;
SELECT * FROM results;
DROP TABLE results;
END;
//
DELIMITER ;
It can be called with
CALL all_columns_example_values('mydb');
Related
So I have a software that store data on those tables. I know how those tables start but there will be always a suffix to them that's a number which I have no idea to know
example of those table name is "itemid5_4423"
I know there is a table with the name itemid5 but i have no way to know the suffix number
is there a wild card something similar to this logic select * from itemid5_*;
Let's say you have 2 tables like this:
create table itemid5_1111 (id int, itemname varchar(100));
create table itemid5_2222 (id int, itemname varchar(100));
You insert data into them:
insert into itemid5_1111 values (1, 'first table');
insert into itemid5_2222 values (2, 'second table');
Your goal is to get output like this from all itemid5* tables.
+------+--------------+
| id | itemname |
+------+--------------+
| 1 | first table |
| 2 | second table |
+------+--------------+
You can do that by typing:
select * from itemid5_1111
union all select * from itemid5_2222;
But, that's a lot of manual typing. You can make a stored procedure to dynamically query table names starting with itemid5 and then create a SQL dynamically and execute it.
Stored procedure
delimiter $$
drop procedure if exists get_items$$
create procedure get_items()
begin
declare eof boolean default false;
declare mytable varchar(255);
declare first_run boolean default true;
declare tablenames_cursor cursor for
select table_name from information_schema.tables
where table_name like 'itemid%';
declare continue handler for not found
set eof = true;
set #my_query = '';
open tablenames_cursor;
read_loop: loop
fetch tablenames_cursor into mytable;
if eof then
leave read_loop;
end if;
if first_run then
set #my_query = concat('select * from ', mytable);
set first_run = false;
else
set #my_query = concat(#my_query, ' union all ', 'select * from ', mytable);
end if;
end loop;
close tablenames_cursor;
prepare stmt from #my_query;
execute stmt;
deallocate prepare stmt;
end$$
delimiter ;
You call this procedure like so to get your results:
call get_items();
If you created a 3rd table like so:
create table itemid5_3333 (id int, itemname varchar(100));
insert into itemid5_3333 values (3, 'third table');
And then, you called the proc, you'd get
call get_items();
+------+--------------+
| id | itemname |
+------+--------------+
| 1 | first table |
| 2 | second table |
| 3 | third table |
+------+--------------+
i think using the data dictionary to retrieve the result would help, run this.
select * from information_schema.tables where table_name like 'itemid5_% ';
you can choose the columns you want that this query outputs, table_name is one of them you need, like we used it in where clause.
SELECT
REPLACE
(
GROUP_CONCAT(
CONCAT("SELECT * FROM ", `TABLE_NAME`)
),
",",
" UNION ALL "
)
INTO #sq
FROM
information_schema.tables
WHERE
`TABLE_SCHEMA` = "test";
USE
test;
PREPARE
stmt1
FROM
#sq;
EXECUTE
stmt1;
DELIMITER //
CREATE PROCEDURE merge_tables(IN in_sname VARCHAR(64),IN in_tname VARCHAR(64))
READS SQL DATA
BEGIN
DECLARE sname VARCHAR(64);
DECLARE tname VARCHAR(64);
DECLARE cname VARCHAR(64);
DECLARE done INT DEFAULT FALSE;
DECLARE table_cur CURSOR FOR SELECT table_schema, table_name FROM
information_schema.TABLES WHERE table_schema = in_sname AND table_name LIKE
'table%';
DECLARE column_cur CURSOR FOR SELECT `COLUMN_NAME` FROM
`INFORMATION_SCHEMA`.`COLUMNS` where table_schema = in_sname and table_name
= in_tname;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
-- build column list (Using the column list for table listed in second
parameter in PROC Call)
SET #column = '';
OPEN column_cur;
column_cur_loop: LOOP
FETCH column_cur INTO cname;
IF done THEN
-- SET #column := CONCAT(#column, ') ');
LEAVE column_cur_loop;
END IF;
IF #column = '' THEN
SET #column := CONCAT(#column,cname);
ELSE
SET #column := CONCAT(#column,',',cname);
END IF;
END LOOP;
CLOSE column_cur;
-- Build UNION Query for all table starting with table%)
SET done = FALSE;
SET #sql = '';
OPEN table_cur;
table_list_loop: LOOP
FETCH table_cur INTO sname, tname;
IF done THEN
LEAVE table_list_loop;
END IF;
IF #sql = '' THEN
SET #sql := CONCAT('INSERT INTO MERGED_TABLE (', #column , ') SELECT
', #column , ' FROM `', sname, '`.`', tname, '`');
ELSE
SET #sql := CONCAT(#sql, ' UNION ALL SELECT ' , #column , ' FROM `',
sname, '`.`', tname, '`');
END IF;
END LOOP;
CLOSE table_cur;
PREPARE stmt FROM #sql; -- prepare and execute the dynamically
EXECUTE stmt; -- created query.
DEALLOCATE PREPARE stmt;
END //
DELIMITER ;`
call merge_tables(testdb,table1)
testdb is Schema Name where tables reside
table1 is one of the tables which needs to be merged to get column names
table% in the procedure is the prefix of the all the tables that needs to be merged.
My problem: I have a few tables in mysql database with column "URL". I want to extract all of URLs to text file or another table.
Is it good way to go?
DELIMITER $$
CREATE or replace PROCEDURE link()
BEGIN
DECLARE finished INTEGER DEFAULT 0;
DECLARE tablename varchar(1000) DEFAULT "";
DECLARE link_tables
CURSOR for
SELECT table_name FROM information_schema.columns WHERE table_name like '__baza%' and COLUMN_NAME like 'url';
DECLARE CONTINUE HANDLER
FOR NOT FOUND SET finished = 1;
OPEN link_tables;
getTable: LOOP
fetch link_tables into tablename;
IF finished = 1 THEN s
LEAVE getTable;
END IF;
select url from tablename into ... <<<< is it good idea? what to do next?
END LOOP getTable;
close link_tables;
END$$
DELIMITER ;
CALL link();
CREATE PROCEDURE link_urls (prefix VARCHAR(64))
BEGIN
SELECT GROUP_CONCAT( CONCAT( 'SELECT url FROM ', table_name) SEPARATOR ' UNION ')
INTO #sql
FROM information_schema.columns
WHERE table_name like CONCAT(prefix, '%');
SET #sql = CONCAT( 'CREATE TABLE urls ', #sql );
PREPARE stmt FROM #sql;
EXECUTE stmt;
DROP PREPARE stmt;
END
fiddle
I want to alter my tables dynamically based on whether the table has specific column.
My database name is summer_cms, and there are over 50 tables in it.
What I want are below:
If a table has a column named add_time, then I would like to add a column add_user_id in it.
Similarly, I would like to add update_user_id in the table if update_time is found.
I know I should get it down in the process of creating the database schemas, but my database has been built and I have to alter it by need.
So I create a procedure to do it:
CREATE PROCEDURE ALTER_SUMMER_TABLE()
BEGIN
DECLARE tableName VARCHAR(64);
DECLARE exitence VARCHAR(64);
DECLARE ntable INT; # number of tables
DECLARE i INT; # index
SET i = 0;
# get the count of table
SELECT COUNT(DISTINCT(TABLE_NAME)) INTO ntable FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'summer_cms';
WHILE i < ntable DO
# select the specific table name into the variable of `tableName`.
SELECT TABLE_NAME INTO tableName
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'summer_cms'
AND COLUMN_NAME = 'add_time'
LIMIT 1 OFFSET i;
# alter table, but I get error in this clause.
ALTER TABLE tableName ADD COLUMN `add_user_id` INT NOT NULL DEFAULT 0 COMMENT 'add user id';
# check if the table has `update_time`
SELECT TABLE_NAME INTO exitence
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'summer_cms'
AND TABLE_NAME = tableName
AND COLUMN_NAME = 'update_time';
# add `update_user_id` if `update_time` be found.
IF exitence THEN
ALTER TABLE tableName ADD COLUMN `update_user_id` INT NOT NULL DEFAULT 0 COMMENT 'update user id';
END IF;
SET i = i + 1;
END WHILE;
END
But I got an error when I call this procedure.
Procedure execution failed
1146 - Table 'summer_cms.tableName' doesn't exist
Dose anyone could tell me what I was missing or wrong? Any help will be appreciated.
There a a few alterations you can make to your procedure to make it more streamlined as well as getting round a few problems.
First using a cursor to select the table names rather than using the two selects your using. Secondly to use a prepared statement to allow you to dynamically set the table name...
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `ALTER_SUMMER_TABLE`()
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE tableName VARCHAR(64);
declare cur cursor for SELECT TABLE_NAME
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'summer_cms'
AND COLUMN_NAME = 'add_time';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
open cur;
start_loop: loop
fetch cur into tableName;
if (done = 1 )THEN
LEAVE start_loop;
END IF;
SET #sql = CONCAT('ALTER TABLE ', tableName,' ADD COLUMN `add_user_id` INT NOT NULL DEFAULT 0 ');
PREPARE stmt FROM #sql;
EXECUTE stmt;
end loop;
close cur;
END$$
DELIMITER ;
You could do a few tweaks - only fetch table names where the column doesn't already exist for example.
Here's an example of dynamic sql
drop procedure if exists alter_table;
delimiter //
CREATE DEFINER=`root`#`localhost` procedure alter_table()
begin
declare tablename varchar(20);
set tablename = 'u';
set #sqlstmt = concat('ALTER TABLE ', tableName, ' ADD COLUMN ', char(96), 'add_user_id', char(96), ' INT NOT NULL DEFAULT 0 COMMENT', char(39), 'add user id', char(39),';');
prepare stmt from #sqlstmt;
execute stmt;
deallocate prepare stmt;
end //
delimiter ;
Note I have used ascii backticks and single quotes.
I am trying this command to choose all wp_%_options tables in all sites:
SELECT * FROM (SELECT * FROM information_schema.tables WHERE table_name LIKE 'wp_%_options') as t
But I need to get one level deeper and get the option_name = 'template' in all of these folders.
Tried this:
SELECT * FROM (SELECT * FROM information_schema.tables WHERE table_name like 'wp_%_options') as t WHERE option_name='template'
Not working.
Any ideas?
You can not treat table name (or column name) as variable in your standard SELECT queries. To solve the task you have to write a stored procedure.
The stored procedure should:
Fetch all table names wp_%_options;
Walk through the fetched names and select from the second level what you need using PREPARE statement.
DELIMITER |
DROP PROCEDURE IF EXISTS get_my_tables;
CREATE PROCEDURE get_my_tables (IN option_name VARCHAR(255))
BEGIN
DECLARE table_name CHAR(255);
DECLARE done INT DEFAULT FALSE;
DECLARE first INT DEFAULT TRUE;
DECLARE cur1 CURSOR FOR SELECT `tables`.`TABLE_NAME` FROM `information_schema`.`tables` `tables` WHERE `tables`.`TABLE_NAME` LIKE 'wp_%_options';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur1;
read_loop: LOOP
FETCH cur1 INTO table_name;
IF done THEN
LEAVE read_loop;
END IF;
IF (first) THEN
SET first = FALSE;
DROP TABLE IF EXISTS `result_table`;
SET #sql = CONCAT("CREATE TEMPORARY TABLE `result_table` (SELECT * FROM `", table_name ,"` WHERE `option_name` = '", option_name, "')");
ELSE
SET #sql = CONCAT("INSERT INTO `result_table` SELECT * FROM `", table_name, "` WHERE `option_name` = '", option_name, "'");
END IF;
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END LOOP;
CLOSE cur1;
SELECT * FROM `result_table`;
END;
|
DELIMITER ;
CALL get_my_tables('template');
I tested this example now it works as expected. You should tune some logic for your needs. For example: how to work with temporary table, fetch fesult and maybe use UNION instead of temporary table.
This is what I did in the end:
UPDATE wp_2_options SET option_value='classic' WHERE option_name='template' OR option_name='stylesheet';
Then I created as many as I need in Excel. Took me 1 min to change them all.
Try using LIKE instead of =.
So that the second query will be:
SELECT * FROM (SELECT * FROM information_schema.tables WHERE table_name like 'wp_%_options') as t WHERE option_name LIKE 'template'
How can I UNION all results from stmtQuery to ONE RESULTS example results from table basia and Comments_11 .... etc
DELIMITER $$
DROP PROCEDURE IF EXISTS SearchUserY $$
CREATE PROCEDURE `SearchUserY`(IN UserIdValue INT(11) )
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE tableName VARCHAR(50);
DECLARE stmtFields TEXT ;
DECLARE columnName VARCHAR(50) default 'UserId';
DECLARE cursor1 CURSOR FOR
SELECT table_name
FROM information_schema.COLUMNS
WHERE table_schema = 'comments'
AND column_name LIKE '%UserId';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cursor1;
read_loop: LOOP
FETCH cursor1 INTO tableName;
IF done THEN
LEAVE read_loop;
END IF;
SET stmtFields = CONCAT('`',tableName,'`','.' , columnName ,'=', UserIdValue) ;
SET #stmtQuery=CONCAT(#sql,'SELECT Nick, Title, Content FROM ' ,'`',tableName,'`', ' WHERE ', stmtFields ) ;
select #stmtQuery;
END LOOP;
PREPARE stmt FROM #stmtQuery ;
EXECUTE stmt ;
DEALLOCATE PREPARE stmt;
CLOSE cursor1;
END
results example (select #stmtQuery):
SELECT Nick, Title, Content FROM `basia` WHERE `basia`.UserId=0
SELECT Nick, Title, Content FROM `Comments_11` WHERE `Comments_11`.UserId=0
... etc
I want get a one results from all this query but know I got only One results
Generate query in a loop using CONCAT function, add 'UNION' or 'UNION ALL' clause between them, then execute result query with a prepared statements.
Solution without cursor:
SET #resultQuery = NULL;
SELECT
GROUP_CONCAT(
DISTINCT
CONCAT('SELECT Nick, Title, Content FROM ', table_name, ' WHERE UserId = ', UserIdValue)
SEPARATOR '\r\nUNION\r\n'
)
INTO
#resultQuery
FROM
information_schema.COLUMNS
WHERE
table_schema = 'comments' AND column_name LIKE '%UserId';
SELECT #resultQuery;
It will produce result like this:
SELECT Nick, Title, Content FROM table1 WHERE UserId = 10
UNION
SELECT Nick, Title, Content FROM table2 WHERE UserId = 10
UNION
SELECT Nick, Title, Content FROM table3 WHERE UserId = 10
UNION
SELECT Nick, Title, Content FROM table4 WHERE UserId = 10
...
Increase group_concat_max_len variable if needed. It is the maximum allowed result length for the GROUP_CONCAT() function, default value = 1024.