用perl做数据库迁移,从MSSQL到MYSQL(三)自动导数据,基本思想,拼SQL语句

   2023-02-07 学习力0
核心提示:哎!这周末过得比上班还累,代码周五基本写完,一直没时间整理,今天晚上整理了发上来。哦,再次提醒一下哈,本代码是本人第一次写perl很多它的优良特性没用到,因此没能写出perl本应该有的优秀的,可爱的,外行看不大懂的代码。如有真心喜欢Perl的人,请不要

哎!这周末过得比上班还累,代码周五基本写完,一直没时间整理,今天晚上整理了发上来。

哦,再次提醒一下哈,本代码是本人第一次写perl很多它的优良特性没用到,因此没能写出perl本应该有的优秀的,可爱的,外行看不大懂的代码。如有真心喜欢Perl的人,请不要见骂,请多多指教。

#!/usr/bin/perl
use DBI;
use Switch;

$dbh=DBI->connect('dbi:ODBC:##隐去的MSSQL访问ODBC连接##','##隐去的MSSQL访问用户名##','##隐去的MSSQL访问密码##');
#获取所有的用户表,当然不想导的表,表名放到not in里边
my $sth=$dbh->prepare("select name,object_id from sys.all_objects where type='U' and is_ms_shipped=0 and name not in
('sysdiagrams')");
$sth->execute();
my $false_Table;

#遍历所有表,呵呵,这里啊,其实用另外一个方法更快。详细请参考:http://www.easysoft.com/developer/languages/perl/dbd_odbc_tutorial_part_2.html
while (@data=$sth->fetchrow_array())
{
    ##测试时用
#    @data=$sth->fetchrow_array();
    ##这个是perl的异常处理,也是后边再加的,因为此次移植的时候,发现很多问题,开始在调试的时候,也没想过加异常处理,但后来发现这个调试的过程,
    ##实在太痛苦了,才查了资料加的,当然对异常处理,perl好像也有try catch~~但,粗粗翻了下资料,好像是python里边的(当然,这个好像是十分好像,)
    ##因为我觉得所有的计算机语言都是为了解决某一类问题的,先解决问题了,再去想解决~~因此没有深究。(哎!又啰嗦了)
    ##哦,资料地址如下:http://perldoc.perl.org/functions/eval.html  ,有志深入学习Perl的同学,可以把这个网站收藏了,基本(基本)相当于了MSDN。
    eval
    {
        #获取某个表的列,并构建 查询,插入,列总数,列类型
        ##输入参数如下:
        ###data[0]:表名,data[1]:对像ID
        ##返回参数描述如下:
        ###$select_columns:构建SELECT的时候,列字符串
        ###$insert_columns:构建insert的时候,列字符串。之所以要把这两分开,因为有些类型在select的时候,会用到列属性方法,例如geometry.STAsText()
        ###$column_count:列数,其实可以从@$column_types得到,但@$columns_types是后边加的,此参数也就没有去掉
        ###$sort_column:用来排序的字段,因为总结了一下,一般第一个字段都是标识字段,主键,因此,这里只取的第一个字段
        ###$columns_types:列的类型列表,一个数组。因为sql server里边的某些类型的值,在进mysql的时候,需要做处理,例如geometry
        #######另外再啰嗦一句,很少见有能同时返回多个值的东东(当然,可能是我把C#忘得差不多了)
        ($select_columns,$insert_columns,$column_count,$sort_column,$column_types)=get_columns($data[0],$data[1]);
        #查询结果。如果是导入失败,会返回False,否则为空
        ##传入参数?说明请参照上边的输出参数
        my $relt = export_data_in ($select_columns,$insert_columns,$column_count,$sort_column,$data[0],$column_types);
        if( $relt eq "False")
        {
            #记录所有未导入成功的表
            $false_Table = "$false_Table,$relt";
        }
    };
    ##如果捕获异常,记录下表名与错误。在本次数据迁移的过程中,有一种错误类型是捕获不到的“out of memory”,而这个异常又是本次数据库迁移中,遇到过最多的
    ##异常。因为表的列,很多都是nvarchar(max),其中有一张表最夸张有32列都是nvarchar(max)。最初遇到这个异常的时候,小弟历经磨难,找了很多方法,最后都不太
    ##凑效,于是,于是,我就有个邪恶的想法:找到现在内容最长的(max_length),然后把nvarchar(max)类型改为nvarchar(max_length+300),反正我们数据库用了差不多3年,
    ##我相信再过30年,这类型长度也就最多不过这样,永远不可能4096KB~~~(说这到里,想起DBA总在对字段长度锱铢必较,是很有道理的)
    if($@)
    {
         $false_Table = "\n$false_Table,$relt       ERROR:".$@;
    }
}
if($false_Table)
{
    print "有下列表,数据未导入:$false_Table";
}

#插入数据的实体
sub export_data_in
{
    #构建SQL
    ($select_columns,$insert_columns,$columns_count,$sort_column,$table_name,$column_types) = @_;
    $rows_count=0;
    $dbh2=DBI->connect('dbi:ODBC:##隐去的MSSQL访问ODBC连接##','##隐去的MSSQL访问用户名##','##隐去的MSSQL访问密码##');
    my $sth_sc=$dbh2->prepare("select count(1) from $table_name");
    $sth_sc->execute();
    @data_count=$sth_sc->fetchrow_array();
    #测试语句,先不让其大于2000
    if(@data_count[0]>0)
    {
        @data_count[0]=20;
    }
    
    my $relt;
    if(@data_count[0]>2000)
    {
        $begin_cnt = 0;
        $end_cnt = 2000;
        while($begin_cnt <= @data_count[0])
        {
            ##先测试所有的表,加的测试SQL
            $sql_select="select top 10 $insert_columns  from $table_name";
            
            ##正式运行时SQL
#            $sql_select="select $insert_columns 
#            FROM 
#            (
#                SELECT $select_columns,ROW_NUMBER() OVER (ORDER BY $sort_column) AS RowNum
#                FROM $table_name
#            ) as t
#            where t.RowNum  BETWEEN $begin_cnt and $end_cnt";
            
            ##提示信息
            print "exporting data $table_name;total:@data_count[0];now:$begin_cnt \n";
            ##导数据,输入参数?请按上边的
            $relt = export_data($table_name,$sql_select,$insert_columns,$columns_count,$column_types);
            
            $begin_cnt = $begin_cnt + 2001;
            $end_cnt = $end_cnt + 2001;
            if($relt eq "False")
            {
                return $relt;
            }
        }
    }
    elsif(@data_count[0]>0)
    {
        ##测试SQL
        $sql_select = "select top 10 $select_columns from $table_name";
        ##正式SQL
#        $sql_select = "select $select_columns from $table_name";
        print "exporting data $table_name;total:@data_count[0];now:$begin_cnt \n";
        ##导数据,输入参数?同上
        $relt = export_data($table_name,$sql_select,$insert_columns,$columns_count,$column_types);
    }
    $relt;
}
#其实,这个才是真正导数据的方法
sub export_data
{
#    ($table_name,$sql_select,$insert_columns,$columns_count,@column_types)=($_[0],$_[1],$_[2],$_[3]);
    ($table_name,$sql_select,$insert_columns,$columns_count,$column_types)=@_;
    $dbh_mssql=DBI->connect('dbi:ODBC:##隐去的MSSQL访问ODBC连接##','##隐去的MSSQL访问用户名##','##隐去的MSSQL访问密码##');
    
    ##mysql连接信息
    ##mysql数据库
    my $db_name="##隐去的mysql导入数据库##";
    ##mysql数据库地址
    my $location="##隐去的mysql地址##";
    ##mysql数据库端口
    my $port="##隐去的MySQL数据库端口##";
    my $data_base = "DBI:mysql:$db_name:$location:$port";
    my $sth_select=$dbh_mssql->prepare($sql_select);
    $sth_select->execute();
    my $data_string = "";
    ##再次提示,fetchrow_arrayref()要快点~~~而且,不只快一点~~
    while(@select_data=$sth_select->fetchrow_array())
    {
        if($data_string ne '')
        {
            $data_string="$data_string ,";
        }
        my $data_col="";
        my $i=0;
        while($i<$columns_count)
        {
            if ($data_col ne "")
            {
                $data_col="$data_col ,";
            }
            #对geometry值的特殊处理,如果有其它类型需要特殊处理,请在else后加eleif....
            if(@$column_types[$i] eq "geometry")
            {
                $data_col="$data_col GeomFromText('@select_data[$i]',4326)"
            }
            else 
            {
                $data_col="$data_col '@select_data[$i]'";
            }
            $i=$i+1;
            
        }
        #构建插入的时候的值字符串
        $data_string="$data_string($data_col)\n";
    }
    
    if($data_string ne "")
    {
        #mySQL用户名
        my $db_user="##隐去的MYSQL用户名##";
        #mysql密码
        my $db_pass="##隐去的MYSQL密码##";
        my $dbh_mysql=DBI -> connect($data_base,$db_user,$db_pass);
        #插入的SQL语句
        my $insert_sql = "INSERT INTO $table_name($insert_columns)values $data_string ;";
        my $sth_mysql=$dbh_mysql->prepare($insert_sql);
        
        $sth_mysql->execute() or die "插入到MYSQL报错:$dbh_mysql->errstr; SQL语句如下:\n $insert_sql \n";
        $dbh_mysql->disconnect;
    }
    else
    {
        return "False";
    }
    
    #底下的东西,全是释放变量
    undef $sth_select;
    undef $data_base ;
    undef $dbh_mssql;
    undef $sth_mysql;
    undef $dbh_mysql;
    undef $data_string;
    undef @select_data;
}

#获得表的列
sub get_columns
{
    print "loading columns of $_[0] \n";
    $dbh2=DBI->connect('dbi:ODBC:##隐去的MSSQL访问ODBC连接##','##隐去的MSSQL访问用户名##','##隐去的MSSQL访问密码##');
    my $sql="select col.name,tp.name from sys.all_columns col
                    inner join sys.types tp on col.system_type_id=tp.system_type_id  and col.user_type_id=tp.user_type_id
                    where object_id=$_[1]";
    my $cols=$dbh2 -> prepare($sql);
    $cols->execute();
    my $cols_insert = "";
    my $cols_select = "";
    my $cols_count = 0;
    my $sort_column="";
    my @cols_types;
    while(@col= $cols->fetchrow_array())
    {
        ($col_name,$type_name)=@col;
        @cols_types[$cols_count]=$type_name;
        if($cols_count>0)
        {
            $cols_insert="$cols_insert ,";
            $cols_select="$cols_select ,";
        }
        else
        {
            $sort_column="[$col_name]";
        }
        #对某些类型的特殊处理:hierarchyid
        if($type_name eq "hierarchyid")
        {
            $cols_select = "$cols_select [$col_name].ToString() as $col_name";
            $cols_insert = "$cols_insert `$col_name`";
        }
        elsif($type_name eq "geometry")
        {
            $cols_select = "$cols_select [$col_name].STAsText() as $col_name";
            $cols_insert = "$cols_insert `$col_name`";
        }
        else
        {
            $cols_select="$cols_select [$col_name]";
            $cols_insert = "$cols_insert `$col_name`";
        }
        $cols_count++;
    }
    #perl函数的返回很有意思。。。
    ($cols_select,$cols_insert,$cols_count,$sort_column,\@cols_types)
}

代码上完,稍后有一个总结,谢谢大家听小弟啰嗦。。。。

 

 

 
反对 0举报 0 评论 0
 

免责声明:本文仅代表作者个人观点,与乐学笔记(本网)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
    本网站有部分内容均转载自其它媒体,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责,若因作品内容、知识产权、版权和其他问题,请及时提供相关证明等材料并与我们留言联系,本网站将在规定时间内给予删除等相关处理.

  • Linux下安装Perl和Perl的DBI模块
    今天在虚拟机测试shell脚本的时候,有些命令使用不了。比如说 mysqlhotcopy ,它提示Perl的版本太低。我用的 RedHat9 的Perl才5.8.0版本。。。(2002年以前的)严重过时。所以重新安装了新版本的 Perl,过程记录如下: 1、在官方网站下载新版本的源码包:http:
    03-16
  • Perl 与Form
    说明事项: 這個範例用來說明如何經由網頁上的HTML form 表單元件來呼叫伺服器端的perl 程式。这个范例用来说明如何经由网页上的HTML form 表单元件来呼叫伺服器端的perl 程式。首先在網頁上設計表單元件,這個範例是設計一個按鈕,其原始碼如下:首先在网页
    02-10
  • Perl学习 perl培训
    http://www.sun126.com/perl5/perl5-1.htm翻译: flamephoenix 第一章 概述一、Perl是什么?二、Perl在哪里?三、运行四、注释一、Perl是什么?  Perl是Practical Extraction and Report Language的缩写,它是由Larry Wall设计的,并由他不断更新和维护,用
    02-10
  • - calm_水手">Perl中的箭头符-> - calm_水手
    Perl中的箭头符-2012-05-21 17:14 calm_水手 阅读(623) 评论(0) 编辑 收藏 举报  有两种用法,都和解引用有关。第一种用法,就是解引用。根据 - 后面跟的符号的不同,解不同类型的引用,-[] 表示解数组引用,-{} 表示解散列引用,-() 表示解子程序引
    02-09
  • Regex in Perl
    Regex in Perl
    regex literal   代表正则文字, 就是 m/regex/ 部分中的 regex, 这部分有自己的解析规则. 用 Perl 的行话就是 "表示正则含义的双引号字符串(regx-aware double-quoted string)", 及处理后传递给正则引擎的结果. 正则文字支持的特性:  1. 变量插值.    
    02-09
  • perl脚本语言学习 perl脚本调用perl脚本
    来公司的第二个星期便看了一下perl语言,发现掌握一门脚本语言还是非常有用的。到现在为止已经入职两个月,用perl脚本做了这些活:1. 修改了公司的一个爬取网页源代码的脚本2. 改进了一个出特征库的脚本,根据svn status的状态,来优化,将只需要添加的DB的数
    02-09
  • Perl模块的安装方法 perl 安装模块
    1. 下载离线安装包 *.tar.gz的形式解包后,#perl Makefile.PL#make#make install2. 在联网的情况下,通过CPAN安装# perl -MCPAN -e shellcpan install PAR::Packer 
    02-09
  • Perl像C一样强大,像awk、sed等脚本描述语言一
    Perl是由Larry Wall设计的,并由他不断更新和维护的编程语言。Perl具有高级语言(如C)的强大能力和灵活性。事实上,你将看到,它的许多特性是从C语言中借用来的。Perl与 脚本语言一样,Perl不需要编译器和链接器来运行代码,你要做的只是写出程序并告诉Perl
    02-09
  • 27-Perl 进程管理
    1.Perl 进程管理Perl 中你可以以不同的方法来创建进程。本教程将讨论一些进程的管理方法。你可以使用特殊变量 $$ 或 $PROCESS_ID 来获取进程 ID。%ENV 哈希存放了父进程,也就是shell中的环境变量,在Perl中可以修改这些变量。exit() 通常用于退出子进程,主
    02-09
  • 在perl中简单的正则匹配 正则匹配或的使用
    (一)、在perl中关于元字符的匹配元字符代表含义点号( .)匹配处换行符以外的任何单字符星号(*)匹配前面的内容零次或多次反斜线屏蔽元字符的特殊含义。\\代表\,\.匹配点号.*匹配所有的字符串加号(+)匹配前一个条目一次以上问号(?)表示前面一个条目可
    02-09
点击排行