MySQL select from all tables with a column name - mysql

Let's say I query this:
SELECT TABLE_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE COLUMN_NAME like 'coduser';
it returns a list of tables which contains the column "coduser":
users
messages
passwords
photos
It's a 20+ items list.
I need to search on those tables all occurrences where "coduser" is equal to "5OEWP1BPSV".
SELECT * FROM tablenamehere WHERE coduser = "5OEWP1BPSV";
but I'm not using anything other than MySQL to do this.
Basically just search all tables where there is a column called "coduser" and coduser = "5OEWP1BPSV".

You will need to use dynamic sql and given the small numbers involved a cursor would be appropriate
drop table if exists t,t1;
create table t(id int auto_increment primary key , codeuser varchar(20));
create table t1(id int auto_increment primary key , codeuser varchar(20));
insert into t(codeuser) values
('aaa'),('5OEWP1BPSV');
insert into t(codeuser) values
('bbb');
drop procedure if exists p;
delimiter $$
create procedure p()
begin
declare tablename varchar(20);
declare finished int;
DECLARE GRD_CUR CURSOR FOR
SELECT TABLE_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE COLUMN_NAME like 'codeuser';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET FINISHED = 0;
OPEN GRD_CUR;
LOOPROWS:LOOP
FETCH GRD_CUR INTO tablename;
IF FINISHED = 0 THEN
LEAVE LOOPROWS;
END IF;
#build and execute dynamic sql
set #sql = concat('SELECT * FROM ' , tablename, ' WHERE codeuser = "5OEWP1BPSV"');
#select #sql;
prepare sqlstmt from #sql;
execute sqlstmt;
deallocate prepare sqlstmt;
END LOOP;
close grd_cur;
end $$
delimiter ;
call p();

Loop over the returned list of tables, and for each table in that list perform a
SELECT *
WHERE columnnamehere = "valueofcolumnhere";
Bear in mind that this will give you separate lists for each table within that FOR loop, so you would have to concatenate these in some way to get a complete list.

Related

Sending comma separated input params into the stored proc and looping each param in MYSQL

