Search all columns in a table - mysql

I want a procedure that will search all columns for non keyboard ascii characters (Dec 16 to Dec 31 or DLE to US) and update the column by replacing them with a space ' ' or nothing ''.
I have a SELECT statement that is finding the rows I need to update, but I have to manually change all columns myself.
SELECT column_name
FROM table_name
WHERE column_name REGEXP '[[.DLE.]-[.US.]]'
Here's the UPDATE script for modifying the column values
UPDATE table
SET
column = replace(column,char(16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31), '')
I want those two to be fused together to a single function or stored procedure but I don't know how, since I'm just starting to learn MySQL.

Disclaimer
Between using REGEXP and CURSORs to loop through each table and column, these examples are not going to be lightning fast. The speed will obviously vary depending on your environment and I suggest testing them out on development before production
One column in one table
To search a single column on a single table, you basically had the UPDATE as you needed it.
UPDATE t1
SET
column_name = replace(column_name,
char(16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31), '')
WHERE column_name REGEXP '[[.DLE.]-[.US.]]'
All columns in one table
To do all columns in a table, you need to identify the table, then loop through the columns using a cursor
DELIMITER $$
CREATE PROCEDURE table_regexp_replace(in_table VARCHAR(128))
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE search_column VARCHAR(64);
DECLARE cur1 CURSOR FOR
SELECT DISTINCT `COLUMN_NAME` FROM `information_schema`.`COLUMNS`
WHERE `TABLE_NAME` = in_table ORDER BY `ORDINAL_POSITION` ;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur1;
read_loop: LOOP
-- Process the next column
FETCH cur1 INTO search_column;
-- If we're done, stop the loop
IF done THEN
LEAVE read_loop;
END IF;
-- Replace everything in this column matching the regexp
SET #new_query := CONCAT ('UPDATE ', in_table,
' SET `', search_column, '` = replace(', search_column,
', char(16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31), \'\')
WHERE ', search_column, ' REGEXP \'[[.DLE.]-[.US.]]\'') ;
PREPARE stmt FROM #new_query;
EXECUTE stmt ;
END LOOP;
CLOSE cur1;
END$$
DELIMITER ;
Then usage
CALL table_regexp_replace('my_table');
How it works
Looks convoluted, it's actually pretty straight forward.
We create a procedure with one parameter in_table which is used to specify the table to work with.
Setup a cursor that pulls the column names from the information_schema table, in their correct order
Loop through each of those columns, executing the manually created UPDATE statement against each one.
You'll notice anywhere in the UPDATE query that required quotes, they've had to be escaped using \.
\'[[.DLE.]-[.US.]]\'
All columns in all tables
You could then use this procedure in a loop through all tables, using a similar method to above. Below is how you'd pull all the table names from information_schema:
SELECT DISTINCT TABLE_NAME FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'your_database_name';

Related

MySQL dropping the indexes (with more than one field) of all exsisting tables except for primary indexes

