php 使用 LOAD DATA LOCAL INFILE方法插入测试数据

参考网上 LOAD DATA LOCAL INFILE 的使用方法,做了一个demo,记录下主要使用过程

需求:往数据库表插入大量的测试数据
主要步骤:

1.生成一个需要的数据txt文件,这个文件大概长这样


image.png

(这里数据格式和样式在后面可根据自己需求修改)

2.将这个文件使用 LOAD DATA LOCAL INFILE 命令导入数据

主要代码
1.数据库连接
//有一种mysqli的set_local_infile_handler的写法,具体没测试,这里使用普通的连接方式
$link = @mysql_connect($host, $user, $password, 1, MYSQL_CLIENT_COMPRESS);
$db = mysql_select_db($dbName, $link);
mysql_query('set names utf8');
2.数据库表处理

该方法取出数据库所有表

//查询所有表,不想往所有表插数据就单独传个表名称参数,跳过该方法
    public function tables(){
        $return = array();
        $sql = " show tables ";
        $result = mysql_query($sql);

        while( $row = mysql_fetch_array($result) ){
            $return[] = $row[0];
        }
        //mysql_free_result($result);
        //取到数据库所有表名
        return $return;
    }

该方法取出表的所有字段

    //获取数据库表的所有字段
    //$tableName string 表名
    public function columns($tableName=''){
        $return = array();
        if( $tableName ){
            $sql = "SHOW FULL COLUMNS FROM ".$tableName;
            $result = mysql_query($sql);

            $i = 0;
            while( $row = mysql_fetch_array($result) ){
                $return[$i] = $row;
                $i++;
            }
        }
        return $return;
    }

该方法查数据库表的自增id,可查所有表和单表

    //获取数据库表的所有字段
    public function auto_increment_id($dbName='',$tableName=''){
        $return = array();

        //所有表状态
        $sql = " SHOW TABLE STATUS ";

        //单个表状态
        if( $dbName && $tableName ){
            $sql = " select  Auto_increment into autoId from information_schema.tables where Table_Schema = ".$dbName."  and table_name = ".$tableName;
        }

        $result = mysql_query($sql);
        while( $row = mysql_fetch_assoc($result) ){
            $tmp_arr[] = $row;
        }

        //只保留自增主键信息
        foreach( $tmp_arr as $val ){
            $return[$val['Name']] = $val['Auto_increment'];
        }
        return $return;
    }

知道一个表的所有字段,知道它的当前自增id,可以造一条数据

public function doColumns($columns,$auto_increment_id){
        $return = array();

        if( $columns && $auto_increment_id ){

            //一条数据字串
            $insert_str = '';
            //字段分隔符,这里需要着重强调注意的是,尽量使用默认的tab分隔字段,使用其它的分隔符,后面导入sql需要拼接分隔符语句,而该语句很可能执行不成功
            $fenge = "\t";  

            //循环字段生成一条数据
            foreach( $columns as $key=>$val ){
                //主键的值
                if( $val['Key'] == 'PRI' ){
                    $insert_str .= $auto_increment_id.$fenge;

                }elseif( isset($val['Default']) && $val['Type'] != 'timestamp' ){
                    //有默认值的数据,以默认值为数据
                    $insert_str .= $val['Default'].$fenge;
                }else{
                    //非主键,没默认值的,给个数字字串
                    $v = $this->__columns($val['Type']);  //该方法在下面
                    $insert_str .= $v.$fenge;
                }
            }
            $insert_str = trim($insert_str,$fenge);
            return $insert_str;
        }

    }

造数据,该方法根据自己的需要修改,这里是根据字串长度随机数字当作字段值

