traverse through the table and update columns with Null values - mysql

Iam working on MYSQL and need a stored procedure that will traverse through table A which has 100 columns and 50,000+ rows and for all Null values found in the table the SP will update it with blank values.
eg
UPDATE table A SET column1=' ' where column1 IS NULL;
column1 is mixture of null and filled and blank rows,but i want to update only null rows.
I do not want to hardcode the column names..want to fetch the columns one by one in the stored procedure itself.is this possible?

You could dynamically fetch the column names from your tables.
SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = 'my_database' AND TABLE_NAME = 'my_table';
And combine this using inner queries.

Use the mysql ifnull function.You will need the below for all hundred columns. This can be used on string/char as well as integer fields.
update table
set
field1=ifnull(field1, " "),
field2=ifnull(field2, " "),
field3=ifnull(field3, 0),
field4=ifnull(field4, "")....
If you don't want to hardcode the column names, you can build the above query as per the other answer:
SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = 'my_database' AND TABLE_NAME = 'my_table';
For each of the above rows, build a concatenated update statement.
DEClARE usercursor CURSOR FOR
SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = 'my_database' AND TABLE_NAME = 'my_table';
declare updatesql varchar(3000);
set updatesql = "update TABLE set ";
OPEN usercursor
get_users:LOOP
FETCH usercursor into v_username;
set updatesql = concat(v_username,"=ifnull(",updatesql)
set updatesql = concat(v_username,",' '),",updatesql)
END LOOP get_users

Related

Search a db for all tables that have a certain value in a column

