Wednesday, 4 July 2018

MySQL Stored procedure to Generate-Extract Insert Statement

MySQL Stored procedure to Generate-Extract Insert Statement
A lot of places I saw people asking for ways to generate Insert statements.
We do have GUI Tools which can extract insert statements for us readily. of the time I choose the MySQLDump way to generate insert statements.
mysqldump -uroot -ppassword –complete-insert –no-create-info DATABASE TABLENAME > TABLENAME.sql
But mind is very unstable and hungry, we don’t stop at one solution.
So to remove my mind’s starvation for the Stored Procedure way to extract Insert statement I created following routine.
As you can see this is really a simple procedure revolves around Information_schema mainly to get details of any table and then fires the simple sql query.
The procedure I named: InsGen
Input parameters:
in_db: Database name of the table for which you want to generate insert statements
in_table: Tabel name
in_file: complete file path [eg: C:/mysqlInserts.sql or /var/lib/data/mysqlInserts.sql]
DELIMITER $$
DROP PROCEDURE IF EXISTS `InsGen` $$
CREATE DEFINER=`root`@`localhost` PROCEDURE `InsGen`(in_db varchar(20),in_table varchar(20),in_file varchar(100))
BEGIN
declare Whrs varchar(500);
declare Sels varchar(500);
declare Inserts varchar(2000);
declare tablename varchar(20);
set tablename=in_table;
select tablename;
# Comma separated column names – used for Select
select group_concat(concat(‘concat(\'”\’,’,’ifnull(‘,column_name,’,””)’,’,\'”\’)’)) INTO @Sels from information_schema.columns where table_schema=in_db and table_name=tablename;
# Comma separated column names – used for Group By
select group_concat(‘`’,column_name,’`’) INTO @Whrs from information_schema.columns where table_schema=in_db and table_name=tablename;
#Main Select Statement for fetching comma separated table values
set @Inserts=concat(“select concat(‘insert into “, in_db,”.”,tablename,” values(‘,concat_ws(‘,’,”,@Sels,”),’);’) from “, in_db,”.”,tablename,” group by “,@Whrs, ” INTO OUTFILE ‘”, in_file ,”‘”);
PREPARE Inserts FROM @Inserts;
EXECUTE Inserts;
END $$
DELIMITER ;
Sample output:
Generate Inserts mysql
I have not considered each and every scenarios yet, but this works for normal tables and it does error if file exists.

MySQL Function to Convert Date To Words

MySQL Function to Convert Date To Words
Recently I saw a MySQL Stored Function requirement on Experts-Exchange for converting date into some specific words format.
You may find MySQL function for date to words conversion online; even udfs might be ready, but I decided to write my own.
I wrote this simple function mainly based on SELECT CASE to convert dates in to words as follows:
mysql>SELECT date_to_words(‘2010-05-08’);
Eighth Day of May Two Thousand Ten
Download sql file below the code.
DELIMITER $$
DROP FUNCTION IF EXISTS `date_to_words` $$
CREATE FUNCTION `date_to_words` (mydate DATE) RETURNS VARCHAR(100)
DETERMINISTIC
BEGIN
/* Converts date into words */
DECLARE yr INT;
DECLARE dateval INT;
DECLARE thousand INT;
DECLARE hundred INT;
DECLARE tens INT;
DECLARE tensword VARCHAR(10);
DECLARE onesword VARCHAR(10);
DECLARE thousandsword VARCHAR(20);
DECLARE hundredsword VARCHAR(20);
DECLARE datevalsword VARCHAR(20);
SET yr=year(mydate);
SET dateval=day(mydate);
/* DAY TO WORDS */
SELECT CASE dateval
WHEN 1 THEN ‘First’
WHEN 2 THEN ‘Second’
WHEN 3 THEN ‘Third’
WHEN 4 THEN ‘Fourth’
WHEN 5 THEN ‘Fifth’
WHEN 6 THEN ‘Sixth’
WHEN 7 THEN ‘Seventh’
WHEN 8 THEN ‘Eighth’
WHEN 9 THEN ‘Ninth’
WHEN 10 THEN ‘Tenth’
WHEN 11 THEN ‘Eleventh’
WHEN 12 THEN ‘Twelfth’
WHEN 13 THEN ‘Thirteenth’
WHEN 14 THEN ‘Fourteenth’
WHEN 15 THEN ‘Fifteenth’
WHEN 16 THEN ‘Sixteenth’
WHEN 17 THEN ‘Seventeenth’
WHEN 18 THEN ‘Eighteenth’
WHEN 19 THEN ‘Nineteenth’
WHEN 20 THEN ‘Twentieth’
WHEN 21 THEN ‘Twenty-first’
WHEN 22 THEN ‘Twenty-second’
WHEN 23 THEN ‘Twenty-third’
WHEN 24 THEN ‘Twenty-fourth’
WHEN 25 THEN ‘Twenty-fifth’
WHEN 26 THEN ‘Twenty-sixth’
WHEN 27 THEN ‘Twenty-seventh’
WHEN 28 THEN ‘Twenty-eighth’
WHEN 29 THEN ‘Twenty-ninth’
WHEN 30 THEN ‘Thirtieth’
WHEN 31 THEN ‘Thirty-first’
END into datevalsword;
/* YEAR TO WORDS */
set thousand=floor(yr/1000) ;
set yr = yr – thousand * 1000;
set hundred = floor(yr / 100);
set yr = yr – hundred * 100;
IF (yr > 19) THEN
set tens = floor(yr / 10);
set yr = yr mod 10;
ELSE
set tens=0;
END IF;
SELECT CASE thousand
WHEN 1 THEN ‘One’
WHEN 2 THEN ‘Two’
WHEN 3 THEN ‘Three’
WHEN 4 THEN ‘Four’
WHEN 5 THEN ‘Five’
WHEN 6 THEN ‘Six’
WHEN 7 THEN ‘Seven’
WHEN 8 THEN ‘Eight’
WHEN 9 THEN ‘Nine’
END INTO thousandsword;
SET thousandsword=concat(thousandsword,’ Thousand ‘);
SELECT CASE hundred
WHEN 0 then ”
WHEN 1 THEN ‘One’
WHEN 2 THEN ‘Two’
WHEN 3 THEN ‘Three’
WHEN 4 THEN ‘Four’
WHEN 5 THEN ‘Five’
WHEN 6 THEN ‘Six’
WHEN 7 THEN ‘Seven’
WHEN 8 THEN ‘Eight’
WHEN 9 THEN ‘Nine’
END INTO hundredsword;
if (hundredsword<>”) then
SET hundredsword=concat(hundredsword,’ Hundred ‘) ;
else
set hundredsword=”;
end if;
/*TENS To WORDS*/
SELECT CASE tens
WHEN 2 THEN ‘Twenty’
WHEN 3 THEN ‘Thirty’
WHEN 4 THEN ‘Fourty’
WHEN 5 THEN ‘Fifty’
WHEN 6 THEN ‘Sixty’
WHEN 7 THEN ‘Seventy’
WHEN 8 THEN ‘Eigthy’
WHEN 9 THEN ‘Ninety’
ELSE ”
END INTO tensword;
/*ONES To WORDS*/
SELECT CASE yr
WHEN 0 THEN ”
WHEN 1 THEN ‘One’
WHEN 2 THEN ‘Two’
WHEN 3 THEN ‘Three’
WHEN 4 THEN ‘Four’
WHEN 5 THEN ‘Five’
WHEN 6 THEN ‘Six’
WHEN 7 THEN ‘Seven’
WHEN 8 THEN ‘Eight’
WHEN 9 THEN ‘Nine’
WHEN 10 THEN ‘Ten’
WHEN 11 THEN ‘Eleven’
WHEN 12 THEN ‘Twelve’
WHEN 13 THEN ‘Thirteen’
WHEN 14 THEN ‘Fourteen’
WHEN 15 THEN ‘Fifteen’
WHEN 16 THEN ‘Sixteen’
WHEN 17 THEN ‘Seventeen’
WHEN 18 THEN ‘Eighteen’
WHEN 19 THEN ‘Nineteen’
END into onesword;
return concat(datevalsword, ‘ Day of ‘, date_format(mydate,’%M’),’ ‘,thousandsword,hundredsword, tensword,’ ‘,onesword);
END $$
DELIMITER ;
Download SQL Code for converting date to words.date_to_words.sql

Generate random test data for MySQL using routines

Generate random test data for MySQL using routines
At times you’ll find yourself responsible for generating test data for newly created tables for testing or sampling purpose. There are tools that will generate random data for you but they’re not free. At-times you’ll write scripts to generate data but those will be table specific.
I hate generating dummy data, yes I do and I assume you do too! I think that’s the major reason I wrote these MySQL functions and procedures for Generating dummy test data.

GitHub: mysql random data generator.


People can manage these things with a simple perl / shell script with loops but again that always need your time.
Why can’t MySQL generate data for it’s own table when MySQL better knows the table than anyone else !! ðŸ™‚
Below are a set of functions and a stored procedure that will make our life easy and of-course it’s free ;).
Downloads:
1. Download the sql code: Generate dummy data for MySQL.
2. Download sql for generating random data for foreign-key dependent child tables: populate_fk.sql
How to install and generate dummy data:
mysql -uUSER -pPASSWORD DATABASE < populate_dummy_data.txt
How use installed functions to generate test data:
– To generate test data of 1000 rows for sakila.film table execute following sql command:
call populate('sakila','film',1000,'N');
MySQL set of functions to get random values generated for individual data-types.
## MySQL function to generate random string of specified length
DROP function if exists get_string;
delimiter $$
CREATE FUNCTION get_string(in_strlen int) RETURNS VARCHAR(500) DETERMINISTIC
BEGIN 
set @var:='';
while(in_strlen>0) do
set @var:=concat(@var,IFNULL(ELT(1+FLOOR(RAND() * 53), 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z',' ','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'),'Kedar'));
set in_strlen:=in_strlen-1;
end while; 
RETURN @var;
END $$
delimiter ;


## MySQL function to generate random Enum-ID from specified enum definition
DELIMITER $$
DROP FUNCTION IF EXISTS get_enum $$
CREATE FUNCTION get_enum(col_type varchar(100)) RETURNS VARCHAR(100) DETERMINISTIC
 RETURN if((@var:=ceil(rand()*10)) > (length(col_type)-length(replace(col_type,',',''))+1),(length(col_type)-length(replace(col_type,',',''))+1),@var);
$$
DELIMITER ;


## MySQL function to generate random float value from specified precision and scale.
DELIMITER $$
DROP FUNCTION IF EXISTS get_float $$
CREATE FUNCTION get_float(in_precision int, in_scale int) RETURNS VARCHAR(100) DETERMINISTIC
 RETURN round(rand()*pow(10,(in_precision-in_scale)),in_scale) 
$$
DELIMITER ;



## MySQL function to generate random date (of year 2012).
DELIMITER $$
DROP FUNCTION IF EXISTS get_date $$
CREATE FUNCTION get_date() RETURNS VARCHAR(10) DETERMINISTIC
 RETURN DATE(FROM_UNIXTIME(RAND() * (1356892200 - 1325356200) + 1325356200))
# Below will generate random data for random years
# RETURN DATE(FROM_UNIXTIME(RAND() * (1577817000 - 946665000) + 1325356200))
$$
DELIMITER ;


## MySQL function to generate random time.
DELIMITER $$
DROP FUNCTION IF EXISTS get_time $$
CREATE FUNCTION get_time() RETURNS INTEGER DETERMINISTIC
 RETURN TIME(FROM_UNIXTIME(RAND() * (1356892200 - 1325356200) + 1325356200))
$$
DELIMITER ;

## MySQL function to generate random int.
DELIMITER $$
DROP FUNCTION IF EXISTS get_int $$
CREATE FUNCTION get_int() RETURNS INTEGER DETERMINISTIC
 RETURN floor(rand()*10000000) 
$$
DELIMITER ;

## MySQL function to generate random tinyint.
DELIMITER $$
DROP FUNCTION IF EXISTS get_tinyint $$
CREATE FUNCTION get_tinyint() RETURNS INTEGER DETERMINISTIC
 RETURN floor(rand()*100) 
$$
DELIMITER ;

## MySQL function to generate random varchar column of specified length(alpha-numeric string).
DELIMITER $$
DROP FUNCTION IF EXISTS get_varchar $$
CREATE FUNCTION get_varchar(in_length varchar(500)) RETURNS VARCHAR(500) DETERMINISTIC
 RETURN SUBSTRING(MD5(RAND()) FROM 1 FOR in_length)
$$
DELIMITER ;

## MySQL function to generate random datetime value (any datetime of year 2012).
DELIMITER $$
DROP FUNCTION IF EXISTS get_datetime $$
CREATE FUNCTION get_datetime() RETURNS VARCHAR(30) DETERMINISTIC
 RETURN FROM_UNIXTIME(ROUND(RAND() * (1356892200 - 1325356200)) + 1325356200)
$$
DELIMITER ;
The MySQL Stored procedure that populates MySQL tables with dummy data:

DELIMITER $$

DROP PROCEDURE IF EXISTS populate $$
CREATE PROCEDURE populate(in_db varchar(50), in_table varchar(50), in_rows int, in_debug char(1)) 
BEGIN
/*
|
| Developer: Kedar Vaijanapurkar
| USAGE: call populate('DATABASE-NAME','TABLE-NAME',NUMBER-OF-ROWS,DEBUG-MODE);
| EXAMPLE: call populate('sakila','film',100,'N');
| Debug-mode will print an SQL that's executed and iterated.
|
*/

DECLARE col_name VARCHAR(100);
DECLARE col_type VARCHAR(100); 
DECLARE col_datatype VARCHAR(100);
DECLARE col_maxlen VARCHAR(100); 
DECLARE col_extra VARCHAR(100);
DECLARE col_num_precision VARCHAR(100);
DECLARE col_num_scale VARCHAR(100);
DECLARE func_query VARCHAR(1000);
DECLARE i INT;

DECLARE done INT DEFAULT 0;
DECLARE cur_datatype cursor FOR
 SELECT column_name,COLUMN_TYPE,data_type,CHARACTER_MAXIMUM_LENGTH,EXTRA,NUMERIC_PRECISION,NUMERIC_SCALE FROM information_schema.columns WHERE table_name=in_table AND table_schema=in_db;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;


SET func_query='';
OPEN cur_datatype;
datatype_loop: loop

FETCH cur_datatype INTO col_name, col_type, col_datatype, col_maxlen, col_extra, col_num_precision, col_num_scale;
#SELECT CONCAT(col_name,"-", col_type,"-", col_datatype,"-", IFNULL(col_maxlen,'NULL'),"-", IFNULL(col_extra,'NULL')) AS VALS;
  IF (done = 1) THEN
    leave datatype_loop;
  END IF;

CASE 
WHEN col_extra='auto_increment' THEN SET func_query=concat(func_query,'NULL, ');
WHEN col_datatype in ('int','bigint') THEN SET func_query=concat(func_query,'get_int(), ');
WHEN col_datatype in ('varchar','char') THEN SET func_query=concat(func_query,'get_string(',ifnull(col_maxlen,0),'), ');
WHEN col_datatype in ('tinyint', 'smallint','year') or col_datatype='mediumint' THEN SET func_query=concat(func_query,'get_tinyint(), ');
WHEN col_datatype in ('datetime','timestamp') THEN SET func_query=concat(func_query,'get_datetime(), ');
WHEN col_datatype in ('float', 'decimal') THEN SET func_query=concat(func_query,'get_float(',col_num_precision,',',col_num_scale,'), ');
WHEN col_datatype in ('enum','set') THEN SET func_query=concat(func_query,'get_enum("',col_type,'"), ');
WHEN col_datatype in ('GEOMETRY','POINT','LINESTRING','POLYGON','MULTIPOINT','MULTILINESTRING','MULTIPOLYGON','GEOMETRYCOLLECTION') THEN SET func_query=concat(func_query,'NULL, ');
ELSE SET func_query=concat(func_query,'get_varchar(',ifnull(col_maxlen,0),'), ');
END CASE;


end loop  datatype_loop;
close cur_datatype;

SET func_query=trim(trailing ', ' FROM func_query);
SET @func_query=concat("INSERT INTO ", in_db,".",in_table," VALUES (",func_query,");");
 IF in_debug='Y' THEN
  select @func_query;
 END IF;
SET i=in_rows;
populate :loop
 WHILE (i>0) DO
   PREPARE t_stmt FROM @func_query;
   EXECUTE t_stmt;
 SET i=i-1;
END WHILE;
LEAVE populate;
END LOOP populate;
SELECT "Kedar Vaijanapurkar" AS "Developed by";
END
$$
DELIMITER ;

A tale of Corrupt InnoDB table, MySQL crash & recovery

       A tale of Corrupt InnoDB table, MySQL crash & recovery


I’m going to narrate you a story that happened around a crashing MyQL, Corrupted InnoDB table and finally the recovery by table restore. We will see how our database administrator detected the issue and what he did to resolve it.
A day in MySQL Database Consultant’s day was taking its shape while a friend called for help.
Friend: Hey, my mysql is crashing and website isn't functioning well. Everything is down. Can you help me?
Our database admin quickly jumps in and checks for the MySQL error log.
        2018-01-01T07:39:03.173398Z 0 [ERROR] InnoDB: Database page corruption on disk or a failed file read of page [page id: space=13701, page number=4603]. You may have to recover from a backup.
        2018-01-01T07:39:03.173428Z 0 [Note] InnoDB: Page dump in ascii and hex (16384 bytes):
        ...
        InnoDB: End of page dump
        2018-01-01T07:39:03.265864Z 0 [Note] InnoDB: Uncompressed page, stored checksum in field1 4187651462, calculated checksums for field1: crc32 4128877936/1194051977, innodb 680941878, none 3735928559, stored checksum in field2 3735928559, calculated checksums for field2: crc32 4128877936/1194051977, innodb 1675940203, none 3735928559,  page LSN 400 3284265879, low 4 bytes of LSN at page end 3284252104, page number (if stored to page already) 4603, space id (if created with >= MySQL-4.1.1 and stored already) 13701
        InnoDB: Page may be an index page where index id is 33515
        2018-01-01T07:39:03.265911Z 0 [Note] InnoDB: Index 33515 is `PRIMARY` in table `nitty-witty`.`flat_address`
        2018-01-01T07:39:03.265919Z 0 [Note] InnoDB: It is also possible that your operating system has corrupted its own file cache and rebooting your computer removes the error. If the corrupt page is an index page. You can also try to fix the corruption by dumping, dropping, and reimporting the corrupt table. You can use CHECK TABLE to scan your table for corruption. Please refer to http://dev.mysql.com/doc/refman/5.7/en/forcing-innodb-recovery.html for information about forcing recovery.
        2018-01-01T07:39:03.265944Z 0 [ERROR] [FATAL] InnoDB: Aborting because of a corrupt database page in the system tablespace. Or,  there was a failure in tagging the tablespace  as corrupt.
        2018-01-01 07:39:03 0x7f5fa0466700  InnoDB: Assertion failure in thread 140048687589120 in file ut0ut.cc line 916
        InnoDB: We intentionally generate a memory trap.
        InnoDB: Submit a detailed bug report to http://bugs.mysql.com.
        InnoDB: If you get repeated assertion failures or crashes, even
        InnoDB: immediately after the mysqld startup, there may be
        InnoDB: corruption in the InnoDB tablespace. Please refer to
        InnoDB: http://dev.mysql.com/doc/refman/5.7/en/forcing-innodb-recovery.html
        InnoDB: about forcing recovery.
        07:39:03 UTC - mysqld got signal 6 ;
        This could be because you hit a bug. It is also possible that this binary
        or one of the libraries it was linked against is corrupt, improperly built,
        or misconfigured. This error can also be caused by malfunctioning hardware.
        Attempting to collect some information that could help diagnose the problem.
        As this is a crash and something is definitely wrong, the information
        collection process might fail.

        key_buffer_size=536870912
        read_buffer_size=16777216
        max_used_connections=6
        max_threads=214
        thread_count=6
        connection_count=6
        It is possible that mysqld could use up to
        key_buffer_size + (read_buffer_size + sort_buffer_size)*max_threads = 7539498 K  bytes of memory
        Hope that's ok; if not, decrease some variables in the equation.

        Thread pointer: 0x0
        Attempting backtrace. You can use the following information to find out
        where mysqld died. If you see no messages after this, something went
        terribly wrong...
database admin: Yeah... your MySQL has a corrupted InnoDB table. What did you do?
Friend: Earlier today I had my disk full, I moved few files and deleted others. Then I tried to backup but my MySQL Crashed. So I tried to repair it.
mysql> repair table nitty-witty.flat_address;
+--------------------------------------+--------+----------+---------------------------------------------------------+
| Table                                | Op     | Msg_type | Msg_text                                                |
+--------------------------------------+--------+----------+---------------------------------------------------------+
| nitty-witty.flat_address | repair | note     | The storage engine for the table doesn't support repair |
+--------------------------------------+--------+----------+---------------------------------------------------------+
1 row in set (0.00 sec)
Friend (continued): Since this storage engine doesn't support repair, I tried to change the engine to MyISAM but it failed as well due to foreign keys.
ALTER TABLE nitty-witty.flat_address ENGINE=MyISAM;
database admin: No No... Good that it failed. Do not convert to MyISAM. Rather, forget MyISAM. Do you have backups? I see there are not binary logs either.
Friend:"yeah, I have one mysqldump from a week ago. I have some recent data in here. Can you repair it?"
No backups, corrupted data? Awesome day! right?
Our database admin looked further for hope. He found that MySQL is crashing frequently upon accessing the corrupted InnoDB table only and not otherwise. Error log does mention this is specific to Primary index. He quickly resorted to the innodb_force_recovery aiming to backup the data first.
You can use the innodb_force_recovery option to force the InnoDB storage engine to start up while preventing background operations from running, so that you can dump your tables.
1 (SRV_FORCE_IGNORE_CORRUPT): Lets the server run even if it detects a corrupt page. Tries to make SELECT * FROM tbl_name jump over corrupt index records and pages, which helps in dumping tables.
Above section comes from documentation and thus our Database Admin concluded to go ahead with following steps:
1. Chang MySQL configuration file (my.cnf) to add innodb_force_recovery=1
2. Restart MySQL
3. Dump the corrupted InnoDB table.
The backup of table was successful. Our database administrator later removes the innodb_force_recovery from my.cnf and restarts the MySQL server.
Further he restored the table as flat_address_NEW and reviewed the restored data.
mysql> select count(1) from flat_address_NEW;
+----------+
| count(1) |
+----------+
|   277878 |
+----------+
1 row in set (0.05 sec)

mysql> select count(1) from flat_address;
+----------+
| count(1) |
+----------+
|   277878 |
+----------+
1 row in set (0.06 sec)
Counts matched, his friend was happy to see his data back. Friend (table owner) manually verified some of the contents of the table and satisfied with the recovery.
Our database admin concluded to swap the tables as follows:
1. Rename flat_address to flat_address_OLD
2. Rename flat_address_NEW to flat_address
But wait there’s a problem. They had foreign keys remember?
See what documentation reads:
RENAME TABLE changes internally generated foreign key constraint names and user-defined foreign key constraint names that contain the string “tbl_name_ibfk_” to reflect the new table name. InnoDB interprets foreign key constraint names that contain the string “tbl_name_ibfk_” as internally generated names.
Foreign key constraint names that point to the renamed table are automatically updated unless there is a conflict, in which case, the statement fails with an error. A conflict occurs if the renamed constraint name already exists. In such cases, you must drop and re-create the foreign keys in order for them to function properly.
So, what will happen is if we swap, the related tables will point to flat_address_OLD table and not the newly restored (and renamed) table flat_address.
Also skipping foreign_key_checks won’t help here. They cannot simply swap tables, they will have to drop and recreate the references.
Next thing our database admin did is to look for tables referencing the corrupted table:
mysql>  SELECT TABLE_SCHEMA,TABLE_NAME,REFERENCED_TABLE_NAME,REFERENCED_COLUMN_NAME FROM information_schema.KEY_COLUMN_USAGE WHERE  REFERENCED_TABLE_SCHEMA = 'nitty-witty' and REFERENCED_TABLE_NAME='flat_address';
+--------------+-------------------+--------------------------+------------------------+
| TABLE_SCHEMA | TABLE_NAME        | REFERENCED_TABLE_NAME    | REFERENCED_COLUMN_NAME |
+--------------+-------------------+--------------------------+------------------------+
| nitty-witty  | flat_address_item | flat_address             | address_id             |
| nitty-witty  | flat_rate         | flat_address             | address_id             |
+--------------+-------------------+--------------------------+------------------------+
The plan there after was as follows:
  1. Drop foreign keys.
  2. Swap corrupted table with recovered table.
  3. Recreate foreign keys.
mysql>  alter table flat_address_item drop foreign key FK_B521389746C00700D1B2B76EBBE53854;
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> alter table flat_rate drop foreign key FK_B1F177EFB73D3EDF5322BA64AC48D150;
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> rename table flat_address to flat_address_OLD,flat_address_NEW to flat_address;
Query OK, 0 rows affected (0.01 sec)

mysql> alter table flat_address_item ADD CONSTRAINT `FK_B521389746C00700D1B2B76EBBE53854` FOREIGN KEY (`quote_address_id`) REFERENCES `flat_address` (`address_id`) ON DELETE CASCADE ON UPDATE CASCADE;
Query OK, 156 rows affected (0.06 sec)
Records: 156  Duplicates: 0  Warnings: 0

mysql> alter table flat_rate ADD CONSTRAINT `FK_B1F177EFB73D3EDF5322BA64AC48D150` FOREIGN KEY (`address_id`) REFERENCES `flat_address` (`address_id`) ON DELETE CASCADE ON UPDATE CASCADE;
Query OK, 0 rows affected (0.00 sec)
Records: 0  Duplicates: 0  Warnings: 0
Finally our Database admin informed his friend that the table is restored and he will have to drop the original corrupted table after the data has been verified. Database Admin also provided him with two golden lines:
1. Backup your data regularly.
2. Setup basic monitoring.
To summarize: A corrupted InnoDB Table was recovered by mysqldump / restore after restarting MySQL with innodb_force_recovery. Once data was verified by owner, the tables were swapped carefully handling the Foreign Keys. Finally the corrupted table was dropped to avoid further MySQL crashes.
Note that the table was comparatively small but there was no recent backup. Also there was complete downtime during this recovery activity.

Export MySQL database table to CSV (delimited / Excel) file

Export MySQL database table to CSV (delimited / Excel) file

Today lets talk a little about converting a MySQL table to CSV (Excel). My friend was looking to export MySQL to Excel, I saw couple of questions for export MySQL tables to CSV on forums. Since I saw the question often, I thought of writing out all the ways I can think of for exporting Delimited (CSV / TSV / …) data from MySQL table. Pretty chewed & basic but frequent topic.
Following are the ways to export CSV data from MySQL database / table(s).

1. Using SELECT INTO … OUTFILE statement to export from MySQL to CSV

SELECT ... INTO OUTFILE writes the selected rows to a file. Column and line terminators can be specified to produce a specific output format. Just to mention, SELECT ... INTO OUTFILE is the complement of LOAD DATA INFILE, which you may use to load CSV (generally speaking delimited) files to MySQL.
Here’s a sample command to export “tablename” table of “db” database as a CSV:
SELECT * INTO OUTFILE '/csv_files/db.tablename.txt'
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
LINES TERMINATED BY '\n'
FROM db.tablename;
Since above command will export only single table as a csv file; let’s see how can we do this for more tables. In this case export all the tables in MySQL database.
– We will use information_schema to prepare syntaxes for exporting tables to CSVs and export them to a temporary file:
mysql> select concat('SELECT * INTO OUTFILE "/var/lib/mysql-files/CSV_exports',TABLE_SCHEMA,'.',table_name,'.csv" FIELDS TERMINATED BY "," OPTIONALLY ENCLOSED BY \'"\' LINES TERMINATED BY "\\n" FROM ', table_schema,'.',table_name,';') backup_csv from information_schema.tables where table_schema not in ('information_schema','sys','mysql','performance_schema','test') into outfile '/var/lib/mysql-files/export_csv';      
– Now since our commands are ready, we’d just run by executing the same. We can source the export_csv file we created in previous command.
mysql> source /var/lib/mysql-files/export_csv
Once the source command is complete, queries are done, you’d be able to see your CSV files under /var/lib/mysql-files/CSV_exports directory.
There is one thing you may need to watch out for – secure-file-priv. If your server is running with secure-file-priv set, you may see following error for our execution of SELECT … INTO OUTFILE command:
The MySQL server is running with the --secure-file-priv option so it cannot execute this statement. 
Actually this option restricts both the import (eg. LOAD … DATA INFILE) and export (eg. SELECT … INTO OUTFILE) operations.
When secure_file_priv=””, there is no restriction.
When secure_file_priv=”/path/dir”, import export are allowed only with the files in that directory.
When secure_file_priv=NULL, all the import / export operations are disabled. (Introduced from 5.7.6)

2. Using the CSV Engine to export from MySQL to CSV

MySQL supports the CSV storage engine, which stores data in text files in comma-separated values format.
This sounds easy, all you need to do is to change the database engine for the table to CSV and copy the file!
ALTER TABLE innodb_to_csv ENGINE=CSV;
After the ALTER command you will have 3 files: CSV, CSM and FRM in your data-directory. You may directly copy the CSV file.
[root@kedar ~]#  ls -lhtr /var/lib/mysql/test/a.*
-rw-r----- 1 mysql mysql 8.4K Jan 26 11:45 /var/lib/mysql/test/innodb_to_csv.frm
-rw-r----- 1 mysql mysql  956 Jan 26 11:45 /var/lib/mysql/test/innodb_to_csv.CSV
-rw-r----- 1 mysql mysql   35 Jan 26 11:45 /var/lib/mysql/test/innodb_to_csv.CSM
[root@kedar ~]#
Spoiler: This will work only if your table doesn’t have an index, because CSV storage engine doesn’t support indexes. meh!
Anyhow, depending on what your requirement is, I’d surely recommend using copy of the table to convert to CSV in order to avoid troubling the original table. You may choose to follow next steps.
Since CSV table support no indexes, we will create a table without indexes:
mysql> CREATE TABLE csv_table AS SELECT * FROM innodb_table LIMIT 0;
Convert the table to CSV:
mysql> ALTER TABLE csv_table ENGINE=CSV;
Load the data:
mysql> INSERT INTO csv_table SELECT * FROM innodb_table;
You can then use the csv_table.CSV from data directory.

3. Using mysqldump to export from MySQL to CSV

After all the hardwork above, here’s another way to export MySQL database table to CSV (or delimited text for that matter); and this one I believe is the easiest.
The mysqldump is widely used client utility to perform logical backups and hence well known. Though it is less common (my guess) to use the command to generate output in CSV (delimited) or XML format.
Just like our SELECT … INTO OUTFILE statement, we can specify options to create the delimited files from mysqldump command. In below command we’re exporting all the tables of “mytest” database as a CSV file:
mysqldump --tab=/var/lib/mysql-files/ --fields-enclosed-by='"' --fields-terminated-by=',' --lines-terminated-by='\n' mytest
This will create SQL and TXT files as an output under the directory specified with –tab option. Sample execution below:
[root@kedar ~]# mysqldump --tab=/var/lib/mysql-files/ --fields-enclosed-by='"' --fields-terminated-by='\n' mytest
[root@kedar ~]# ls -lhtr /var/lib/mysql-files/
...
-rw-r--r-- 1 root  root  1.6K Jan 26 11:39 app_user.sql
-rw-rw-rw- 1 mysql mysql 367K Jan 26 11:39 app_user.txt
-rw-r--r-- 1 root  root  2.1K Jan 26 11:39 random_data_gen.sql
-rw-rw-rw- 1 mysql mysql 1.3M Jan 26 11:39 random_data_gen.txt
-rw-r--r-- 1 root  root  2.6K Jan 26 11:39 splits.sql
-rw-rw-rw- 1 mysql mysql  13M Jan 26 11:40 splits.txt
-rw-r--r-- 1 root  root  1.4K Jan 26 11:40 sbtest1.sql
-rw-rw-rw- 1 mysql mysql 589K Jan 26 11:40 sbtest1.txt
...
There you go, the .txt files are your CSV tables. You can filter table data or automate the export as per requirement from the shell script quite easily.
There are surely other ways to export from MySQL like using MySQL WorkBench, PHPMyAdmin & other GUI tools or even scripting your way out.

Restore A Table / Database From Full Backup – Yet Another Way


Restore requests are common and so are the restores of specific entities: a database, or one or more table(s). This has been discussed a lot and we have plenty of tools and solutions already available.
In this blog post we will cover an interesting solution that I came across when I received a restoration request from a client with a specific scenario.
The scenario? Well, the client was on a Windows server with 400GB of mysqldump and wanted to restore a table.
As Linux players we already know of some tools and techniques to export a table or database from mysqldump – for example, using sed command or using the script mysqldumpsplitter (based on sed itself). But on Windows we are powerless by not being able to use sed (we’re sad without sed.) Also, there was no cygwin to ease up the pain.
We had to come-up with a solution that works on Windows as well. During this discussion, my Pythian colleague, Romuller, suggested a cool but simple trick which enlightens us and offers one more way of exporting or recovering a table from a full mysqldump.
So the trick here is as follows:

– Create a user that has very specific grants, limited to one or more table(s) or database(s) that we need to restore.
– Add SELECT ON *.* lets us use other databases though we will not be able to load the tables. (Source: comment from Pythian blog)
– Load mysqldump into the database with that user provide with –force. The option –force will ignore all the errors that will occur due to lack of privileges of the new user we created specifically for restore.
Easy right? Database Consultants like to KISS ;).
Let’s give it a try.
I selected a table “stories” & create the “bad” situation by dropping that table.
mysql> use test
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> show tables;
+------------------------+
| Tables_in_test      |
+------------------------+
...
| stories             |
...
+------------------------+

mysql> select count(*) from stories;
+----------+
| count(*) |
+----------+
|   881 |
+----------+
1 row in set (0.02 sec)

mysql> drop table stories;
Query OK, 0 rows affected (0.29 sec)
Let’s begin the recovery phase now following the grants method.
1. Create the user with limited grants only on test.stories table.
mysql> grant all privileges on test.stories to 'stories'@localhost identified by 'X';
Query OK, 0 rows affected, 1 warning (0.03 sec)

mysql> show warnings;
+---------+------+------------------------------------------------------------------------------------------------------------------------------------+
| Level   | Code | Message                                                                                                                            |
+---------+------+------------------------------------------------------------------------------------------------------------------------------------+
| Warning | 1287 | Using GRANT for creating new user is deprecated and will be removed in future release. Create new user with CREATE USER statement. |
+---------+------+------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
Wait, there is a warning. We see this warning in MySQL 5.7.6 onward and it says GRANT commands will be deprecated in favour of CREATE USER statement to create new users. So, we shall have following practice to be ready for MySQL 8 ðŸ™‚
CREATE USER 'stories'@’localhost’ identified with mysql_native_password by ‘X';
grant all privileges on test.stories to 'stories'@'localhost';
grant select on *.* to 'stories'@'localhost';
2. Load the mysqldump using the same user with –force.
[root@mysql1c ~]# cat fuldump.sql | mysql -ustories -pX test --force
mysql: [Warning] Using a password on the command line interface can be insecure.
ERROR 1044 (42000) at line 22: Access denied for user 'stories'@'localhost' to database 'archive'
...
ERROR 1142 (42000) at line 420: ALTER command denied to user 'stories'@'localhost' for table 'emp_new'
...
ERROR 1142 (42000) at line 1966: ALTER command denied to user 'stories'@'localhost' for table 'user_address'
3. Verify table is restored:
mysql> show tables;
+------------------------+
| Tables_in_test      |
+------------------------+
...
| stories             |
...
+------------------------+

mysql> select count(*) from stories;
+----------+
| count(*) |
+----------+
|   881 |
+----------+
1 row in set (0.00 sec)
Conclusion:
When you compare the table that is being restored to the other one, mysqldump is smaller. This method may take a lot of time just ignoring errors due to –force option. Of course, in most cases you will end up reading the whole file. If our table appears early in the mysqldump, we may monitor the progress and kill the process as well. Otherwise, it may make more sense to try and install Cygwin or move the backup to a Linux Box to extract a database object from the backup file.

MySQL Load Data Infile Syntax Generator Tool Download


The LOAD DATA INFILE statement reads rows from a text file into a table at a very high speed. The file name must be given as a literal string.
I have already written the basic how to follow that considering additional cases.
A lot of questions I have seen for loading separated data to MySQL, so here I’ve created a very beginner level excel tool that will allow you to generate the LOAD DATA syntax as per your choices.
Presently sample table name and field/line separators are specified, which you may alter as per your own file.


Download the sheet here: Load Data

Load Data Syntax:

Options column specifies options that are required for syntaxes generation.
Under Selection column you will choose your option while under Description column, for each Option description is provided.
Using basic knowledge of load data you can quite easily generate the syntax by specifying option, choosing file to be loaded and finally clicking the Syntax button.