Perl MSSQL 액세스 및 MySQL 데이터베이스 스크립트 인스턴스로 마이그레이션

6419 단어
리눅스에는 MSSQL을 위한 액세스 라이브러리가 없지만 MSSQL은sybase에서 파생된 것이기 때문에 Sybase에 접근하는 라이브러리도 당연히 MSSQL에 접근할 수 있다.FreeTDS는 바로 이러한 실현이다.Perl에서는 일반적으로 DBI를 사용하여 데이터베이스에 접근하기 때문에 시스템에FreeTDS가 설치된 후에 DBI를 사용하여FreeTDS를 통해 MSSQL 데이터베이스에 접근할 수 있다. 예:
 
  
using DBI;
my $cs = "DRIVER={FreeTDS};SERVER= ;PORT=1433;DATABASE= ;UID=sa;PWD= ;TDS_VERSION=7.1;charset=gb2312";
my $dbh = DBI->connect("dbi:ODBC:$cs") or die $@;

본인은 윈도우즈를 잘 사용하지 않기 때문에 QQ군 데이터베이스를 연구하기 위해 MSSQL에서 MySQL로 데이터를 옮겨야 합니다. 특히 QQ군 데이터베이스에 Windows Server 2008과 SQL Server 2008r2를 설치했습니다. 며칠 지나면 평가가 만료됩니다. MySQL을 연구한 Workbench는 MS SQL Server에서 데이터를 옮기는 능력이 있습니다. 그러나 QQ군과 같은 거대한 데이터와 라이브러리별 데이터는 너무 번거롭습니다.따라서 데이터베이스를 MSSQL에서 MySQL로 이동하는 데 사용되는 일반적인 퍼블릭 스크립트를 썼고 bash와 결합하여 이 20여 개의 라이브러리에서 100장의 테이블을 쉽게 옮겼다. 퍼블릭 코드는 다음과 같다.
 
  
#!/usr/bin/perl
use strict;
use warnings;
use DBI;


die "Usage: qq db
" if @ARGV != 1;
my $db = $ARGV[0];

print "Connectin to databases $db...
";
my $cs = "DRIVER={FreeTDS};SERVER=MSSQL ;PORT=1433;DATABASE=$db;UID=sa;PWD=MSSQL ;TDS_VERSION=7.1;charset=gb2312";

sub db_connect
{
    my $src = DBI->connect("dbi:ODBC:$cs") or die $@;
    my $target = DBI->connect("dbi:mysql:host=MySQL ", "MySQL ", "MySQL ") or die $@;
    return ($src, $target);
}
my ($src, $target) = db_connect;

print "Reading table schemas....
";

my $q_tables = $src->prepare("SELECT name FROM sysobjects WHERE xtype = 'U' AND name != 'dtproperties';");#
my $q_key_usage = $src->prepare("SELECT TABLE_NAME, COLUMN_NAME from INFORMATION_SCHEMA.KEY_COLUMN_USAGE;");#
$q_tables->execute;
my @tables = ();
my %keys = ();
push @tables, @_ while @_ = $q_tables->fetchrow_array;

$q_tables->finish;

$q_key_usage->execute();
$keys{$_[0]} = $_[1] while @_ = $q_key_usage->fetchrow_array;
$q_key_usage->finish;


#
my $q_index = $src->prepare(qq(
    SELECT T.name, C.name
    FROM sys.index_columns I
    INNER JOIN sys.tables T ON T.object_id = I.object_id
    INNER JOIN sys.columns C ON C.column_id = I.column_id AND I.object_id = C.object_id;
));
$q_index->execute;
my %table_indices = ();
while(my @row = $q_index->fetchrow_array)
{
    my ($table, $column) = @row;
    my $columns = $table_indices{$table};
    $columns = $table_indices{$table} = [] if not $columns;
    push @$columns, $column;
}
$q_index->finish;

# MySQL
$target->do("DROP DATABASE IF EXISTS `$db`;") or die "Cannot drop old database $db
";
$target->do("CREATE DATABASE `$db` DEFAULT CHARSET = utf8 COLLATE utf8_general_ci;") or die "Cannot create database $db
";
$target->disconnect;
$src->disconnect;


my $total_start = time;
for my $table(@tables)
{
    my $pid = fork;
    unless($pid)
    {
        ($src, $target) = db_connect;
        my $start = time;
        $src->do("USE $db;");
        # , MySQL DDL
        my $q_schema = $src->prepare("SELECT COLUMN_NAME, IS_NULLABLE, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = ? ORDER BY ORDINAL_POSITION;");
        $target->do("USE `$db`;");
        $target->do("SET NAMES utf8;");
        my $key_column = $keys{$table};
        my $ddl = "CREATE TABLE `$table` (
";
        $q_schema->execute($table);
        my @fields = ();
        while(my @row = $q_schema->fetchrow_array)
        {
            my ($column, $nullable, $datatype, $length) = @row;
            my $field = "`$column` $datatype";
            $field .= "($length)" if $length;
            $field .= " PRIMARY KEY" if $key_column eq $column;
            push @fields, $field;
        }
        $ddl .= join(",
", @fields);
        $ddl .= "
) ENGINE = MyISAM;

";
        $target->do($ddl) or die "Cannot create table $table
";
        #
        my $indices = $table_indices{$table};
        if($indices)
        {
            for(@$indices)
            {
                $target->do("CREATE INDEX `$_` ON `$table`(`$_`);
") or die "Cannot create index on $db.$table$.$_
";
            }
        }
        #
        my @placeholders = map {'?'} @fields;
        my $insert_sql = "INSERT DELAYED INTO $table VALUES(" .(join ', ', @placeholders) . ");
";
        my $insert = $target->prepare($insert_sql);
        my $select = $src->prepare("SELECT * FROM $table;");
        $select->execute;
        $select->{'LongReadLen'} = 1000;
        $select->{'LongTruncOk'} = 1;
        $target->do("SET AUTOCOMMIT = 0;");
        $target->do("START TRANSACTION;");
        my $rows = 0;
        while(my @row = $select->fetchrow_array)
        {
            $insert->execute(@row);
            $rows++;
        }
        $target->do("COMMIT;");
        # ,
        my $elapsed = time - $start;
        print "Child process $$ for table $db.$table done, $rows records, $elapsed seconds.
";
        exit(0);
    }
}
print "Waiting for child processes
";
#
while (wait() != -1) {}
my $total_elapsed = time - $total_start;
print "All tasks from $db finished, $total_elapsed seconds.
";


이 스크립트는 테이블마다 하위 프로세스와 해당하는 데이터베이스 연결을 보여 줍니다. 따라서 이 이전을 하기 전에 목표 MySQL 데이터베이스 설정의 최대 연결 수를 견딜 수 있도록 해야 합니다.그리고 bash에서 실행합니다.
 
  
for x in {1..11};do ./qq.pl QunInfo$x; done
for x in {1..11};do ./qq.pl GroupData$x; done

상관하지 마십시오. 스크립트는 MSSQL 이쪽 테이블 구조에 따라 MySQL 쪽에서 같은 구조를 만들고 색인을 설정합니다.

좋은 웹페이지 즐겨찾기