Is it possible to search all tables in a DB for a certain value in a column? I have 30 tables in my DB. Not all of them are using the FK employee_no. Out of all the tables that do contain an employee_no column, not all tables will have a record entered for every employee.
I would like to get a list of all the tables that contain the value 6172817 for the employee_no column.
I know
SELECT *
FROM INFORMATION_SCHEMA.COLUMNS
WHERE COLUMN_NAME like '%employee_no'
will return all the tables with the column name employee_no, but now I want all the tables with the value 6172817 for employee_No
I have tried
SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE employee_no like '%6172817'
Is this possible?
So this is what i got so far (made in postegresql though so you'll need to convert to mysql):
DO $$
DECLARE
rowt text; -- cursor retun
rowf text; -- name of the current table that meets our requirement
rowfz text; -- formated rout
cr CURSOR FOR (SELECT t.table_name::text
FROM information_schema.tables t
INNER JOIN information_schema.columns c
ON c.table_name = t.table_name
AND c.table_schema = t.table_schema
WHERE c.column_name = 'employee_no' -- The column you're looking for here
AND t.table_schema NOT IN ('information_schema', 'my_db') -- Add any unwanted DB's
-- separated by comas
AND t.table_type = 'BASE TABLE'
ORDER BY t.table_name);
BEGIN
FOR rowt IN cr LOOP
rowfz := REPLACE (rowfz::text,'(','');
rowfz := REPLACE (rowfz::text,')','');
EXECUTE (concat(' SELECT ''',rowfz, ''' FROM ', rowfz,' WHERE ', rowfz,
'.employee_no LIKE '''%6172817''' ')) INTO rowf;
IF rowf IS NOT NULL -- prints out only the valid(not null) table names
RAISE NOTICE '%',rowf;
END IF;
END LOOP;
END $$;
This will tell you exactly what tables have what you want, however it won't be shown in a neat looking table(you might need to scroll through the result text).

How to loop through all the tables on a database to update columns

I'm trying to update a column (in this case, a date) that is present on most of the tables on my database. Sadly, my database has more than 100 tables already created and full of information. Is there any way to loop through them and just use:
UPDATE SET date = '2016-04-20' WHERE name = 'Example'
on the loop?
One painless option would be to create a query which generates the UPDATE statements you want to run on all the tables:
SELECT CONCAT('UPDATE ', a.table_name, ' SET date = "2016-04-20" WHERE name = "Example";')
FROM information_schema.tables a
WHERE a.table_schema = 'YourDBNameHere'
You can copy the output from this query, paste it in the query editor, and run it.
Update:
As #PaulSpiegel pointed out, the above solution might be inconvenient if one be using an editor such as HeidiSQL, because it would require manually copying each record in the result set. Employing a trick using GROUP_CONCAT() would give a single string containing every desired UPDATE query in it:
SELECT GROUP_CONCAT(t.query SEPARATOR '; ')
FROM
(
SELECT CONCAT('UPDATE ', a.table_name,
' SET date = "2016-04-20" WHERE name = "Example";') AS query,
'1' AS id
FROM information_schema.tables a
WHERE a.table_schema = 'YourDBNameHere'
) t
GROUP BY t.id
You can use SHOW TABLES command to list all tables in database. Next you can check if column presented in table with SHOW COLUMNS command. It can be used this way:
SHOW COLUMNS FROM `table_name` LIKE `column_name`
If this query returns result, then column exists and you can perform UPDATE query on it.
Update
You can check this procedure on sqlfiddle.
CREATE PROCEDURE UpdateTables (IN WhereColumn VARCHAR(10),
IN WhereValue VARCHAR(10),
IN UpdateColumn VARCHAR(10),
IN UpdateValue VARCHAR(10))
BEGIN
DECLARE Finished BOOL DEFAULT FALSE;
DECLARE TableName VARCHAR(10);
DECLARE TablesCursor CURSOR FOR
SELECT c1.TABLE_NAME
FROM INFORMATION_SCHEMA.COLUMNS c1
JOIN INFORMATION_SCHEMA.COLUMNS c2 ON (c1.TABLE_SCHEMA = c2.TABLE_SCHEMA AND c1.TABLE_NAME = c2.TABLE_NAME)
WHERE c1.TABLE_SCHEMA = DATABASE()
AND c1.COLUMN_NAME = WhereColumn
AND c2.COLUMN_NAME = UpdateColumn;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET Finished = TRUE;
OPEN TablesCursor;
MainLoop: LOOP
FETCH TablesCursor INTO TableName;
IF Finished THEN
LEAVE MainLoop;
END IF;
SET #queryText = CONCAT('UPDATE ', TableName, ' SET ', UpdateColumn, '=', QUOTE(UpdateValue), ' WHERE ', WhereColumn, '=', QUOTE(WhereValue));
PREPARE updateQuery FROM #queryText;
EXECUTE updateQuery;
DEALLOCATE PREPARE updateQuery;
END LOOP;
CLOSE TablesCursor;
END
This is just an example how to iterate through all tables in database and perform some action with them. Procedure can be changed according to your needs.
Assuming you are using MySQL, You can use Stored Procedure.
This post is a very helpful.
Mysql-loop-through-tables

MySQL Select columns where the column name contains a substring

I have a table with a lot of fields about a person and then several recommendations of other people.
They are named:
"recommendation_1_name" "recommendation_1_company" 'recommendation_1_contact"
"recommendation_2_name" "recommendation_2_company" "recommendation_2_contact"
and so on.
I am trying to come up with a statement that allows me to only get the recommendations.
I imported an excel file into the table so it's just one large table.
This is what I have and it is returning an Empty set.
select * from questionnaire where 'COLUMN_NAME' like '%recommendation%';
I've been playing around with it making a table with only the recommendation fields and it still doesn't return anything.
Mysql: select recommendation_1_name, recommendation_2_name etc... from (table) where (USER) = (USERID) or however you can uniquely identify that user.
This Query generates you dynamic a SELECT query with all fields like 'recommendation%'. You only must setup the Databasename, and the Tablename. You can directly query the result of my query or add the WHERE clause.
SELECT
CONCAT( 'SELECT ',
GROUP_CONCAT(COLUMN_NAME SEPARATOR ',\n')
)
FROM information_schema.columns
WHERE TABLE_SCHEMA = 'DBNAME'
AND TABLE_NAME = 'TABLENAME'
AND COLUMN_NAME LIKE 'recommendation%';
You really need to normalize your schema.
But just as an experiment and example for some other cases (maybe somebody really need it). Here is solution to get this case resolved using stored procedure:
CREATE PROCEDURE `get_recommendations`()
BEGIN
DECLARE Q VARCHAR(100);
DECLARE C_NAME VARCHAR(100);
DECLARE cur CURSOR FOR SELECT GROUP_CONCAT(column_name) as `columns`
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'test'
AND TABLE_NAME ='questionnaire'
AND COLUMN_NAME LIKE '%recommendation%'
;
SET Q = 'SELECT ';
OPEN cur;
FETCH cur INTO C_NAME;
SET Q = CONCAT(Q,C_NAME,' ');
CLOSE cur;
SET #Q = CONCAT(Q,'FROM questionnaire;');
PREPARE stmt FROM #Q;
EXECUTE stmt ;
END
Don't forget to replace TABLE_SCHEMA = 'test' with your real database name.

Count number of occurrences in SQL

I have a table with the following structure:
(table_name, column_name)
and for each row in this table I need to query the column_name in the table_name and do a COUNT(column_name) GROUP BY column_name of the values in there.
Currently I do
SELECT * FROM this table
/*and then*/ foreach row: do another query with:
SELECT column_name, COUNT(column_name) GROUP BY column_name
Is there any way I can do this in a single query?
Something like
SELECT column_name, COUNT(column_name)
GROUP BY column_name
FOREACH(SELECT table_name, column_name FROM my_initial_table)
I know the last query is not valid, it's just an example for what I am looking to to achieve if it's possible.
LE:
The table that tells me where to look has 2 varchar columns
Ex:
|++++++++++++++++++++++++++++++++
| table_name | column_name |
|+++++++++++++++++++++++++++++++|
| logsa | foo |
|===============================|
| logsa | bar |
|===============================|
| testx | baz |
|===============================|
This tells me that now i have too look in columns foo and bar of table logsa and column baz of table testx
Every column in every table has VARCHAR as datattype and i just need to count those that are the same. that's way i was doing
SELECT column_name, COUNT(column_name)
GROUP BY column_name
If you are working in MySql, you can't directly use parametrized column names.
There is an indirect way of doing this using stored procedures and prepared statements.
some sloppy first-draft code...
notice the difference between backticks ` and quotes '
CREATE PROCEDURE CountTables()
BEGIN
DECLARE done TINYINT DEFAULT 0;
DECLARE table_name varchar(30), colunn_name varchar(30);
DECLARE cur1 CURSOR FOR SELECT
table_name, column_name
FROM ColumnTable;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
CREATE TEMPORARY TABLE t1( table_name varchar(30), column_name varchar(30), count int);
OPEN cur1;
START TRANSACTION;
read_loop: LOOP
FETCH FROM cur1 INTO table_name, column_name;
IF done THEN LEAVE read_loop; END IF;
SET insert_sql = CONCAT( "INSERT INTO `t1`(`table_name`, `column_name`, `count`) SELECT ",
"'", table_name, "', '", column_name, "', count(`", column_name, "`)",
" FROM `", table_name, "`"
);
PREPARE insert_stmt FROM insert_sql;
EXECUTE insert_stmt;
DEALLOCATE PREPARE insert_stmt;
END LOOP;
COMMIT;
SELECT * FROM t1 GROUP BY column_name;
DROP TEMPORARY TABLE t1;
END;
Oh ya, don't forget to call the procedure:
CALL CountTables();
A subquery should be able to help you here. This is an example (untested) which should work with some refinement. I am going to call your table Schema for the purposes of my example, and in the subquery I am going to alias it as ColumnCount to make the code (hopefully) more readable.
SELECT Schema.table_name, Schema.column_name,
(SELECT COUNT(*) FROM Schema ColumnCount
WHERE Schema.column_name = ColumnCount.column_name) AS ColumnUsageCount
FROM Schema
No need to do a subselect, just do this, if there's only one table involved.
SELECT max(table_name) as table_name, column_name, COUNT(*) as occurrence
FROM initial_table
GROUP BY column_name
ORDER BY column_name
The Max(table_name) substitutes for the non-existent function Whatever(table_name).
In MySQL you can also use group_concat(table_name) as table_names. Try it out!
The ORDER BY is optional (and in MySQL not needed) but can be handy if you want it sorted by column name as well.
If you want to list the unique occurrences of the combi table_name + column_name do:
SELECT table_name, column_name, COUNT(*) as occurrence
FROM initial_table
GROUP BY table_name, column_name
ORDER BY table_name, column_name
in PHP
$result=mysql_query("SELECT column_name FROM $table_name");
$row_count = mysql_num_rows($result);
I'm not too familiar with mysql, but i think its along those lines

How to check if an index exists on a table field in MySQL

How do I check if an index exists on a table field in MySQL?
I've needed to Google this multiple times, so I'm sharing my Q/A.
Use SHOW INDEX like so:
SHOW INDEX FROM [tablename]
Docs: https://dev.mysql.com/doc/refman/5.0/en/show-index.html
Try:
SELECT * FROM information_schema.statistics
WHERE table_schema = [DATABASE NAME]
AND table_name = [TABLE NAME] AND column_name = [COLUMN NAME]
It will tell you if there is an index of any kind on a certain column without the need to know the name given to the index. It will also work in a stored procedure (as opposed to show index)
show index from table_name where Column_name='column_name';
SHOW KEYS FROM tablename WHERE Key_name='unique key name'
will show if a unique key exists in the table.
Use the following statement:
SHOW INDEX FROM *your_table*
And then check the result for the fields: row["Table"], row["Key_name"]
Make sure you write "Key_name" correctly
To look at a table's layout from the CLI, you would use
desc mytable
or
show table mytable
Adding to what GK10 suggested:
Use the following statement: SHOW INDEX FROM your_table
And then check the result for the fields: row["Table"],
row["Key_name"]
Make sure you write "Key_name" correctly
One can take that and work it into PHP (or other language) wrapped around an sql statement to find the index columns. Basically you can pull in the result of SHOW INDEX FROM 'mytable' into PHP and then use the column 'Column_name' to get the index column.
Make your database connection string and do something like this:
$mysqli = mysqli_connect("localhost", "my_user", "my_password", "world");
$sql = "SHOW INDEX FROM 'mydatabase.mytable' WHERE Key_name = 'PRIMARY';" ;
$result = mysqli_query($mysqli, $sql);
while ($row = $result->fetch_assoc()) {
echo $rowVerbatimsSet["Column_name"];
}
Try to use this:
SELECT TRUE
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
WHERE TABLE_SCHEMA = "{DB_NAME}"
AND TABLE_NAME = "{DB_TABLE}"
AND COLUMN_NAME = "{DB_INDEXED_FIELD}";
You can use the following SQL to check whether the given column on table was indexed or not:
select a.table_schema, a.table_name, a.column_name, index_name
from information_schema.columns a
join information_schema.tables b on a.table_schema = b.table_schema and
a.table_name = b.table_name and
b.table_type = 'BASE TABLE'
left join (
select concat(x.name, '/', y.name) full_path_schema, y.name index_name
FROM information_schema.INNODB_SYS_TABLES as x
JOIN information_schema.INNODB_SYS_INDEXES as y on x.TABLE_ID = y.TABLE_ID
WHERE x.name = 'your_schema'
and y.name = 'your_column') d on concat(a.table_schema, '/', a.table_name, '/', a.column_name) = d.full_path_schema
where a.table_schema = 'your_schema'
and a.column_name = 'your_column'
order by a.table_schema, a.table_name;
Since the joins are against INNODB_SYS_*, the match indexes only came from the INNODB tables only.
If you need to check if a index for a column exists as a database function, you can use/adopt this code.
If you want to check if an index exists at all regardless of the position in a multi-column-index, then just delete the part AND SEQ_IN_INDEX = 1.
DELIMITER $$
CREATE FUNCTION `fct_check_if_index_for_column_exists_at_first_place`(
`IN_SCHEMA` VARCHAR(255),
`IN_TABLE` VARCHAR(255),
`IN_COLUMN` VARCHAR(255)
)
RETURNS tinyint(4)
LANGUAGE SQL
DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT 'Check if index exists at first place in sequence for a given column in a given table in a given schema. Returns -1 if schema does not exist. Returns -2 if table does not exist. Returns -3 if column does not exist. If index exists in first place it returns 1, otherwise 0.'
BEGIN
-- Check if index exists at first place in sequence for a given column in a given table in a given schema.
-- Returns -1 if schema does not exist.
-- Returns -2 if table does not exist.
-- Returns -3 if column does not exist.
-- If the index exists in first place it returns 1, otherwise 0.
-- Example call: SELECT fct_check_if_index_for_column_exists_at_first_place('schema_name', 'table_name', 'index_name');
-- check if schema exists
SELECT
COUNT(*) INTO #COUNT_EXISTS
FROM
INFORMATION_SCHEMA.SCHEMATA
WHERE
SCHEMA_NAME = IN_SCHEMA
;
IF #COUNT_EXISTS = 0 THEN
RETURN -1;
END IF;
-- check if table exists
SELECT
COUNT(*) INTO #COUNT_EXISTS
FROM
INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = IN_SCHEMA
AND TABLE_NAME = IN_TABLE
;
IF #COUNT_EXISTS = 0 THEN
RETURN -2;
END IF;
-- check if column exists
SELECT
COUNT(*) INTO #COUNT_EXISTS
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_SCHEMA = IN_SCHEMA
AND TABLE_NAME = IN_TABLE
AND COLUMN_NAME = IN_COLUMN
;
IF #COUNT_EXISTS = 0 THEN
RETURN -3;
END IF;
-- check if index exists at first place in sequence
SELECT
COUNT(*) INTO #COUNT_EXISTS
FROM
information_schema.statistics
WHERE
TABLE_SCHEMA = IN_SCHEMA
AND TABLE_NAME = IN_TABLE AND COLUMN_NAME = IN_COLUMN
AND SEQ_IN_INDEX = 1;
IF #COUNT_EXISTS > 0 THEN
RETURN 1;
ELSE
RETURN 0;
END IF;
END$$
DELIMITER ;
You can't run a specific show index query because it will throw an error if an index does not exist. Therefore, you have to grab all indexes into an array and loop through them if you want to avoid any SQL errors.
Heres how I do it. I grab all of the indexes from the table (in this case, leads) and then, in a foreach loop, check if the column name (in this case, province) exists or not.
$this->name = 'province';
$stm = $this->db->prepare('show index from `leads`');
$stm->execute();
$res = $stm->fetchAll();
$index_exists = false;
foreach ($res as $r) {
if ($r['Column_name'] == $this->name) {
$index_exists = true;
}
}
This way you can really narrow down the index attributes. Do a print_r of $res in order to see what you can work with.