perl - dbd:: oracle
Perl DBI: ejecuta SQL Script con mĂșltiples instrucciones (4)
La base de datos controla cuántas sentencias se pueden ejecutar a la vez. No recuerdo si Oracle permite múltiples declaraciones por prepare
o no (MySQL lo hace). Prueba esto:
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;
Por supuesto, obtendrá un mejor manejo de errores si rompe las declaraciones. Puede usar un analizador simple para dividir la cadena en declaraciones individuales:
#!/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";
}
Tengo un archivo sql test.sql utilizado para ejecutar algunos SQL (crear objeto / actualizar / eliminar / insertar) que se puede ver así
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);
Por lo general, solo usaría SQLPLUS (desde dentro de Perl) para ejecutar este test.sql usando este comando: @ test.sql
¿Hay alguna manera de hacer lo mismo usando DBI en Perl? Hasta ahora, he encontrado que DBI solo puede ejecutar una declaración a la vez, y sin el ";" al final.
Oracle puede ejecutar múltiples instrucciones SQL en una preparación usando un bloque PL / SQL anónimo.
p.ej
$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 (creación o eliminación de objetos) es más complicado, sobre todo porque es algo que no debería estar haciendo ad-hoc.
Por favor, eche un vistazo a este nuevo módulo de CPAN: DBIx :: MultiStatementDo
Ha sido concebido precisamente para eso.
Puede agregar otra capa de lógica en Perl que analiza el script SQL, lo divide en sentencias y lo ejecuta uno por uno utilizando la técnica anterior
--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);