2013-11-19 3 views
19

Учитывая хранимую процедуру на SQL Server с несколькими операторами select, существует ли способ работать с этими результатами отдельно при вызове процедуры?Извлечь данные из хранимой процедуры, которая имеет несколько наборов результатов

Например:

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

В .NET, если я называю этот процедурный, я могу использовать SqlDataReader для перемещения между двумя наборами результатов, так что я могу легко получить все люди и автомобили. Однако в SQL, когда я выполняю proc напрямую, я получаю оба набора результатов.

Если я называю:

insert @myTempTable 
    exec dbo.GetSomething; 

Тогда это ошибка, так как определение столбца не совпадает. Если каким-то образом Person и Car имеют одинаковые столбцы, он объединяет их вместе, а @myTempTable получает все записи из обеих таблиц, что, очевидно, тоже не хорошо.

Я могу определить новые пользовательские типы, представляющие два набора результатов, и сделать эти выходные параметры вместо нескольких операторов select, но мне интересно, есть ли лучший способ - каким-то образом вывести оба результата во временные таблицы, или зацикливание результатов, или что-то еще.

EDIT

На самом деле, после того, присмотревшись, даже выходные параметры таблицы не решить эту проблему - они только для чтения, и это все еще верно в SQL 2012. (Connect ticket asking for this to be added)

+2

No. Если вы хотите работать с одним набором результатов из одной процедуры, тогда вы можете возвращать только один результирующий набор. – Kermit

+0

Удивительно, но вы * можете * получить доступ к множеству наборов результатов из приложения ADO.NET с помощью 'SqlDataReader', как вы упомянули, но вы ** не можете ** получить доступ к множеству наборов результатов от T-SQL ..... мне никогда не было смысла , но так оно и есть (по крайней мере на данный момент). –

+0

Скажите, что у вас есть столбец с именем «type» в таблице «Car and Person», новая таблица не может иметь 2 столбца с именем «type». Возможно, вы явно указали имена столбцов на 1 из выбранных, например. SELECT тип AS carType из автомобиля. – NoChance

ответ

2

Кажется, что нет простого простого способа сделать это без взлома или серьезного сдвига парадигмы.Похоже, что лучший способ это просто отщепляются оригинальные проки и в конечном итоге с еще одним прока, чем раньше:

Старый путь:

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

Новый способ:

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; 

Тогда, когда Я нахожусь в другом proc и нуждаюсь в обоих наборах результатов, я просто должен был бы назвать их по одному.

4

В TSQL землю, ты застрял.

Вот трюк (некоторые могут назвать полуулыбчатым) способом, который я использовал один раз.

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

Это интересная идея, спасибо. Немного хакерский, как вы упоминаете, но похоже, что это сработает. –

+0

Я использовал его один раз, на некотором коде, который я унаследовал. Я не рекомендую его в качестве лучшей практики. Но это работает. – granadaCoder

+0

А ... Я вижу, что вы там делали. Определенно поможет мне выйти из трудного места, где мне нужен только доступ к одному результирующему набору из специального места. – Jaans

1

Der. Прочтите весь вопрос, прежде чем писать ответ! :-P

Если вы пытаетесь работать с результатами на земле TSQL, вам нужно будет каким-то образом сохранить результаты отдельно. Написание результатов для таблиц Temp - это, пожалуй, лучший выбор, поскольку вам не нужно будет зависеть от выравнивания столбцов (или нет, в зависимости от ситуации) и может обрабатывать данные «естественным» способом для SQL Server. Например.

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

Это не сработает, если temp1 и temp2 не созданы за пределами proc, до вызова proc. Временные таблицы, созданные в подпроцессе, удаляются при возврате в вызывающий процесс. –

+0

Это работает для меня в SQL Server 2008. Нет необходимости создавать temp1 и temp2 вне proc, так как они не являются фактически temp-таблицами, это настоящие таблицы.Конечно, теперь вам нужно очистить после себя ... И, как всегда, YMMV. :) –

3

Хотя это не по всей видимости, изначально поддерживаются в T-SQL, при использовании CLR хранимой процедуры является вариантом для вас, то вы должны быть в состоянии создать хранимую процедуру в предпочитаемом .Net языке использует метод SqlDataReader.NextResult() для перехода к желаемому набору результатов, а затем отправьте SqlDataReader обратно с помощью метода SqlPipe.Send(SqlDataReader). Вам просто нужно передать в SQL для выполнения и желаемый результат, заданный в качестве параметров для этого proc.

Это позволит вам работать с proc как есть, не изменяя его для отправки всего или только одного результирующего набора.

+1

Спасибо - не огромный поклонник CLR procs только потому, что им больно работать, но это звучит как интересный подход. –

7
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. 
    } 
} 
+0

myReader.NextResult(); // возвращает данные второго результата –

2

Вы можете поместить несколько результирующий набор в виде XML в таблицу

Так что, когда вы хотите получить доступ ко всем этим результатом разбора тех, столбец результирующего набора в табличной форме

Смежные вопросы