SQL 服务器 - Select 来自子-父-子和 return 多个结果集

SQL Server - Select from child-parent-child and return multiple results-set

我正在使用 SQL Server 12/Azure 并且有 3 个 tables(T1、T2、T3),其中 T1 与 T2 和 T3 有一对多,我想 select 来自 T2 和 return T1 记录及其关联的 T3 记录的信息。 举个简单的例子,T1是"Customer",T1是"Orders",T3是"CustomerAddresses",所以一个客户可以有很多个订单,多个地址。现在我想查询订单并包括客户信息和地址,使事情变得有点复杂,订单查询可能包括匹配客户地址,例如获取这些地址的订单。

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

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

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

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

订单 (T2) 和客户信息 (T1) 在一个 result-set/table 中 return 编辑,而客户地址 (T2) 在另一个 result-set/table 中 return 编辑。我正在使用 ADO.NET 生成和执行查询,并使用 System.Data.SqlClient.SqlDataReader 循环 returned 结果。

返回结果的示例:

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. 如何扩展上述查询以允许 return 在单独的 result-set/table 中访问 CustomerAddresses? 要在 CustomerAddresses 上启用匹配,我应该能够与 Customer table 进行连接,并在 WHERE 语句中包含我需要匹配的任何列。

  1. 是否有更好、更简单、更优化的方法来实现我想要的?

--------更新---------------- 要详细说明我如何在我的应用程序中使用 returned 数据:

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

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

订单Class: OrderId、OrderDate、CustomerId、CustomerName、CustomerAddresses[]、...

客户地址 Class: 街道,邮政编码,....

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

另一种解决方案是将 select 语句中的 ID 插入临时 table,然后 return 多个 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 )

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

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)

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

所以你提到的方法是一个很好的方法。您所说的是从第一个查询中获取 customer_ids 并将这些 ID 用于 "generate" 订单查询和地址查询。

基本上您需要做的是发出如下查询:

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]

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

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

好的,首先......这对你来说会更容易,建议使用你的 SQL 逻辑创建一个存储过程,例如 '[dbo].[GetOrderDetails]'.

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

这里有一些经过解释的伪代码可以指导您:

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

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

SqlDataReader:NextResult()

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

SqlConnection myConnection = new SqlConnection("*YourAzureDbConnectionString*");
SqlCommand myCommand = new SqlCommand();
SqlDataReader myReader;

myCommand.CommandType = CommandType.StoredProcedure;
myCommand.Connection = myConnection;
myCommand.CommandText = "[dbo].[OrderDetails]";
myCommand.Parameters.Add(new SqlParameter("@OrderDate", DateTime.Now);
myCommand.Parameters.Add(new SqlParameter("@AddressFilter", "Some or other address filter"));

int resultSetCount = 0;

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

    do
    {
        resultSetCount++;
        while (myReader.Read())
        {
            switch (resultSetCount)
            {
                case 1:
                    // Populate Order information from 1st resultset
                    break;
                case 2:
                    // Populate Customer customer information from 2nd resultset
                    break;
                case 3:
                    // Populate CustomerAddress information from 3rd resultset
                    break;
            }                        
        }
    }
    while (myReader.NextResult());
}
catch (Exception ex)
{
    // handle exception logic
}
finally
{
    myConnection.Close();
}

最后,这也可以使用 SqlDataAdapter 完成,它将 return 您的存储过程输出为 3 个单独的数据表。 Here 是文档的 link。

希望对您有所帮助!