I am struggling with deleting indexes of each table
that seems quite easy but there are some catches. drop clause takes only one index to be deleted
and mysql gives me an error when i am trying to make a second loop ( which would go through indexes so that i could delete needed ones)
delimiter &&
CREATE PROCEDURE dropping_indexes_excpt_for_primaryone()
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE tbl_name VARCHAR(200) DEFAULT '';
DECLARE all_tables_cursor CURSOR for
(SELECT TABLE_NAME
FROM information_schema.tables
WHERE table_schema = DATABASE()
AND table_type = 'BASE TABLE');
DECLARE CONTINUE handler FOR NOT FOUND SET done = 1;
OPEN all_tables_cursor;
tables_loop: LOOP
fetch all_tables_cursor INTO tbl_name;
if done then
leave tables_loop;
END if;
--------------------------------------------------------------------------
#how can i delete them using the following statement?
#SELECT index_name FROM information_schema.STATISTICS WHERE TABLE_NAME= tbl_name AND table_schema= DATABASE()
AND index_name <>'PRIMARY'
GROUP BY index_name
HAVING COUNT(*)>1
---------------------------------------------
END loop tables_loop;
close all_tables_cursor;
END;
&&
i tried to make a second loop but it didn't work because i needed to have some cursor to store it in
it is impossible to declare cursor not in the begining because table hasn`t been taken yet and ,to the best of my knowledge, it is not legitimate to use declare clause inside the loop (or in general - not in the begining)
i also tried to create a separate procedure to do it but something went wrong there,too
but i would like to do it in one stored procedure

SQL get column names in a loop for aggregate function

Lets make it simple: I need counts of all columns from a table. Is it possible to loop through a table names instead of typing each COUNT(column_name) for every single column (if there are >20 columns)?
SELECT COUNT(column_1) AS column_1,
COUNT(column_2) AS column_2,
COUNT(column_3) AS column_3,
COUNT(column_4) AS column_4
FROM table
It would be nice to know a general principle, which could be applied not only for COUNT() but in other situations as well.
You can create the query using dynamic SQL in a stored procedure. You get the column names from the INFORMATION_SCHEMA.COLUMNS table.
SET #cols = (
SELECT GROUP_CONCAT('COUNT(`', column_name, '`) AS `', column_name, '`'))
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'table');
PREPARE #stmt FROM CONCAT('SELECT ', #cols, ' FROM table');
EXECUTE #stmt;
You can use the INFORMATION_SCHEMA.COLUMNS to get all the column names of a table. Then You can use procedure or anonymous block to loop through all columns and store the results in some variable or insert into some table
CREATE PROCEDURE curdemo()
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE col_names CURSOR FOR
SELECT column_name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'tbl_name'
ORDER BY ordinal_position;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN col_names;
read_loop: LOOP
FETCH col_names INTO my_col;
IF done THEN
LEAVE read_loop;
END IF;
--Your Operation goes here
--select count(mycol) insert into yourtab
END LOOP;
CLOSE cur1;
END;
I'm not really sure what you mean. COUNT() returns the number of records and that would be the same for each column in a table. A row is a row.
Is it the number of columns in the table you are asking for? If so, one way is to use the INFORMATION_SCHEMA.COLUMNS like this:
SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = N'your_table_name'

mysql - create dynamic union

Database is mysql/mariaDB.
I have database designed to store monthly reports about something. and their names are (example): table1, table2, table3...
I want to create function/procedure that will create/recreate view that contains all tables union (union ALL).
Something like:
1. first select all table names from information schema.
SELECT TABLE_NAME from information_schema.`TABLES` where TABLE_NAME like 'table%'
then i would try to set it in some loop to use result set from first query.
But i have problem with first step where i try to merge only one fixed table + one from first query and it returns error to me.
i try:
select * from `table4`
UNION
SELECT * from (SELECT TABLE_NAME from information_schema.`TABLES`
where TABLE_NAME like 'table%' limit 1) as dd
it returns me error: The used SELECT statements have a different number of columns ,
but when i execute sub query i get 1 result with correct name of table, and when i set that name in from clause without sub query, it works.
Any idea why it is happening, and maybe some advice how to accomplish that dynamic union.
I think a little push will help you to the correct way of handling this problem.
First, as Tim Biegeleisen suggests, the way to proceed is to use dynamic SQL, this is your only avenue if the table names cannot be absolutely determined before you try to run the query.
Second, you are correct to think that you need to start by querying the information_schema.TABLE, which you should do using a CURSOR. The results from that query should then be used to build up a query string which you then PREPARE and EXECUTE.
Third, I take it that the error message you included in your post refers specifically to the running of that query and doesn't indicate that the monthly tables differ in any way. You can't do a UNION unless the results from each part return the same number of columns.
Fourth, because we are going to build the query dynamically, this has to done within a stored procedure, it's not possible to do it in a stored function.
There are good tutorials in the mysql docs for using CURSOR and PREPARE/EXECUTE, which you should read. The version I give below will be based on those examples. I'm assuming the only input parameter will be the schema name (in case you happen to have some similarly named tables in another database on the server).
DELIMITER //
DROP PROCEDURE IF EXISTS dyn_union //
CREATE PROCEDURE dyn_union(IN v_sname VARCHAR(64))
READS SQL DATA
BEGIN
-- NB the order of declaration for variables cursor
-- and handler must be strictly observed
DECLARE sname VARCHAR(64); -- variable the schema names
DECLARE tname VARCHAR(64); -- variable the table names
DECLARE done INT DEFAULT FALSE; -- cursor control variable
DECLARE cur1 CURSOR FOR
SELECT table_schema, table_name
FROM information_schema.TABLES
WHERE table_schema = v_sname
AND table_name LIKE 'table%';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
SET #sql = ''; -- build the query string in this var
OPEN cur1;
read_loop: LOOP -- loop over the rows returned by cursor
FETCH cur1 INTO sname, tname; -- fetching the schema and table names
IF done THEN
LEAVE read_loop;
END IF;
IF #sql = '' THEN -- build the select statement
SET #sql := CONCAT('SELECT * FROM `', sname, '`.`', tname, '`');
ELSE
SET #sql := CONCAT(#sql, ' UNION ALL SELECT * FROM `', sname, '`.`', tname, '`');
END IF;
END LOOP;
CLOSE cur1;
select #sql;
PREPARE stmt FROM #sql; -- prepare and execute the dynamically
EXECUTE stmt; -- created query.
DEALLOCATE PREPARE stmt;
END //
DELIMITER ;
-- call the procedure
CALL dyn_union('your_db_name');

Updating empty string to NULL for entire database

I'm performing some database clean up and have noticed that there are a lot of columns that have both empty strings and NULL values in various columns.
Is it possible to write an SQL statement to update the empty strings to NULL for each column of each table in my database, except for the ones that do not allow NULL's?
I've looked at the information_schema.COLUMNS table and think that this might be the place to start.
It's not possible to do this with one simple SQL statement.
But you can do it using one statement for each column.
UPDATE TABLE SET COLUMN = NULL
WHERE LENGTH(COLUMN) = 0
or, if you want to null out the items that also have whitespace:
UPDATE TABLE SET COLUMN = NULL
WHERE LENGTH(TRIM(COLUMN)) = 0
I don't think it's possible within MySQL but certainly with a script language of your choice.
Start by getting all tables SHOW TABLES
Then for each table get the different columns and find out witch ones allow null, either with DESC TABLE, SHOW CREATE TABLE or SELECT * FROM information_schema.COLUMNS, take the one you rather parse
Then for each column that allows null run a normal update that changes "" to null.
Prepare to spend some time waiting :)
I figured out how to do this using a stored procedure. I'd definitely look at using a scripting language next time.
DROP PROCEDURE IF EXISTS settonull;
DELIMITER //
CREATE PROCEDURE settonull()
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE _tablename VARCHAR(255);
DECLARE _columnname VARCHAR(255);
DECLARE cur1 CURSOR FOR SELECT
CONCAT(TABLE_SCHEMA, '.', TABLE_NAME) AS table_name,
COLUMN_NAME AS column_name
FROM information_schema.COLUMNS
WHERE IS_NULLABLE = 'YES'
AND TABLE_SCHEMA IN ('table1', 'table2', 'table3');
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur1;
read_loop: LOOP
FETCH cur1 INTO _tablename, _columnname;
IF done THEN
LEAVE read_loop;
END IF;
SET #s = CONCAT('UPDATE ', _tablename, ' SET ', _columnname, ' = NULL WHERE LENGTH(TRIM(', _columnname, ')) = 0' );
PREPARE stmt FROM #s;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END LOOP;
CLOSE cur1;
END//
DELIMITER ;
CALL settonull();

How to remove a prefix name from every table name in a mysql database

I have a joomla mysql database with a table name prefix of "jos_" on all of my table names. But I would like to remove it from all of my tables. I understand how to rename each table, one at a time, but I have 600 tables. Is there an easy to run a sql query to do this.
If someone has a solution, could you please post the exact sql query I can use?
In phpmyadmin select all tables of your database.
From the dropdown 'With selected:' choose 'Replace table prefix'
Set from->to replacement.
DONE
You can generate the necessary statements with a single query:
select 'RENAME TABLE ' || table_name || ' TO ' || substr(table_name, 5) ||';'
from information_schema.tables
Save the output of that query to a file and you have all the statements you need.
Or if that returns 0s and 1s rather the statemenets, here's the version using concat instead:
select concat('RENAME TABLE ', concat(table_name, concat(' TO ', concat(substr(table_name, 5), ';'))))
from information_schema.tables;
You can create your own stored procedure to rename your tables, with that you don't need to open an external editor everything will be done on the server:
delimiter //
CREATE PROCEDURE rename_tables( IN db CHAR(255), IN srch CHAR(255), IN rplc CHAR(255) )
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE from_table CHAR(255);
DECLARE cur1 CURSOR FOR SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA=db;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN cur1;
read_loop: LOOP
IF done THEN
LEAVE read_loop;
END IF;
FETCH cur1 INTO from_table;
SET #to_table = REPLACE(from_table, srch, rplc);
IF from_table != #to_table THEN
SET #rename_query = CONCAT('RENAME TABLE ', db, '.', from_table, ' TO ', #to_table, ';');
PREPARE stmt FROM #rename_query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END IF;
END LOOP;
CLOSE cur1;
END//
delimiter ;
Usage:
CALL rename_tables('test', 'jos_', '');
Update: This was my first MySQL stored procedure and I ran into the 6 years old bug #5967 which was quite annoying, your variable names must be different from the field names, because if they aren't you'll get NULL values in your variables.
So be aware of that if you decide to write a MySQL stored procedure.