I have a simple stored procedure with comma separated input params. I will send Database Names in Json or Comma separated values.
Like DB1,DB2,DB3 etc..;
When ever I send that parameter will be loading that table in each of the Database.
I'm looking for loop condition and splitting values.
EXEC student_info (DBName = 'DB1,DB2,DB3,DB')
DELIMITER &&
ALTER PROCEDURE student_info (IN DBName varchar(100))
BEGIN
**Splitting comma separated**
**loop condition**
INSERT INTO #DBName.tbl_student(Name,Class)
SELECT Name,Class FROM DB.student_info ;
END &&
DELIMITER ;
What is the best way?
You need to use prepared statements (see: PREPARE)
drop procedure if exists student_info;
DELIMITER &&
CREATE PROCEDURE student_info(IN dbname varchar(100))
BEGIN
declare a CHAR(16);
declare s VARCHAR(400);
DECLARE cur1 CURSOR FOR
with recursive cte as (
select
#dbname as s1,
substring_index(substring_index(#dbname,',',1),',',-1) as s2,
1 as x
union all
select
s1,
substring_index(substring_index(s1,',',x+1),',',-1),
x+1
from cte
where x< (select length(s1)-length(replace(s1,',',''))+1)
)
select s2 from cte;
open cur1;
read_loop: loop
fetch cur1 into a;
set #s = CONCAT('INSERT INTO ',a,'.tbl_student(Name,Class) SELECT Name,Class FROM DB.student_info ');
PREPARE stmt1 FROM #s;
execute stmt1 ;
deallocate prepare stmt1;
END loop;
close cur1;
END &&
DELIMITER ;
I tested with this:
drop table if exists DB1.tbl_student;
drop table if exists DB2.tbl_student;
drop table if exists DB3.tbl_student;
drop table if exists DB.tbl_student;
drop table if exists DB.student_info;
create table DB.tbl_student(name varchar(100),class varchar(100));
create table DB1.tbl_student(name varchar(100),class varchar(100));
create table DB2.tbl_student(name varchar(100),class varchar(100));
create table DB3.tbl_student(name varchar(100),class varchar(100));
create table DB.student_info(name varchar(100),class varchar(100));
insert into DB.student_info values ('Willem', 'Mathematics');
insert into DB.student_info values ('John', 'Mathematics');
insert into DB.student_info values ('Trudy', 'Mathematics');
CALL student_info ('DB1,DB2,DB3,DB');
After this the data checked to be copied from student_info to the different tbl_student tables.
NOTE:
I would not create those tables, and copy the data, but I would use a view:
create view db1.tbl_student as select name,class from db.student_info;
When you create a view, there is no need to copy the data to the other database, because this view will be updated automatically when records in the table db.student_info are updated/deleted or inserted.
P.S. Above was testen on MySQL 8.x. Some functionality that is not available in earlier versions of MySQL (like WITH) is used.
(Answer to the original question, before the edit)
Use FIND_IN_SET instead of a loop
SELECT Name,Class
FROM student_info
where FIND_IN_SET(ID, studentID);

Selecting from tables that don't yet exist

I am creating some reporting which summarises user actions (clicks) from an online job board.
The MYSQL tables are structured so that there is one table for each days activities. The table names are standard (except for the date) e.g. 'User_Clicks_DD_MM_YYY'
I want to select from all of the tables (including future tables that have not yet been created) without having to revisit the code each day.
Does anyone know of a way that I can do this?
here is one way to approach this, it is a stored procedure that builds a query
what this does is, loops through a range of dates (you can substitute now() for the last date) and checks if a table exists for a particular date. if it does exist then it adds it to a select statement string. then the string is executed at the end of the proc.
create table tab_01_01_2020 (id int);
create table tab_02_01_2020 (id int);
insert into tab_01_01_2020 values (1);
insert into tab_02_01_2020 values (1);
DELIMITER $$
DROP PROCEDURE IF EXISTS test$$
CREATE PROCEDURE test()
BEGIN
DECLARE count INT DEFAULT 0;
DECLARE mydate DATE;
set mydate = '2020/01/01';
set #qry='select 0 as id where 1 = 0';
WHILE mydate < '2020/02/01' DO
SET count = count + 1;
set #day = (SELECT RIGHT(CONCAT('0',DAY(mydate)), 2));
set #mon = (SELECT RIGHT(CONCAT('0',MONTH(mydate)), 2));
set #tab = CONCAT('tab_',#day,'_',#mon,'_',YEAR(mydate));
IF (SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = #tab) = 1
THEN
set #qry = CONCAT(#qry, ' union select id from ', #tab);
END IF;
SET mydate = DATE_ADD(mydate, INTERVAL 1 DAY);
END WHILE;
select #qry;
prepare stmt from #qry;
execute stmt;
deallocate prepare stmt;
END$$
DELIMITER ;
call test();

Mysql calling procedure failed when dynamically alter table in it

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.

How do I select every row from a table based on a string containing the name of the table?

In MySQL, I have a number of procedures which are more or less identical - they all perform the same (or very similar) operations, but they perform it on different tables.
I'd like to reduce these to one procedure, parameterized by table name, if possible. For example, suppose I wanted to execute a generic select:
SELECT * FROM TableFor("TableName")
Is this (or anything similar) possible in MySQL? Is it possible in any SQL dialect?
Per Tomva's Answer
A full example:
DROP PROCEDURE IF EXISTS example;
CREATE PROCEDURE example(IN tablename VARCHAR(1000)) BEGIN
SET #statement = CONCAT('SELECT * FROM ', #tablename);
PREPARE statement FROM #statement;
EXECUTE statement;
DEALLOCATE PREPARE statement;
END;
CALL example('tablename');
You can do this with a prepared statement.
It will be something along the lines of
SET #stat = CONCAT('SELECT * FROM ', #tab');
PREPARE stat1 FROM #stat;
EXECUTE stat1;
DEALLOCATE PREPARE stat1;
Dynamic SQL does not work in a function, so make a Stored Procedure from this, and you will be able to provide the table parameter.
I am going to assume you know what a stored procedure is (I hope you do otherwise my answer will be useless)
First create a table object in your procedure
declare #tablenames table(name varchar)
insert into #MonthsSale (name) values ('firsttable')
insert into #MonthsSale (name) values ('secondtable')
...
You can add this little line to suppress the rows affected messages:
SET NOCOUNT ON
Then create a cursor for this table and a variable to save your table name
DECLARE #TABLENAME VARCHAR
DECLARE tables_cursor CURSOR FOR SELECT name FROM #tablenames
Then loop through cursor and execute your code for each table name
OPEN Tables_cursor
FETCH NEXT FROM Tables_cursor INTO #Tablename
WHILE ##FETCH_STATUS = 0
BEGIN
YOUR CODE USING THE #Tablename
END
CLOSE Tables_cursor
DEALLOCATE Tables_cursor

check if column exists before ALTER TABLE -- mysql

Is there a way to check if a column exists in a mySQL DB prior to (or as) the ALTER TABLE ADD coumn_name statement runs? Sort of an IF column DOES NOT EXIST ALTER TABLE thing.
I've tried ALTER IGNORE TABLE my_table ADD my_column but this still throws the error if the column I'm adding already exists.
EDIT: use case is to upgrade a table in an already installed web app-- so to keep things simple, I want to make sure the columns I need exist, and if they don't, add them using ALTER TABLE
Since mysql control statements (e.g. "IF") only work in stored procedures, a temporary one can be created and executed:
DROP PROCEDURE IF EXISTS add_version_to_actor;
DELIMITER $$
CREATE DEFINER=CURRENT_USER PROCEDURE add_version_to_actor ( )
BEGIN
DECLARE colName TEXT;
SELECT column_name INTO colName
FROM information_schema.columns
WHERE table_schema = 'connjur'
AND table_name = 'actor'
AND column_name = 'version';
IF colName is null THEN
ALTER TABLE actor ADD version TINYINT NOT NULL DEFAULT '1' COMMENT 'code version of actor when stored';
END IF;
END$$
DELIMITER ;
CALL add_version_to_actor;
DROP PROCEDURE add_version_to_actor;
Do you think you can try this?:
SELECT IFNULL(column_name, '') INTO #colName
FROM information_schema.columns
WHERE table_name = 'my_table'
AND column_name = 'my_column';
IF #colName = '' THEN
-- ALTER COMMAND GOES HERE --
END IF;
It's no one-liner, but can you at least see if it will work for you? At least while waiting for a better solution..
Utility functions and procedures
First, I have a set of utility functions and procedures that I use to do things like drop foreign keys, normal keys and columns. I just leave them in the database so I can use them as needed.
Here they are.
delimiter $$
create function column_exists(ptable text, pcolumn text)
returns bool
reads sql data
begin
declare result bool;
select
count(*)
into
result
from
information_schema.columns
where
`table_schema` = 'my_database' and
`table_name` = ptable and
`column_name` = pcolumn;
return result;
end $$
create function constraint_exists(ptable text, pconstraint text)
returns bool
reads sql data
begin
declare result bool;
select
count(*)
into
result
from
information_schema.table_constraints
where
`constraint_schema` = 'my_database' and
`table_schema` = 'my_database' and
`table_name` = ptable and
`constraint_name` = pconstraint;
return result;
end $$
create procedure drop_fk_if_exists(ptable text, pconstraint text)
begin
if constraint_exists(ptable, pconstraint) then
set #stat = concat('alter table ', ptable, ' drop foreign key ', pconstraint);
prepare pstat from #stat;
execute pstat;
end if;
end $$
create procedure drop_key_if_exists(ptable text, pconstraint text)
begin
if constraint_exists(ptable, pconstraint) then
set #stat = concat('alter table ', ptable, ' drop key ', pconstraint);
prepare pstat from #stat;
execute pstat;
end if;
end $$
create procedure drop_column_if_exists(ptable text, pcolumn text)
begin
if column_exists(ptable, pcolumn) then
set #stat = concat('alter table ', ptable, ' drop column ', pcolumn);
prepare pstat from #stat;
execute pstat;
end if;
end $$
delimiter ;
Dropping constraints and columns using the utilities above
With those in place, it is pretty easy to use them to check columns and constraints for existence:
-- Drop service.component_id
call drop_fk_if_exists('service', 'fk_service_1');
call drop_key_if_exists('service', 'component_id');
call drop_column_if_exists('service', 'component_id');
-- Drop commit.component_id
call drop_fk_if_exists('commit', 'commit_ibfk_1');
call drop_key_if_exists('commit', 'commit_idx1');
call drop_column_if_exists('commit', 'component_id');
-- Drop component.application_id
call drop_fk_if_exists('component', 'fk_component_1');
call drop_key_if_exists('component', 'application_id');
call drop_column_if_exists('component', 'application_id');
Make a count sentence with the example below by John Watson.
SELECT count(*) FROM information_schema.COLUMNS
WHERE COLUMN_NAME = '...'
and TABLE_NAME = '...'
and TABLE_SCHEMA = '...'
Save that result in an integer and then make it a condition to apply the ADD COLUMN sentence.
You can test if a column exists with:
IF EXISTS (
SELECT * FROM information_schema.COLUMNS
WHERE COLUMN_NAME = '...'
and TABLE_NAME = '...'
and TABLE_SCHEMA = '...')
...
Just fill in your column name, table name, and database name.
Although its quite an old post but still i feel good about sharing my solution to this issue. If column doesn't exist then an exception would occur definitely and then i am creating the column in table.
I just used the code below:
try
{
DATABASE_QUERY="SELECT gender from USER;";
db.rawQuery(DATABASE_QUERY, null);
}
catch (Exception e)
{
e.printStackTrace();
DATABASE_UPGRADE="alter table USER ADD COLUMN gender VARCHAR(10) DEFAULT 0;";
db.execSQL(DATABASE_UPGRADE);
}
You can create a procedure with a CONTINUE handler in case the column exists (please note this code doesn't work in PHPMyAdmin):
DROP PROCEDURE IF EXISTS foo;
CREATE PROCEDURE foo() BEGIN
DECLARE CONTINUE HANDLER FOR 1060 BEGIN END;
ALTER TABLE `tableName` ADD `columnName` int(10) NULL AFTER `otherColumn`;
END;
CALL foo();
DROP PROCEDURE foo;
This code should not raise any error in case the column already exists. It will just do nothing and carry on executing the rest of the SQL.
DELIMITER $$
DROP PROCEDURE IF EXISTS `addcol` $$
CREATE DEFINER=`admin`#`localhost` PROCEDURE `addcol`(tbn varchar(45), cn varchar(45), ct varchar(45))
BEGIN
#tbn: table name, cn: column name, ct: column type
DECLARE CONTINUE HANDLER FOR 1060 BEGIN END;
set cn = REPLACE(cn, ' ','_');
set #a = '';
set #a = CONCAT("ALTER TABLE `", tbn ,"` ADD column `", cn ,"` ", ct);
PREPARE stmt FROM #a;
EXECUTE stmt;
END $$
DELIMITER ;
This syntax work for me :
SHOW COLUMNS FROM < tablename > LIKE '< columnName >'
More in this post :
https://mzulkamal.com/blog/mysql-5-7-check-if-column-exist?viewmode=0
As per MYSQL Community:
IGNORE is a MySQL extension to standard SQL. It controls how ALTER TABLE works if there are duplicates on unique keys in the new table or if warnings occur when strict mode is enabled. If IGNORE is not specified, the copy is aborted and rolled back if duplicate-key errors occur. If IGNORE is specified, only one row is used of rows with duplicates on a unique key. The other conflicting rows are deleted. Incorrect values are truncated to the closest matching acceptable value.
So a working Code is:
ALTER IGNORE TABLE CLIENTS ADD CLIENT_NOTES TEXT DEFAULT NULL;
Data posted here:
http://dev.mysql.com/doc/refman/5.1/en/alter-table.html