SQL Server - 从child-parent-child中选择并返回多个结果集

时间:2015-06-28 05:30:18

标签: sql-server ado.net relational-database azure-sql-database

我正在使用SQL Server 12 / Azure并且有3个表(T1,T2,T3),其中T1有1个多用T2和T3,我想从T2中选择并返回T1记录及其相关T3的信息记录。 举一个简单的例子,T1是"客户",T1是"订单",T3是" CustomerAddresses",因此客户可以有许多订单和多个地址。现在我想查询订单并包括客户信息和地址,使事情有点复杂,订单查询可能包括匹配客户地址,例如获取这些地址的订单。

Customer Table                   
----------------------          
Id, Name,...                    
----------------------          

Orders Table                            
------------------------------          
OrderId, CustomerKey, Date,...          
------------------------------          

CustomerAddresses
-----------------------------------------------
AutoNumber, CustomerKey, Street, ZipCode,...
-----------------------------------------------

我无法编写最佳方法(优化)以在一个事务中返回所有结果并动态生成sql语句,这就是我认为结果应该回来的方式:

在一个结果集/表中返回订单(T2)和客户信息(T1),并在另一个结果集/表中返回CustomerAddresses(T2)。我使用ADO.NET生成并执行查询,并使用System.Data.SqlClient.SqlDataReader循环返回结果。

结果如何回归的示例:

Order-Customer Table
-------------------------------
Order.OrderId, Customer.Id, Customer.Name, Order.Date,....
-------------------------------

CustomerAddresses
-------------------------------
AutoNumber, CustomerKey, Street
-------------------------------

这是我当前生成的查询示例:

SELECT [Order].[OrderId], [Order].[Date], [Customer].[Id], [Customer].[Name] 
FROM Order 
INNER JOIN [Customer] on [Order].[CustomerKey] = [Customer].[Id] 
WHERE ([Order].[Date] > '2015-06-28') 

问题: 1.如何扩展上述查询以允许在单独的结果集/表中返回CustomerAddresses? 要在CustomerAddresses上启用匹配,我应该能够与Customer表进行连接,并在WHERE语句中包含我需要匹配的任何列。

  1. 是否有更好,更简单,更优化的方式来实现我想要的目标?
  2. -------- UPDATE --------------- 要详细说明我如何在我的应用中使用返回的数据:

    1. 我正在使用ADO.NET,SqlConnection,SqlCommand和SqlDataReader来解析结果。 (我不想在这里使用Entity Framework或任何其他高级DB Framework)

    2. 我的模型对象是T2(订单)的集合,其中包含T1(客户信息)和T3(客户地址)

    3. OrderClass: OrderId,OrderDate,CustomerId,CustomerName,CustomerAddresses [],...

      CustomerAddresses类: Street,ZipCode,....

      我发现人们通常会在一个表中的一个select语句中返回所有结果,这会返回一个冗余数据。我更喜欢按原样返回表格(T1,T2和T3),只包含相关信息,然后我可以在我的应用程序中处理它以创建模型。

      另一个解决方案是将select语句中的ID插入Temp表中,然后在多个select语句中返回结果:

      Select T1.* From T1 where  Id in (
              select Temp.T1Id from Temp )
      Select T2.* From T2 where  Id in (
              select Temp.T2Id from Temp )
      Select T3.* From T3 where  Id in (
              select Temp.T3Id from Temp )
      

2 个答案:

答案 0 :(得分:1)

这是一个很好的问题,是一个在其他地方显然没有得到很好解决的常见问题。正如您所提到的,问题在于,由于每个客户可能有许多订单和地址,因此单个查询中的结果数量可能很大。例如,

select * from customer
left outer join order 
on (order.customer_id = customer.customer_id)
left outer join customer_orders co 
on (co.customer_id = customer.customer_id)

将生成您需要的信息,但会返回许多结果。例如,如果每个客户有n个订单,每个客户有m个地址,则会有mxn结果。

因此,您也提到的方法是一种很好的方法。你所说的是从第一个查询中获取customer_ids,并使用这些id来“生成”订单查询和地址查询。

基本上你需要发出的问题是:

select * from customer
where ....

检索客户信息。然后

select * from order 
where customer_id in [The customer_ids found in the above query]

select * from customer_address 
where customer_id in [The customer_ids found in the above query]

您可以按照建议使用临时表,但是表值参数会更有效。由于您使用的是SQL Server 12,因此可以使用表值参数。有关详细信息,请参阅以下链接:http://www.sommarskog.se/arrays-in-sql-2008.html

所有这些查询都应该在一个事务中完成,您需要注意事务隔离级别,这会使问题进一步复杂化。

答案 1 :(得分:0)

好的,首先关闭......对您来说会更容易,并建议您使用SQL逻辑创建存储过程     ' [DBO] [GetOrderDetails]'

原因是为了防止陷入SQL注入的受害者,并防止重新编译和重新分发您的程序集,直到您的查询更新。

以下是一些解释伪代码来指导您:

public class GpsService extends Service {
    private BroadcastReceiver mGpsReceiver;
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
       super.onStartCommand(intent, flags, startId);
       registerReceiver();
       return Service.START_NOT_STICKY;
    }
    private void registerReceiver() {
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.FROYO) {
            IntentFilter mIntentFilter = new IntentFilter();
            mIntentFilter.addAction(LocationManager.PROVIDERS_CHANGED_ACTION);
            this.mGpsReceiver = new GpsReceiver();
            this.registerReceiver(this.mGpsReceiver, mIntentFilter);
        }       
    }
}

然后从解释的环境转移到托管(编译)代码,您需要查看:

  

SqlDataReader的:NextResult()

此方法将数据读取器推进到下一个结果集,如下面的伪代码块所示。

CREATE PROCEDURE [dbo].[OrderDetails] 
(
    @OrderDate DateTime,
    @AddressFilter VARCHAR(128)
)

AS

BEGIN

    IF(OBJECT_ID('tempdb..#OrderDetails') IS NOT NULL) DROP TABLE #OrderDetails

    SELECT orders.[*ListYourOrdersColumnsHere*], customer.[*ListYourCustomerColumnsHere*], addresses.[*ListYourAddressColumnsHere*]
    INTO #OrderDetails
    FROM [Order] orders
        INNER JOIN [Customer] customer on orders.[CustomerKey] = customer.[Id] 
        LEFT JOIN [CustomerAddresses] addresses ON addresses.[CustomerKey] = customer.[CustomerKey]
        -- Or inner join if you need orders just with CustomerAddresses
        --INNER JOIN [CustomerAddresses] addresses ON addresses.[CustomerKey] = customer.[CustomerKey]
    WHERE orders.[Date] > @OrderDate
    AND addresses.StreetName LIKE '%' + @AddressFilter + '%'

    -- Orders
    SELECT [*ListYourOrdersColumnsHere*]
    FROM #OrderDetails
    GROUP BY [*ListYourOrdersColumnsHere*]

    -- Customers
    SELECT [*ListYourCustomerColumnsHere*]
    FROM #OrderDetails
    GROUP BY [*ListYourCustomerColumnsHere*]

    -- Addresses
    SELECT [*ListYourAddressColumnsHere*]
    FROM #OrderDetails
    GROUP BY [*ListYourAddressColumnsHere*]

    DROP TABLE #OrderDetails
END

最后,这也可以使用SqlDataAdapter来完成,它将把存储过程作为3个单独的DataTable返回。 Here是文档的链接。

希望这有帮助!