Joe*_*nos 24 sql-server stored-procedures
给定SQL Server中有多个select语句的存储过程,有没有办法在调用过程时单独使用这些结果?
例如:
alter procedure dbo.GetSomething
as
begin
select * from dbo.Person;
select * from dbo.Car;
end;
Run Code Online (Sandbox Code Playgroud)
在.NET中,如果我调用此proc,我可以使用a SqlDataReader在两个结果集之间移动,这样我就可以轻松检索所有人和汽车.但是在SQL中,当我直接执行proc时,我得到两个结果集.
如果我打电话:
insert @myTempTable
exec dbo.GetSomething;
Run Code Online (Sandbox Code Playgroud)
然后它会出错,因为列定义不匹配.如果Person和Car有一些机会有相同的列,它将两者连接在一起,@ myTempTable从两个表中获取所有记录,这显然也不好.
我可以定义代表两个结果集的新自定义类型,并创建那些输出参数而不是多个select语句,但我想知道是否有更好的方法 - 将结果拉入临时表或循环结果的某种方式, 或者其他的东西.
编辑
实际上,在仔细观察之后,即使输出表参数也无法解决这个问题 - 它们只是读取,而且在SQL 2012中仍然如此.(连接票据要求添加此项)
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.
}
}
Run Code Online (Sandbox Code Playgroud)
在 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
Run Code Online (Sandbox Code Playgroud)
虽然 T-SQL 中似乎不支持此功能,但如果您可以选择使用 CLR 存储过程,那么您应该能够使用您首选的 .Net 语言创建一个存储过程,并使用该方法SqlDataReader.NextResult()前进到所需的结果集,然后通过该方法将 SqlDataReader 发送回SqlPipe.Send(SqlDataReader)。您只需将要执行的 SQL 和所需的结果集作为参数传递给该过程。
这将允许您按原样使用过程,而无需修改它以发送回所有或仅一个结果集。
如果没有黑客攻击或重大范式转变,似乎没有好的简单方法可以做到这一点。看起来最好的方法是把原来的 proc 拆分出来,最后得到一个比以前多一个的 proc:
旧方式:
create procedure dbo.GetSomething
as
begin
select * from dbo.Person;
select * from dbo.Car;
end;
Run Code Online (Sandbox Code Playgroud)
新方法:
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;
Run Code Online (Sandbox Code Playgroud)
然后当我在不同的过程中并且需要两个结果集时,我只需要一次调用它们一个。
| 归档时间: |
|
| 查看次数: |
78112 次 |
| 最近记录: |