it-swarm.com.de

Rufen Sie Daten aus einer gespeicherten Prozedur ab, die über mehrere Ergebnissätze verfügt

Gibt es eine Möglichkeit, eine gespeicherte Prozedur in SQL Server mit mehreren select-Anweisungen zu verwenden, um diese Ergebnisse beim Aufruf der Prozedur separat zu bearbeiten?

Zum Beispiel:

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

Wenn ich diese Prozedur in .NET aufrufe, kann ich eine SqlDataReader verwenden, um zwischen den beiden Ergebnismengen zu wechseln, sodass ich problemlos alle Personen und Autos abrufen kann. In SQL bekomme ich jedoch beide Ergebnismengen, wenn ich die Prozedur direkt ausführte.

Wenn ich anrufe:

insert @myTempTable
    exec dbo.GetSomething;

Dann wird es fehlerhaft, da die Spaltendefinition nicht übereinstimmt. Wenn Person und Car zufällig die gleichen Spalten haben, werden die beiden miteinander verkettet, und @myTempTable erhält alle Datensätze aus beiden Tabellen, was offensichtlich nicht gut ist.

Ich kann neue benutzerdefinierte Typen definieren, die die beiden Ergebnismengen darstellen, und diese Ausgabeparameter definieren, anstatt die mehreren select-Anweisungen zu verwenden. Ich frage mich jedoch, ob es einen besseren Weg gibt - eine Möglichkeit, beide Ergebnisse in temporäre Tabellen zu ziehen oder durch die Ergebnisse oder etwas.

EDIT

Nach genauerem Hinsehen lösen sogar die Parameter der Ausgabetabelle das Problem nicht - sie sind schreibgeschützt, und das gilt auch in SQL 2012. ( Connect-Ticket mit der Bitte, dass dies hinzugefügt wird )

20
Joe Enos

Es scheint, dass es keinen einfachen Weg gibt, dies ohne einen Hack oder einen großen Paradigmenwechsel. Es sieht so aus, als wäre es am besten, die ursprünglichen Procs aufzuteilen und ein weiteres Proc zu erhalten, als zuvor:

Alter Weg:

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

Neuer Weg:

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;

Wenn ich dann in einem anderen Prozess bin und beide Ergebnissätze brauche, müsste ich sie nur einzeln aufrufen.

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

Im TSQL-Land stecken Sie fest.

Hier ist ein Trick, den ich einmal benutzt habe.

/*  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

Während dies in T-SQL scheinbar nicht nativ unterstützt wird, sollten Sie eine gespeicherte Prozedur in Ihrer bevorzugten .Net-Sprache erstellen können, die die SqlDataReader.NextResult()-Methode verwendet, um eine gespeicherte Prozedur zu erstellen die gewünschte Ergebnismenge und senden Sie den SqlDataReader dann über die SqlPipe.Send(SqlDataReader)-Methode zurück. Sie müssen lediglich die SQL-Anweisung zur Ausführung übergeben und das gewünschte Ergebnis als Parameter für diese Prozedur festlegen.

Auf diese Weise können Sie mit dem proc so arbeiten, wie er ist, ohne ihn zu ändern, um alle oder nur eine Ergebnismenge zurückzusenden.

4
Solomon Rutzky

Sie können mehrere Ergebnissätze in Form von XML in eine Tabelle einfügen

Wenn Sie also auf alle diese Ergebnisse zugreifen möchten, analysieren Sie diese Ergebnissatzspalte in eine tabellarische Form 

2
abdulla wasay

Der. Lesen Sie die ganze Frage, bevor Sie eine Antwort schreiben! :-P

Wenn Sie versuchen, mit den Ergebnissen in TSQL-Land zu arbeiten, müssen Sie eine Möglichkeit verwenden, um die Ergebnisse getrennt zu halten. Das Schreiben von Ergebnissen in Temp-Tabellen ist möglicherweise die beste Wahl, da Sie nicht auf Spalten (oder auch nicht) angewiesen sein müssen und die Daten für SQL Server auf "natürliche" Weise verarbeiten können. Z.B.

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

Erstellen Sie einen SqlDataAdapter, legen Sie für SelectCommand die Ausführung von SP "GetSomething" fest, und füllen Sie ein DataSet mit dem Datenadapter aus. Das DataSet enthält so viele DataTables, wie Sie Anweisungen zum "Auswählen" haben, die Recordsets vom SP zurückgeben.

So würde Ihr Code aussehen:

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