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 语句中包含我需要匹配的任何列。
- 是否有更好、更简单、更优化的方法来实现我想要的?
--------更新----------------
要详细说明我如何在我的应用程序中使用 returned 数据:
我正在使用 ADO.NET、SqlConnection、SqlCommand 和 SqlDataReader 来解析结果。 (我不想在这里使用 Entity Framework 或任何其他高级数据库框架)
我的模型对象是 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。
希望对您有所帮助!
我正在使用 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 语句中包含我需要匹配的任何列。
- 是否有更好、更简单、更优化的方法来实现我想要的?
--------更新---------------- 要详细说明我如何在我的应用程序中使用 returned 数据:
我正在使用 ADO.NET、SqlConnection、SqlCommand 和 SqlDataReader 来解析结果。 (我不想在这里使用 Entity Framework 或任何其他高级数据库框架)
我的模型对象是 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。
希望对您有所帮助!