2009-08-05 88 views
1

我有一个SQL文件TEST.SQL用来运行一些SQL(创建对象/更新/删除/插入),可以是这样的Perl的DBI - 运行SQL脚本多个语句

CREATE TABLE test_dbi1 (
    test_dbi_intr_no NUMBER(15) 
    , test_dbi_name  VARCHAR2(100); 

UPDATE mytable 
SET col1=1; 

    CREATE TABLE test_dbi2 (
    test_dbi_intr_no NUMBER(15) 
    , test_dbi_name  VARCHAR2(100); 

一般,我只是使用SQLPLUS(从Perl内)来执行此test.sql使用此命令: @ test.sql

有没有办法做同样的事情,在Perl中使用DBI? 到目前为止,我发现DBI一次只能执行一条语句,而没有“;”最后。

回答

6

数据库控制一次可以执行多少个语句。我不记得Oracle是否允许按照prepare多个语句(MySQL)。试试这个:

my $dbh = DBI->connect(
    "dbi:Oracle:dbname", 
    "username", 
    "password", 
    { 
     ChopBlanks  => 1, 
     AutoCommit  => 1, 
     RaiseError  => 1, 
     PrintError  => 1, 
     FetchHashKeyName => 'NAME_lc', 
    } 
); 
$dbh->do(" 
    CREATE TABLE test_dbi1 (
     test_dbi_intr_no NUMBER(15), 
     test_dbi_name  VARCHAR2(100) 
    ); 

    UPDATE mytable 
     SET col1=1; 

    CREATE TABLE test_dbi2 (
     test_dbi_intr_no NUMBER(15), 
     test_dbi_name  VARCHAR2(100) 
    ); 
"); 

$dbh->disconnect; 

当然,如果你打破了声明,你会得到更好的错误处理。您可以使用一个简单的解析器向上突破串入个人陈述:

#!/usr/bin/perl 

use strict; 
use warnings; 

my $sql = " 
    CREATE TABLE test_dbi1 (
     test_dbi_intr_no NUMBER(15), 
     test_dbi_name  VARCHAR2(100) 
    ); 

    UPDATE mytable 
     SET col1=';yes;' 
     WHERE col2=1; 

    UPDATE mytable 
     SET col1='Don\\'t use ;s and \\'s together, it is a pain' 
     WHERE col2=1; 


    CREATE TABLE test_dbi2 (
     test_dbi_intr_no NUMBER(15), 
     test_dbi_name  VARCHAR2(100) 
    ); 
"; 

my @statements = (""); 
#split the string into interesting pieces (i.e. tokens): 
# ' delimits strings 
# \ pass on the next character if inside a string 
# ; delimits statements unless it is in a string 
# and anything else 
# NOTE: the grep { ord } is to get rid of the nul 
# characters the split seems to be adding 
my @tokens  = grep { ord } split /([\\';])/, $sql; 
# NOTE: this ' fixes the stupid SO syntax highlighter 
#this is true if we are in a string and should ignore ; 
my $in_string = 0; 
my $escape  = 0; 
#while there are still tokens to process 
while (@tokens) { 
    #grab the next token 
    my $token = shift @tokens; 
    #if we are in a string 
    if ($in_string) { 
     #add the token to the last statement 
     $statements[-1] .= $token; 
     #setup the escape if the token is \ 
     if ($token eq "\\") { 
       $escape = 1; 
       next; 
     } 
     #turn off $in_string if the token is ' and it isn't escaped 
     $in_string = 0 if not $escape and $token eq "'"; 
     $escape = 0; #turn off escape if it was on 
     #loop again to get the next token 

     next; 
    } 
    #if the token is ; and we aren't in a string 
    if ($token eq ';') { 
     #create a new statement 
     push @statements, ""; 
     #loop again to get the next token 
     next; 
    } 
    #add the token to the last statement 
    $statements[-1] .= $token; 
    #if the token is ' then turn on $in_string 
    $in_string = 1 if $token eq "'"; 
} 
#only keep statements that are not blank 
@statements = grep { /\S/ } @statements; 

for my $i (0 .. $#statements) { 
    print "statement $i:\n$statements[$i]\n\n"; 
} 
+0

不幸的是,这不起作用,我得到了“ORA-00911:invalid character”,因为“;” 事情是,我有这个test.sql文件,我需要一种方法将它加载到Oracle使用DBI。 唯一的办法就是像你说的那样分解它。但是因为我永远不知道这个文件中到底会有什么,我怎么分解它? 如果我使用“;”分割文件这可能会导致问题,如果我有这样的更新: UPDATE mytable SET col1 ='; yes;' WHERE col2 = 1; – guigui42 2009-08-05 12:55:52

+0

这是Oracle抱怨,而不是Perl。对于Oracle不允许多重声明,我并不特别感到惊讶。它们很危险,并且允许某些形式的SQL注入攻击。 – 2009-08-05 13:01:40

3

Oracle可以在一个运行多个SQL语句准备使用匿名PL/SQL块。

$dbh->do(" 
    BEGIN 
     UPDATE table_1 SET col_a = col_a -1; 
     DELETE FROM table_2 where id in (select id from table_1 where col_a = 0); 
    END; 
"); 

DDL(创建或删除对象)较为复杂,主要是因为它是你不应该一个特设的基础上做的事情。

0

您可以添加在Perl逻辑的另一个层,解析SQL脚本,将其分解成语句,并使用该技术由一个执行它一个以上

--sql file 
    -- [statement1] 
    SQLCODE... 

    -- [statement2] 
    SQLCODE... 

#Gets queries from file. 
sub sql_q { 
    my ($self) = @_; 
    return $self->{sql_q} if $self->{sql_q}; 
    my $file = $self->{sql_queries_file}; 

    $self->{sql_q} || do { 
     -e $file || croak('Queries file ' . $file . ' can not be found.'); 
     my $fh = IO::File->new("< $file"); 
     my @lines; 
     ($fh->binmode and @lines = $fh->getlines and $fh->close) or croak $!; 

     my ($key); 
     foreach (0 .. @lines - 1) { 
      next if ($lines[$_] =~ /^;/); 
      if ($lines[$_] =~ /^--\s*?\[(\w+)\]/) { 
       $key = $1; 
      } 
      $self->{sql_q}{$key} .= $lines[$_] if $key; 
     } 
    }; 
    return $self->{sql_q}; 
} 
#then in your script 
#foreach statement something like 
$dbh->prepare($sql_obj->{sql_q}->{statement_name})->execute(@bindvars);