简体   繁体   中英

Getting ResultSet from stored procedure within another stored procedure

I have a stored procedure that calls another stored procedure. The inner stored procedure returns a result set. After using a CallableStatement to execute the calling stored procedure I am unable to get the result set returned by called stored procedure.

I tried both execute and executeQuery for execution of callable statement. When I execute the calling stored procedure from SQL Server I am getting proper results.

Calling procedure:-

ALTER PROC [User].[Get_Data]
(@UserID NVARCHAR(20))
AS
BEGIN
Select 'User Data'
Exec [Order].[Get_Order] @UserID
END

Called procedure:-

ALTER PROC [Order].[Get_Order]
(@UserID NVARCHAR(20))
AS
BEGIN
Select * from orders where userId=@UserID
END

Your outer stored procedure is returning two result sets:

  1. The results from Select 'User Data'
  2. The results from Exec [Order].[Get_Order] @UserID

You need to call .getMoreResults() in order to retrieve the second result set, eg,

CallableStatement cs = connection.prepareCall("{CALL Get_Data (?)}");
cs.setString(1, "gord");
ResultSet rs = cs.executeQuery();
System.out.println("[First result set]");
while (rs.next()) {
    System.out.printf("(No column name): %s%n", rs.getString(1));
}
cs.getMoreResults();
rs = cs.getResultSet();
System.out.println();
System.out.println("[Second result set]");
while (rs.next()) {
    System.out.printf("userId: %s, orderId: %s%n", 
            rs.getString("userId"), rs.getString("orderId"));
}

producing

[First result set]
(No column name): User Data

[Second result set]
userId: gord, orderId: order1
userId: gord, orderId: order2

(Tested using mssql-jdbc-6.2.1.jre8.jar connecting to SQL Server 2014.)

For more details, see

How to get *everything* back from a stored procedure using JDBC

You cannot select the results of a stored procedure directly within SQL Server itself. You need to first insert the result into a temp table as per example below.

Example use:

-- Create a tempoary table to store the results.
CREATE TABLE #UserOrderDetail
(
    UserData NVARCHAR(50) -- Your columns here
)

-- Insert result into temp table.
-- Note that the columns returned from procedure has to match columns in your temp table.
INSERT INTO #UserOrderDetail
EXEC [Order].[Get_Order] @UserID

-- Select the results out of the temp table.
SELECT *
FROM    #UserOrderDetail

If the intent is to simply return one or more result sets to a client application, you should ensure that the SET NOCOUNT ON statement is added to the top of your stored procedures, this will prevent SQL Server from sending the DONE_IN_PROC messages to the client for each statement in the stored procedure. Database libraries like ODBC, JDBC and OLEDB can get confused by the row counts returned by the various insert and update statements executed within a SQL Server stored procedures. Your original procedure will look as follow:

ALTER PROC [User].[Get_Data]
(
    @UserID NVARCHAR(20)
)
AS
BEGIN

    SET NOCOUNT ON

    SELECT 'User Data'
    EXEC [Order].[Get_Order] @UserID

END

The correct way to do this with JDBC

Getting this right with JDBC is quite hard. The accepted answer by Gord Thompson might work, but it doesn't follow the JDBC spec to the word, so there might be edge cases where it fails, eg when there are interleaved update counts (known or accidental), or exceptions / messages.

I've blogged about the correct approach in detail here . The Oracle version is even more tricky, in case you need it . So here it goes:


// If you're daring, use an infinite loop. But you never know...
fetchLoop:
for (int i = 0, updateCount = 0; i < 256; i++) {

    // Use execute(), instead of executeQuery() to handle
    // leading update counts or exceptions
    boolean result = (i == 0)
        ? s.execute()
        : s.getMoreResults();
 
    // Warnings here
    SQLWarning w = s.getWarnings();
    for (int j = 0; j < 255 && w != null; j++) {
        System.out.println("Warning     : " + w.getMessage());
        w = w.getNextWarning();
    }
 
    // Don't forget this
    s.clearWarnings();
 
    if (result)
        try (ResultSet rs = s.getResultSet()) {
            System.out.println("Result      :");
 
            while (rs.next())
                System.out.println("  " + rs.getString(1));
        }
    else if ((updateCount = s.getUpdateCount()) != -1)
        System.out.println("Update Count: " + updateCount);
    else
        break fetchLoop;
}

Using jOOQ

Note that in case you're using jOOQ, you could leverage code generation for your stored procedures and call the simplified API to do this in a few lines only:

GetDatap = new GetData();
p.setUserId("gord");
p.execute(configuration);
Results results = p.getResults();
 
for (Result<?> result : results)
  for (Record record : result)
    System.out.println(record);

Disclaimer: I work for the company behind jOOQ

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM