web-dev-qa-db-fra.com

Récupérer les données d'une procédure stockée après plusieurs jeux de résultats

Avec une procédure stockée dans SQL Server comportant plusieurs instructions select, existe-t-il un moyen de travailler avec ces résultats séparément tout en appelant la procédure?

Par exemple:

alter procedure dbo.GetSomething
as
begin
    select * from dbo.Person;
    select * from dbo.Car;
end;

Dans .NET, si j'appelle ce processus, je peux utiliser une SqlDataReader pour passer d'un jeu de résultats à un autre, afin de pouvoir récupérer facilement toutes les personnes et les voitures. En SQL cependant, quand j'exécute directement le proc, j'obtiens les deux jeux de résultats.

Si j'appelle:

insert @myTempTable
    exec dbo.GetSomething;

Ensuite, il se trompe car la définition de la colonne ne correspond pas. Si, par hasard, Person et Car ont les mêmes colonnes, cela concatène les deux et @myTempTable obtient tous les enregistrements des deux tables, ce qui n'est évidemment pas bon non plus.

Je peux définir de nouveaux types personnalisés représentant les deux jeux de résultats, et définir ces paramètres de sortie au lieu d’avoir plusieurs instructions select, mais je me demande s’il existe un meilleur moyen - d’extraire les deux résultats dans des tables temporaires ou de passer en boucle dans résultats, ou quelque chose.

MODIFIER

En fait, après une recherche plus approfondie, même les paramètres de la table de sortie ne résoudront pas ce problème - ils sont en lecture seule, et cela reste vrai dans SQL 2012. ( Connect ticket demandant l'ajout de cette information )

20
Joe Enos

Il semble qu’il n’existe pas de moyen simple et efficace de procéder ainsi, sans piratage ni changement de paradigme majeur. Il semble que le meilleur moyen consiste simplement à séparer les procs d'origine et à obtenir un proc de plus qu'avant:

Ancienne façon:

create procedure dbo.GetSomething
as
begin
    select * from dbo.Person;
    select * from dbo.Car;
end;

Nouvelle façon:

create procedure dbo.GetPeople
as
begin
    select * from dbo.Person;
end;

create procedure dbo.GetCars
as
begin
    select * from dbo.Car;
end;

-- This gives the same result as before
create procedure dbo.GetSomething
as
begin
    exec dbo.GetPeople;
    exec dbo.GetCars;
end;

Ensuite, lorsque je suis dans un processus différent et que j'ai besoin des deux jeux de résultats, je dois les appeler un à la fois.

2
Joe Enos
String myConnString  = "User ID="username";password="password";Initial Catalog=pubs;Data Source=Server";
SqlConnection myConnection = new SqlConnection(myConnString);
SqlCommand myCommand = new SqlCommand();
SqlDataReader myReader ;

myCommand.CommandType = CommandType.StoredProcedure;
myCommand.Connection = myConnection;
myCommand.CommandText = "MyProc";

try
{
    myConnection.Open();
    myReader = myCommand.ExecuteReader();

    while (myReader.Read())
    {
        //Write logic to process data for the first result.   
        }

    myReader.NextResult();
    while (myReader.Read())
    {
        //Write logic to process data for the second result.
    }
}
8
Mahesh Gaikwad

Au pays TSQL, vous êtes bloqué.

Voici une astuce (certains peuvent appeler semi-hacky) que j'ai utilisée une fois.

/*  START TSQL CODE */

/*  Stored Procedure Definition */

Use Northwind
GO


IF EXISTS 
    (
    SELECT * FROM INFORMATION_SCHEMA.ROUTINES
    WHERE ROUTINE_TYPE = N'PROCEDURE' and ROUTINE_SCHEMA = N'dbo' and ROUTINE_NAME = N'uspOrderDetailsByCustomerId'  
    )
BEGIN
    DROP PROCEDURE [dbo].[uspOrderDetailsByCustomerId]
END


GO

CREATE Procedure dbo.uspOrderDetailsByCustomerId
(
  @CustomerID nchar(5)
, @ResultSetIndicator smallint = 0
)
AS

BEGIN

    SET NOCOUNT ON



    /* ResultSet #1 */

    if (@ResultSetIndicator = 0 OR @ResultSetIndicator = 1)
    BEGIN 
        SELECT 
            c.CustomerID, c.CompanyName /*,c.ContactName,c.ContactTitle,c.[Address],c.City,c.Region,c.PostalCode,c.Country ,c.Phone,c.Fax */
        FROM 
            Customers c 
            JOIN Orders o ON c.CustomerID = o.CustomerID 
        WHERE 
            c.CustomerID = @CustomerID
    END


    /* */
    /* ResultSet #2 */ 

    if (@ResultSetIndicator = 0 OR @ResultSetIndicator = 2)
    BEGIN 

        SELECT o.OrderID,o.CustomerID /* ,o.EmployeeID,o.OrderDate,o.RequiredDate,o.ShippedDate,o.ShipVia ,o.Freight,o.ShipName,o.ShipAddress,o.OrderID,o.CustomerID,o.EmployeeID,o.OrderDate  */
        FROM 
            Orders o 
         WHERE 
            o.CustomerID = @CustomerID
        ORDER BY 
            o.CustomerID , o.OrderID 

    END


    /* */
    /* ResultSet #3 */

    if (@ResultSetIndicator = 0 OR @ResultSetIndicator = 3)
    BEGIN 
         SELECT od.OrderID,od.ProductID /* ,od.UnitPrice,od.Quantity,od.Discount  */
         FROM 
            [Order Details] od 
         WHERE 
            exists (select null from dbo.Orders  innerOrds where innerOrds.OrderID = od.OrderID and innerOrds.CustomerID = @CustomerID )
         ORDER BY 
            od.OrderID 

    END

    SET NOCOUNT OFF


END

GO 
/* Get everything */


exec dbo.uspOrderDetailsByCustomerId 'ALFKI'




    IF OBJECT_ID('tempdb..#TempCustomer') IS NOT NULL
    begin
            drop table #TempCustomer
    end


    CREATE TABLE #TempCustomer
    ( 
      [CustomerID] nchar(5)
    , [CompanyName] nvarchar(40)
    )

INSERT INTO #TempCustomer ( [CustomerID] , [CompanyName])
exec dbo.uspOrderDetailsByCustomerId 'ALFKI' , 1

Select * from #TempCustomer



    IF OBJECT_ID('tempdb..#TempOrders') IS NOT NULL
    begin
            drop table #TempOrders
    end


    CREATE TABLE #TempOrders
    ( 
        OrderID int
      , [CustomerID] nchar(5)

    )

INSERT INTO #TempOrders ( OrderID , [CustomerID] )
exec dbo.uspOrderDetailsByCustomerId 'ALFKI' , 2

Select * from #TempOrders






    IF OBJECT_ID('tempdb..#TempOrderDetails') IS NOT NULL
    begin
            drop table #TempOrderDetails
    end


    CREATE TABLE #TempOrderDetails
    ( 
        OrderID int
      , [ProductID] int

    )

INSERT INTO #TempOrderDetails ( OrderID , [ProductID] )
exec dbo.uspOrderDetailsByCustomerId 'ALFKI' , 3

Select * from #TempOrderDetails


    IF OBJECT_ID('tempdb..#TempOrderDetails') IS NOT NULL
    begin
            drop table #TempOrders
    end


    IF OBJECT_ID('tempdb..#TempOrders') IS NOT NULL
    begin
            drop table #TempOrders
    end



    IF OBJECT_ID('tempdb..#TempCustomer') IS NOT NULL
    begin
            drop table #TempCustomer
    end
4
granadaCoder

Bien que cela ne semble pas être pris en charge de manière native dans T-SQL, si l’utilisation d’une procédure stockée CLR est une option pour vous, vous devriez pouvoir créer une procédure stockée dans votre langage .Net préféré utilisant la méthode SqlDataReader.NextResult() le résultat souhaité, puis renvoyez le SqlDataReader via la méthode SqlPipe.Send(SqlDataReader). Il vous suffit de passer le code SQL à exécuter et le résultat souhaité défini en tant que paramètres de ce processus.

Cela vous permettrait de travailler avec le proc tel quel, sans le modifier pour renvoyer tout ou un seul jeu de résultats.

4
Solomon Rutzky

Vous pouvez mettre plusieurs résultats sous forme de fichier xml dans une table

Ainsi, lorsque vous souhaitez accéder à tous ces résultats, vous les analysez sous forme de tableau 

2
abdulla wasay

Der. Lire toute la question avant d'écrire une réponse! :-P

Si vous essayez d'utiliser les résultats dans TSQL Land, vous devrez utiliser un moyen de les séparer. Écrire les résultats dans des tables temporaires est peut-être votre meilleur choix, car vous n'aurez pas besoin de vous fier aux colonnes alignées (ou non, selon le cas) et pourrez traiter les données de manière "naturelle" pour SQL Server. Par exemple.

create proc test_something
as begin
    select a, b into temp1 from table1
    select b, c into temp2 from table2
end
go

exec dbo.test_something()

select * from temp1
select * from temp2
1
Steve G
Would passing a parameter to the sp do the trick
----------------------
CREATE PROCEDURE  dostuff @parm1 int
AS

BEGIN
Declare @resultset Int
Set @resultset = @parm1

--0 = Select ranks
--1 = Select suits
--other - Select ALL

If @resultset = 0 
 SELECT [rank] FROM [ranks]
 Else If @resultset = 1
 SELECT [suit] FROM [suits]
 Else 
 SELECT * FROM [suits]
 cross join   [ranks] 
END
GO

 declare @mytemptbl table (rank text)
 insert @mytemptbl
  exec dostuff 0

 select * from @mytemptbl
1
level3looper

Créez un SqlDataAdapter, définissez sa SelectCommand pour exécuter le SP "GetSomething", puis utilisez l'adaptateur de données pour remplir un DataSet. Le DataSet contiendra autant de DataTable que vous avez d'instructions "select" renvoyant des jeux d'enregistrements du SP.

Voici à quoi ressemblerait votre code:

System.Data.SqlClient.SqlDataAdapter da = new System.Data.SqlClient.SqlDataAdapter();
System.Data.SqlClient.SqlCommand cmd = new System.Data.SqlClient.SqlCommand();
cmd.Connection = myConnectionObject;
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "GetSomething";
da.SelectCommand = cmd;

System.Data.DataSet ds = new DataSet();
da.Fill(ds);
// at this point, the (ds) object contains DataTables created from the recordsets returned by the SP
DataTable dt0 = ds.Tables[0];
DataTable dt1 = ds.Tables[1];

// note that dt0 corresponds to the FIRST recordset returned by the SP, etc.
0
Olivier RASSI