Update Entire Table from 'empty' to 0 or null [duplicate] - mysql

I have a legacy table with about 100 columns (90% nullable). In those 90 columns I want to remove all empty strings and set them to null. I know I can:
update table set column = NULL where column = '';
update table set column2 = NULL where column2 = '';
But that is tedious and error prone. There has to be a way to do this on the whole table?

UPDATE
TableName
SET
column01 = CASE column01 WHEN '' THEN NULL ELSE column01 END,
column02 = CASE column02 WHEN '' THEN NULL ELSE column02 END,
column03 = CASE column03 WHEN '' THEN NULL ELSE column03 END,
...,
column99 = CASE column99 WHEN '' THEN NULL ELSE column99 END
This is still doing it manually, but is slightly less painful than what you have because it doesn't require you to send a query for each and every column. Unless you want to go to the trouble of scripting it, you will have to put up with a certain amount of pain when doing something like this.
Edit: Added the ENDs

One possible script:
for col in $(echo "select column_name from information_schema.columns
where table_name='$TABLE'"|mysql --skip-column-names $DB)
do
echo update $TABLE set $col = NULL where $col = \'\'\;
done|mysql $DB

For newbies, you may still need more work after seeing the above answers. And it's not realistic to type thousands lines.
So here I provide a complete working code to let you avoid syntax errors etc.
DROP PROCEDURE IF EXISTS processallcolumns;
DELIMITER $$
CREATE PROCEDURE processallcolumns ()
BEGIN
DECLARE i,num_rows INT ;
DECLARE col_name char(250);
DECLARE col_names CURSOR FOR
SELECT column_name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'PROCESSINGTABLE'
ORDER BY ordinal_position;
OPEN col_names ;
select FOUND_ROWS() into num_rows;
SET i = 1;
the_loop: LOOP
IF i > num_rows THEN
CLOSE col_names;
LEAVE the_loop;
END IF;
FETCH col_names
INTO col_name;
SET #command_text = CONCAT('UPDATE `PROCESSINGTABLE` SET ', col_name, '= IF(LENGTH(', col_name, ')=0, NULL,', col_name, ') WHERE 1 ;' ) ;
-- UPDATE `PROCESSINGTABLE` SET col_name=IF(LENGTH(col_name)=0,NULL,col_name) WHERE 1;
-- This won't work, because MySQL doesn't take varibles as column name.
PREPARE stmt FROM #command_text ;
EXECUTE stmt ;
SET i = i + 1;
END LOOP the_loop ;
END$$
DELIMITER ;
call processallcolumns ();
DROP PROCEDURE processallcolumns;

Hammerite's answer is good but you can also replace CASE statements with IFs.
UPDATE
TableName
SET
column01 = IF(column01 = '', NULL, column01),
column02 = IF(column02 = '', NULL, column02),
column03 = IF(column03 = '', NULL, column03),
...,
column99 = IF(column99 = '', NULL, column99)

There isn't a standard way - but you can interrogate the system catalog to get the relevant column names for the relevant table and generate the SQL to do it. You can also probably use a CASE expression to handle all the columns in a single pass - a bigger SQL statement.
UPDATE Table
SET Column1 = CASE Column1 = ' ' THEN NULL ELSE Column1 END,
...
Note that once you've generated the big UPDATE statement, all the work is done down in the server. This is much more efficient than selecting data to the client application, changing it there, and writing the result back to the database.

I think you'll need to pull each row into a language like C#, php, etc.
Something like:
rows = get-data()
foreach row in rows
foreach col in row.cols
if col == ''
col = null
end if
next
next
save-data()

You could write a simple function and pass your columns to it:
Usage:
SELECT
fn_nullify_if_empty(PotentiallyEmptyString)
FROM
table_name
;
Implementation:
DELIMITER $$
CREATE FUNCTION fn_nullify_if_empty(in_string VARCHAR(255))
RETURNS VARCHAR(255)
BEGIN
IF in_string = ''
THEN RETURN NULL;
ELSE RETURN in_string;
END IF;
END $$
DELIMITER ;

Related

How to find the count of nulls value of all the columns in a SQL table using procedures/UDF

I want to find null values of columns of SQL table using procedures/UDF.
We tried to find the null columns using case expression.
select sum(case when a is null then 1 else 0 end) count_a_nulls,
sum(case when b is null then 1 else 0 end) count_b_nulls,
sum(case when c is null then 1 else 0 end) count_c_nulls
from dummy;
Here the problem is that we don't want to put columns manually. If there are 50+ columns, we will have to add too many case statements.
Here is a dummy table.
Output
Somehow i am able to write stored procedure to find the null count of
columns of a table.
DELIMITER //
CREATE PROCEDURE `passTableDynamic5`(tables_name TEXT)
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE column_name_value VARCHAR(100) DEFAULT NULL;
DECLARE case_expression VARCHAR(500) DEFAULT NULL;
DECLARE SQL_QUERY VARCHAR(500) DEFAULT NULL;
DECLARE csr_var_list CURSOR FOR SELECT COLUMN_NAME FROM information_schema.`COLUMNS` WHERE TABLE_NAME=tables_name;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
SET case_expression ='';
OPEN csr_var_list;
get_id: LOOP
FETCH csr_var_list INTO column_name_value;
SELECT CONCAT("coulm_name", done);
IF done = 1 THEN
SELECT CONCAT("coulm_name", done);
SET case_expression =CONCAT(case_expression, ' sum(', column_name_value,' is NULL) count_',column_name_value);
LEAVE get_id;
END IF;
IF done = 0 THEN
SET case_expression = CONCAT(case_expression,' sum(', column_name_value,' is NULL) count_',column_name_value, ',');
END IF;
END LOOP get_id;
CLOSE csr_var_list;
SET SQL_QUERY = CONCAT('SELECT ', case_expression, ' From ', tables_name, ';');
PREPARE stmt FROM SQL_QUERY;
EXECUTE stmt;
END//
DELIMITER ;
Use this stored procedure like this
passTableDynamic5('table_name')

why mysql procedure return null

I have a simple mysql procedure which must return an query string. But, it returns almost every time QueryResult (column name) as <null> value.
create procedure return_table_rename_query(
IN targetTable VARCHAR(100),
IN tblPrefix VARCHAR(100)
)
BEGIN
SET #returnQuery = CONCAT('SELECT "MYSQLIMPORT can not rename table for target ', #targetTable, '";');
SET #totalRows = (SELECT COUNT(*) FROM table);
if IFNULL(#totalRows, 0) > 0
then
SET #returnQuery = CONCAT('drop table if exists table_name.', ...);
end if;
SELECT #returnQuery AS 'QueryResult';
end;
#targettable is not the same variable as targettable - you are mixing user defined variables and parameter variables and it seems likely that #targettable is null and if any element in a concat is null then the result is null.
Please read How to declare a variable in MySQL?

Unable to insert write data conditionaly inside trigger (IF, CONCAT, IFNULL) in MySQL

CREATE TRIGGER TR_Update_Member
AFTER UPDATE ON `member`
FOR EACH ROW
BEGIN
DECLARE changeNote VARCHAR(5000) DEFAULT '';
SET changeNote = IF(OLD.Name != NEW.Name,
CONCAT( changeNote,
'Name(',
IFNULL(OLD.Name, '--'),
'->',
IFNULL(NEW.Name, '--'),
'), '
),
changeNote);
SELECT TRIM(TRAILING ', ' FROM changeNote) INTO changeNote;
INSERT INTO `member_change_log`(`Name`) VALUES(changeNote)
END
The above trigger does not insert any data when the name contains null. Could anyone please what is wrong with my code.
The above trigger does not insert any data when the name contains null. Could anyone please what is wrong with my code.
If NEW.Name is NULL then OLD.Name != NEW.Name is NULL too, and IF() executes alternative variant, i.e. you obtain
SET changeNote = changeNote
Simply swap variants:
SET changeNote = IF( OLD.Name = NEW.Name, changeNote, CONCAT( ... ) );
If both OLD.Name and NEW.Name may be NULL then use null-safe compare operator <=> instead of regular compare =.

Check MySQL database for unique value over many tables

I'm looking for a way to easily check each table of a MySQL database and make sure that a certain field contains one value only. I have tables named Authors, Titles, Places, etc.
Each table contains a field called xuser and it needs to ask "does the field xuser contain the value xy in all records of all tables".
Can someone push me in the right direction how to do this with a SQL query if this is possible?
Thanks for reading, regards
Nico
I've created stored procedure which checks all table for provided db:
DELIMITER $$
DROP PROCEDURE IF EXISTS `UTL_CHECK_BACKUP_FOR_USER` $$
CREATE PROCEDURE `UTL_CHECK_BACKUP_FOR_USER`(
IN i_database_name VARCHAR(255),
IN i_user_column_name VARCHAR(255),
IN i_user_column_value VARCHAR(255),
OUT o_result TINYINT(1)
)
BEGIN
DECLARE v_table_name VARCHAR(255);
DECLARE v_last_row_fetched TINYINT(3) DEFAULT 0;
DECLARE tables_cursor CURSOR FOR
SELECT table_name
FROM information_schema.tables
WHERE table_schema = i_database_name
;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_last_row_fetched = 1;
SET v_last_row_fetched = 0;
OPEN tables_cursor;
SET #query =
CONCAT(
'SELECT SUM(IF(user_column=''',
i_user_column_value,
''', 1, -1)) = 1 INTO #o_result FROM ( SELECT ''test'' AS user_column FROM information_schema.tables WHERE 1<>1 '
)
;
table_loop: LOOP
FETCH tables_cursor INTO v_table_name;
IF (v_last_row_fetched = 1) THEN
LEAVE table_loop;
END IF;
SET #query =
CONCAT(
#query,
' UNION SELECT DISTINCT(',
i_user_column_name,
') AS user_column FROM ',
v_table_name
)
;
END LOOP table_loop;
CLOSE tables_cursor;
SET v_last_row_fetched=0;
SET #query =
CONCAT(
#query,
' ) all_xusers;'
)
;
PREPARE stmt FROM #query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET o_result = COALESCE(#o_result, 0);
END $$
DELIMITER ;
Just deploy this stored procedure to database.
And then it could be executed in the following way:
-- db_name, user_column_name, user_column_value, result
call UTL_CHECK_BACKUP_FOR_USER('test', 'xuser', 'xxx', #result);
select #result;
To get the rows from all three tables where xuser has the same value in all three tables you could use:
SELECT *
FROM authors a
JOIN titles t
ON t.xuser = a.xuser
JOIN places p
ON p.xuser = t.xuser
If you want to look at a specific xuser value you could add the following WHERE clause:
WHERE a.xuser = 'xy'
The first thing comes to my mind:
select sum(if(xuser='xxx', 1, -1)) = 1
from (
select distinct(xuser) from authors
union
select distinct(xuser) from titles
union
select distinct(xuser) from places
) all_xusers;
This will return 1 (true) if all tables contains records belonging ONLY to 'xxx' user. Otherwise (if there is no 'xxx' records or there is some other user records) it will return 0 (false).

Asking about stored function in mysql

drop function if exists rty_check_member_info_status;
DELIMITER $$
--
-- Functions
CREATE DEFINER=root#localhost FUNCTION rty_check_member_info_status(memb_id int,field_name_1 varchar(100),field_name_2 varchar(100),login_member_amount int(11),login_status char(1)) RETURNS char(1) CHARSET latin1
begin
declare fn_field_name_1 varchar(100) ;
declare fn_field_name_2 varchar(100) ;
declare fn_amount_for_profile_visible int(11);
declare fn_return char(1) default 'N';
declare test_field varchar(100);
select field_name_1,field_name_2,amount_for_profile_visible into
fn_field_name_1,fn_field_name_2,fn_amount_for_profile_visible
from member_account_settings inner join tbl_members on member_account_settings.member_auto_id = tbl_members.member_id
where tbl_members.member_id = memb_id ;
if fn_field_name_1 = 'H' Then
set fn_return = 'N' ;
else
if fn_field_name_2 = 'Y' Then
if fn_amount_for_profile_visible = '0' Then
set fn_return = 'Y' ;
else
if login_status = 1 Then
if fn_amount_for_profile_visible > login_member_amount Then
set fn_return = 'N' ;
else
set fn_return = 'Y' ;
end if;
else
set fn_return = 'N';
end if ;
end if;
else
set fn_return = 'Y';
end if ;
end if ;
return fn_return ;
end$$
DELIMITER ;
You're two choices are pretty much generated SQL(normally a bad idea because it's harder to write, debug and document) and using a case statement to select the column based on the name matching a string(which is normally a pretty good solution).
Here's an example of the second, since it's the solution I'd definitely recommend.
SET #test_field1 = "last_name_display_status" ;
SET #test_field2 = "last_name_display_for_other_partcpnt" ;
SELECT
CASE #test_field1
-- List columns here that you might want to return:
WHEN 'last_name_display_status' THEN last_name_display_status
WHEN 'last_name_display_for_other_partcpnt' THEN last_name_display_for_other_partcpnt
WHEN 'create_date' THEN create_date
-- Return a value for an invalid name here:
ELSE NULL
END AS test_field1,
CASE #test_field2
-- List columns here that you might want to return:
WHEN 'last_name_display_status' THEN last_name_display_status
WHEN 'last_name_display_for_other_partcpnt' THEN last_name_display_for_other_partcpnt
WHEN 'create_date' THEN create_date
-- Return a value for an invalid name here:
ELSE NULL
END AS test_field2,
-- Rest of select unaffected by this change
amount_for_profile_visible
INTO
fn_field_name_1,
fn_field_name_2,
fn_amount_for_profile_visible
FROM member_account_settings
INNER JOIN tbl_members
ON member_account_settings.member_auto_id = tbl_members.member_id
WHERE
tbl_members.member_id = memb_id
;
And for the sake of completeness a copy of the first solution I proposed(the generated SQL):
-- Need to use #vars, since named vars aren't in scope for the generated SQL:
SET #output1 = '';
SET #output2 = '';
SET #output3 = '';
SET #input1 = memb_id;
-- We also need to store our generated SQL to a variable
SET #query = 'SELECT ' + #test_field1 + ',' + #test_field2 + ', amount_for_profile_visible INTO #output1, #output2, #output3 FROM member_account_settings INNER JOIN tbl_members ON member_account_settings.member_auto_id = tbl_members.member_id WHERE tbl_members.member_id = ?';
-- To execute the code we have to convert it to a prepared statement
-- named stmt here, because it's what most people use in this instance
PREPARE stmt FROM #query;
-- Execute the statement using our input variable
EXECUTE stmt USING #input1;
-- Delete the prepared statement now we've run it.
DEALLOCATE PREPARE stmt;
-- Store our #vars back into the named vars.
SET fn_field_name_1 = #output1;
SET fn_field_name_2 = #output2;
SET fn_amount_for_profile_visible = #output3;