如何使用Perl获取存储过程结果?

时间:2017-01-03 09:36:31

标签: sql-server perl stored-procedures

我是sql的初学者。我创建了如下程序

create procedure  testprocedure2 as
select 'one'
select 'three'
select 'five'

当我在数据库中执行查询时它会显示三个结果one three five。 sql查询是exec TEST_ABC_DB.dbo.testprocedure2

当我在Perl中运行相同的查询时,它只提供一条one

的记录
$sth = $dbh->prepare("exec TEST_ABC_DB.dbo.testprocedure2");
$sth->execute();
while (@row= $sth->fetchrow_array())  
{
    print $row[0]."\t";
    print "\n";
}

我不知道是什么问题。我该如何解决?我希望这个答案在yesterday's question

中有所帮助

3 个答案:

答案 0 :(得分:7)

通过驱动程序(例如DBD::ODBC

Since you're using DBD::ODBC,您可以使用more_results provided by that driver在一个execute中获取多个查询的结果。

这是他们在文档中显示的示例。

do {
   my @row;
   while (@row = $sth->fetchrow_array()) {
      # do stuff here
   }
} while ($sth->{odbc_more_results});

如果我们想对您的示例查询执行此操作,则它几乎相同。您运行存储过程,然后继续do {} while构造(请注意,这不是块,您不能next!)。

my $sth = $dbh->prepare("exec TEST_ABC_DB.dbo.testprocedure2");
$sth->execute;

do {
    while (my @row = $sth->fetchrow_array()) {
        print $row[0]."\t";
        print "\n";
    }
} while ($sth->{odbc_more_results});

这应打印您的预期结果。

one
three
five

其他一些司机也提供此功能。如果他们这样做,您可以拨打$sth->more_results,而不是使用如下所述的内部。

如果您的驱动程序不支持此

,则解决方法

DBI本身无法一次返回多个查询的结果。您可以运行它们,但无法获得结果。

如果您的程序中确实需要三个单独的查询并且想要所有结果,那么ShakheerShahzad使用UNION的答案就会被发现。

然而,你的例子可能是人为的。您可能在每个查询中都没有相同数量的列,您需要区分每个查询的结果。

我们必须为此更改SQL和Perl代码。

要使其正常工作,您可以插入其他行,以后可以使用这些行将每个结果堆栈映射到每个查询。

让我们说程序如下:

create procedure testprocedure3 as
select 'one'
select 'three', 'three', 'three'
select 'five', 'five', 'five', 'five', 'five'

每个查询仍然只有一行,但它应该作为示例。使用UNION方法,它首先变为:

create procedure testprocedure3 as
select 'one'
union all
select 'three', 'three', 'three'
union all
select 'five', 'five', 'five', 'five', 'five'

如果你运行它,它可能会失败。在ANSI SQL中,UNION需要在其所有查询中具有相同数量的列,因此我假设SQLServer也想要这样。我们需要用NULL填充它们。将它们添加到所有查询中,使它们匹配列数最大的列数。

create procedure testprocedure3 as
select 'one', NULL, NULL, NULL, NULL
union all
select 'three', 'three', 'three', NULL, NULL
union all
select 'five', 'five', 'five', 'five', 'five'

如果我们现在使用以下代码在Perl中循环它,我们将某些返回。

use Data::Dumper;
my $sth = $dbh->prepare("exec TEST_ABC_DB.dbo.testprocedure3");
$sth->execute;
while ( my $row = $sth->fetchrow_arrayref ) {
    print Dumper $row;
}

我们会看到与此类似的输出(我没有运行代码,但手动编写输出):

$VAR1 = [ 'one', undef, undef, undef, undef ];
$VAR1 = [ 'three', 'three', 'three', undef, undef ];
$VAR1 = [ 'five', 'five', 'five', 'five', 'five' ];

我们无法知道哪一行属于查询的哪一部分。所以让我们插入一个分隔符。

create procedure testprocedure3 as
select 'one', NULL, NULL, NULL, NULL
union all
select '-', '-', '-', '-', '-'
union all
select 'three', 'three', 'three', NULL, NULL
union all
select '-', '-', '-', '-', '-'
union all
select 'five', 'five', 'five', 'five', 'five'

现在,Perl代码的结果如下所示:

$VAR1 = [ 'one', undef, undef, undef, undef ];
$VAR1 = [ '-', '-', '-', '-', '-' ];
$VAR1 = [ 'three', 'three', 'three', undef, undef ];
$VAR1 = [ '-', '-', '-', '-', '-' ];
$VAR1 = [ 'five', 'five', 'five', 'five', 'five' ];

这可能不是分隔符的最佳选择,但它很好地说明了我打算做的事情。我们现在要做的就是把它分成不同的结果。

use Data::Dumper;

my @query_results;
my $query_index = 0;
my $sth = $dbh->prepare("exec TEST_ABC_DB.dbo.testprocedure3");
$sth->execute;
while ( my $row = $sth->fetchrow_arrayref ) {
     # move to the next query if we hit the delimiter
     if ( join( q{}, @$row ) eq q{-----} ) {
         $query_index++;
         next;
     }

     push @{ $query_results[$query_index] }, $row;
}

print Dumper \@query_results;

我定义了两个新变量。 @query_results保存所有结果,按查询编号排序。 $query_index是该数组的索引。它从0开始。

我们迭代所有结果行。这里$row lexical 非常重要。在循环头中使用my创建必须。 (您正在使用use strict,对吧?)如果我们看到分隔符,我们会增加$query_index并继续前进。如果我们没有常规结果行,那么我们会将其粘贴到当前查询索引中的@query_results数组中。

整体结果是一个包含数组数组的数组。

$VAR1 = [
   [
       [ 'one', undef, undef, undef, undef ]
   ], 
   [
       [ 'three', 'three', 'three', undef, undef ]
   ], 
   [
       [ 'five', 'five', 'five', 'five', 'five' ]
   ], 
];

如果你有实际的查询返回很多行,这开始很有意义。

当然,您不必存储所有结果。您也可以直接在循环中处理每个查询的结果。

免责声明:我没有在此答案中运行任何代码,因为我无法访问SQLServer。它可能包含Perl和SQL中的语法错误。但它确实证明了这种方法。

答案 1 :(得分:2)

您创建的过程返回3个结果集。而你只捕获了1个结果。如果您对集合不感兴趣,请使用UNION ALL

将它们作为单个结果
create procedure  testprocedure2 as
select 'one'
union all
select 'three'
union all
select 'five'

修改:

如果要捕获从存储过程返回的多个结果集,这是一个用MySQL数据库Multiple data sets in MySQL stored procedures

解释的一个很好的例子

答案 2 :(得分:1)

简单地使用union就像这样只有一个表显示数据。

enter image description here