【问题标题】:How to get the stored procedure result using Perl?如何使用 Perl 获取存储过程结果?
【发布时间】:2017-06-22 15:03:52
【问题描述】:

我是 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 有帮助

【问题讨论】:

  • 你能告诉我们当你直接运行你的程序时输出到底是什么样子的吗?复制准确的输出或从表格视图中截取屏幕截图。
  • 我就是这么想的。 Shakeer Mirza 有它的解释,但 Shahzad Riaz 也首先回答了同样的问题。由于您实际上是在过程中运行三个命令,因此如果不更改过程,就无法从 Perl 中的 DBI 获得所有三个结果。
  • @simbabque 但是当我编写错误查询时,例如select 10/0。现在 Perl 脚本显示错误消息。那么 DBI 是如何读取它的呢?
  • 好问题。我会说数据库在遇到错误时会抛出错误,并停止运行查询。 DBI 知道如何处理它,然后您会看到错误。但是我对 SQLServer 一点也不熟悉。
  • 您使用的是哪个 DBI 驱动程序?

标签: sql-server perl stored-procedures


【解决方案1】:

通过驱动程序(例如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 也需要这个。我们需要用NULLs 填写它们。将它们添加到所有查询中,使它们与列数最多的查询中的列数相匹配。

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 中循环它,我们会得到 something 回来。

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 在这里是词法,这一点很重要。它必须在循环头中使用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 中的语法错误。但它确实展示了这种方法。

【讨论】:

  • 我得到了我想要的。然后我也得到了昨天的答案。通过设置RaiseError => 0。但我面临一个问题,即我无法将运行时错误存储在变量中。
  • @mkHun 我认为这可能是一个新问题,因为我们对您如何进行查询有了更多了解。但我认为如果没有实际的 SQL Server 就很难使用它。
  • 我是通过$error = $sth->errstr 得到的。并感谢您花时间为我服务:)
【解决方案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

【讨论】:

  • 我不能试试这个。因为这是示例查询。有没有办法在 Perl 脚本中做到这一点?
  • 查看上面的编辑,您可能需要查看提供的链接。
  • 哇,太酷了。它内置在 dbd::mysql 驱动程序中,并记录在 here 中。但这使得它只有 mysql。
  • 这就是为什么用 +1 来支持你的答案的原因 :) @simbabque
  • 您的链接和 OP 对 DBD::ODBC 的评论都帮助我找到了解决方案。谢谢你。 :)
【解决方案3】:

像这样简单使用联合然后只显示一个带有数据的表。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-07-24
    • 1970-01-01
    • 2020-10-23
    • 2017-08-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多