Join the Stack Overflow Community
Stack Overflow is a community of 6.6 million programmers, just like you, helping each other.
Join them; it only takes a minute:
Sign up

I'm beginner in sql. I have created the procedure as follows

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

When I execute query into the database It shows the three result one three five. sql query is exec TEST_ABC_DB.dbo.testprocedure2

When I run the same query into the Perl it gives only one record which is one

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

I don't know what is the problem. How can I fix it? I hope this answer will help in yesterday's question

share|improve this question
    
Can you show us what exactly the output looks like when you run your procedure directly? Copy out the exact output or take a screenshot from the table view. – simbabque Jan 3 at 9:50
    
That's what I thought. Shakeer Mirza has it spot-on with explanation, but Shahzad Riaz also answered the same thing first. Since you're essentially running three commands in your procedure, there is no way to get all three results from DBI in Perl without changing the procedure. – simbabque Jan 3 at 9:57
    
@simbabque But when I write the query with error for example select 10/0. Now Perl script shows the error message. Then how the DBI read it ? – mkHun Jan 3 at 10:03
1  
Good question. I'd say the DB throws the error when it encounters it, and stops running the queries. DBI knows how to handle it, and then you see the error. But I'm not familiar with SQLServer at all. – simbabque Jan 3 at 10:04
    
Which DBI driver are you using? – simbabque Jan 3 at 13:10
up vote 5 down vote accepted

Through the DBD::ODBC driver

Since you're using DBD::ODBC, you can use more_results provided by that driver to get the results of multiple queries in one execute.

This is the example they show in the documentation.

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

If we want to do this with your example queries, it's pretty much the same. You run your stored procedure, and then proceed with the do {} while construct (note that this is not a block, you cannot next out of it!).

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});

This should print your expected result.

one
three
five

Workaround if your driver doesn't support this

There is no way for DBI itself to return the result of multiple queries at once. You can run them, but you cannot get the results.

If you really need three separate queries in your procedure and want all of the results, the answers by Shakheer and Shahzad to use a UNION are spot on.

However, your example is probably contrived. You probably don't have the same amount of columns in each of those queries, and you need to distinguish the results of each of the queries.

We have to change SQL and Perl code for this.

To get that to work, you can insert additional rows that you can later use to map each stack of results to each query.

Let's say the procedure looks like this:

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

This is still just one row per query, but it should do as an example. With the UNION approach, it first becomes this:

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

If you run this, it might fail. In ANSI SQL a UNION needs to have the same number of columns in all its queries, so I assume SQLServer also wants this. We need to fill them up with NULLs. Add them to all the queries so they match the number of columns in the one with the largest number of columns.

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'

If we now loop over it in Perl with the following code, we'll get something back.

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

We'll see output similar to this (I didn't run the code, but wrote the output manually):

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

We have no way of knowing which line belongs to which part of the query. So let's insert a delimiter.

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'

Now the result of the Perl code will look as follows:

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

This might not be the best choice of delimiter, but it nicely illustrates what I am planning to do. All we have to do now is split this into separate results.

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;

I've defined two new variables. @query_results holds all the results, sorted by query number. $query_index is the index for that array. It starts with 0.

We iterate all the resulting rows. It's important that $row is lexical here. It must be created with my in the loop head. (You are using use strict, right?) If we see the delimiter, we increment the $query_index and move on. If we don't we have a regular result line, so we stick that into our @query_results array within the current query's index.

The overall result is an array with arrays of arrays in it.

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

If you have actual queries that return many rows this starts making a lot of sense.

Of course you don't have to store all the results. You can also just work with the results of each query directly in your loop.


Disclaimer: I've run none of the code in this answer as I don't have access to an SQLServer. It might contain syntax errors in the Perl as well as the SQL. But it does demonstrate the approach.

share|improve this answer
    
I'm getting what I want. Then I have got the yesterday answer also. By setting RaiseError => 0. But I'm facing one problem which is I can't store the run time error in a variable. – mkHun Jan 3 at 14:38
    
@mkHun I think that might be a new question now that we know more about how you do your queries. But I think it's hard to play around with it without an actual SQL Server. – simbabque Jan 3 at 14:47
1  
I got it by $error = $sth->errstr. And thank for spend your time for me :) – mkHun Jan 3 at 14:57
1  
@mkHun this was fun and I learned something :) – simbabque Jan 3 at 14:58

The procedure you created is returning 3 result sets. And you are capturing only 1 result. If you are not bother about sets, make them as single result with UNION ALL

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

Edit:

If you want to capture multiple resultsets returned from stored procedure, here is a good example explained with MySQL database Multiple data sets in MySQL stored procedures

share|improve this answer
    
I can't try this. Because this is the sample query. Is there any possible way to do it in Perl Script? – mkHun Jan 3 at 9:58
1  
see the above edit, you may need to take a look at the link provided. – Shakeer Mirza Jan 3 at 10:15
    
Wow, that's cool. It's built into the dbd::mysql driver and documented here. But that makes it mysql-only. – simbabque Jan 3 at 13:08
    
And that is the reason why upvoted your answer with +1 :) @simbabque – Shakeer Mirza Jan 3 at 13:16
1  
Your link and the comment by the OP with DBD::ODBC both helped me to find the solution. Thank you. :) – simbabque Jan 3 at 13:50

simple use union all like this then only one table is shown with data.

enter image description here

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.