//具体字段处理
    public static function __columns($type){

        if( $type == 'datetime' || $type == 'timestamp' ){  //年月日时分秒格式
            $val = date('Y-m-d H:i:s');

        }elseif( $type == 'date' ){     //年月日格式
            $val = date('Y-m-d');

        }elseif( $type == 'time' ){     //时分秒格式
            $val = date('H:i:s');

        }elseif( $type == 'year' ){     //年
            $val = date('Y');

        }else{  
             //其它类型的用数字填充,可根据自己需要的修改                       
            //浮点数类型
            if( strpos($type,'float') === 0 ){
                $preg = '/\((\d+),?(\d+)\)/';
            }else{
                $preg = '/\((\d+)\)/';
            }
            preg_match_all($preg,$type,$match);

            //获取字段类型中括号里的长度,浮点数取前面的数字长度
            $length = $match[1][0];
            if( !$length ){
                $val = mt_rand(1,9);   //没有长度的,如文本等字段,使用一个长度的数字
            }else{
                $tmp_str = '123456789';

                $num = ceil($length/9);
                for($i = 1;$i<$num;$i++){
                    $tmp_str .= '123456789';
                }
                $val = substr(str_shuffle($tmp_str),0,$length);

            }
        }

        return $val;
    }

生成txt数据文件,并执行导入

/*
     * 单个表生成txt并执行导入
     * $tableName string 表名
     * $num int 每张表插入多少条
     * $file_path 生成数据.txt文件的保存路径
     */
    public function insert_table($tableName,$num,$file_path){
        //error_reporting(E_ALL);
        set_time_limit(0);

        //根据表明获取字段
        $columns = $this->columns($tableName);

        //获取该表的自增主键id
        $auto_increment_id_arr = $this->auto_increment_id();
        //该表的自增id
        $auto_increment_id = $auto_increment_id_arr[$tableName];

        //有自增主键的插入数据,没有的忽略
        if( $auto_increment_id ){

            //根据插入的条数生成一条条数据
            for($i=0;$i<$num;$i++){
                $auto_increment_id+=1;
                $insert_str = $this->doColumns($columns,$auto_increment_id);

                //将该数据追加写入txt文件
                //每条数据之间拼接换行符
                file_put_contents($file_path,$insert_str.PHP_EOL,FILE_APPEND);
            }

            //将生成的txt文件导入数据库
            //$result = $this->local_infile($file_path,$tableName);

            //导入数据的sql,这里未拼接一些换行忽略等语句
            $str = " LOAD DATA LOCAL INFILE '".$file_path."' ignore INTO TABLE ".$tableName;

            $result = mysql_query($str);
            //释放
            mysql_free_result($result);

        }
    }

导入语句使用的最简方式来保证执行成功,记录下一些写法

        //字段分隔符为逗号
        //$fenge = "\t";
        //$ziduan_fenge = " FIELDS TERMINATED BY '".$fenge."' ";  //以什么字符作为分隔符,默认的是 tab键

        //字段忽略符号
        //$hulue = '\/"';
        //$ziduan_hulue = " OPTIONALLY ENCLOSED BY '".$hulue."' ";    //字段括起字符

        //字段转义符号
        //$zhuanyi = '\/"';
        //$ziduan_zhuanyi = " ESCAPED BY '".$zhuanyi."' ";

        //行分隔符
        //$hang = '\n';
        //$hang_fenge = " LINES TERMINATED BY '".$hang."' ";

        $str = " LOAD DATA LOCAL INFILE '".$file_path."' REPLACE INTO TABLE ".$table_name;
        //一个比较完整的导入sql
       $sql = $str.$ziduan_fenge.$ziduan_hulue.$ziduan_zhuanyi.$hang_fenge;

补充说明
1.字段分隔符用默认的 \t,用其它的需要凭借字段分隔符语句,拼接后在执行sql时经常报分隔符字段错误
2.文件保存路径,用相对路径就好,使用本地路径测试的话,导入的sql需要加上上面字段转义符号的写法,否则会语法错误,加上转义写法sql在navicat能执行成功,使用mysql_query执行时错误,程序不识别这种写法,所以使用个相对文件路径,导入sql尽量简短来省略麻烦
3.数据txt文件生成后执行sql导入,多张表操作的话记得释放结果集
4.拿100万来测试数据的,单个表容易,一次性取出数据库多个表循环操作的话,后面的表数据没能准确插入,还可以继续优化
5.因为需求所以写了一个demo,列出其中主要代码,mysql没有专门研究,但凡有错漏还望大家指正

你可能感兴趣的:(php 使用 LOAD DATA LOCAL INFILE方法插入测试数据)