Perl MSSQL 에 접근 하여 MySQL 데이터베이스 스 크 립 트 인 스 턴 스 로 이전

6603 단어 PerlMSSQLMySQL
Linux 에 서 는 MSSQL 을 위 한 접근 라 이브 러 리 가 없습니다.그러나 MSSQL 에 서 는 원래 sybase 에서 파생 된 것 이기 때문에 Sybase 를 방문 하 는 라 이브 러 리 도 당연히 MSSQL 에 접근 할 수 있 습 니 다.FreeTDS 는 바로 이러한 실현 입 니 다.펄 에 서 는 보통 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 $@;
본인 은 windows 를 잘 사용 하지 않 기 때문에 QQ 군 데이터 베 이 스 를 연구 하기 위해 데 이 터 를 MSSQL 에서 MySQL 로 옮 겨 야 한다.특히 QQ 군 데이터 베 이 스 를 위해 Windows Server 2008 과 SQL Server 2008 r2 를 설치 했다.그러나 며칠 후 평가 가 만 료 되 었 습 니 다.MySQL 의 Workbench 는 MS SQL Server 에서 데 이 터 를 이전 하 는 능력 이 있 습 니 다.그러나 QQ 군 과 같은 거대 한 데이터 와 분 표 라 이브 러 리 의 데이터 에 있어 서 너무 번 거 로 워 서 통용 되 는 perl 스 크 립 트 를 썼 습 니 다.데이터 베 이 스 를 MSSQL 에서 MySQL 로 이전 하고 bash 와 결합 합 니 다.이 20 여 개의 라 이브 러 리 에 있 는 백 장의 시 계 를 편리 하 게 옮 겼 습 니 다.Perl 코드 는 다음 과 같 습 니 다

#!/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.
";

이 스 크 립 트 는 모든 표 fork 에 따라 키 프로 세 스 와 해당 하 는 데이터 베 이 스 를 연결 합 니 다.따라서 이 이전 을 하기 전에 목표 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 쪽 에서 같은 구 조 를 만 들 고 색인 을 설정 합 니 다.

좋은 웹페이지 즐겨찾기