2

我正在使用SQL Server 12/Azure並有3個表(T1,T2,T3),其中T1與T2和T3有一對多,I想要從T2中選擇並返回T1記錄的信息及其相關的T3記錄。 舉一個簡單的例子,T1是「客戶」,T1是「訂單」,T3是「CustomerAddresses」,所以客戶可以有很多訂單和多個地址。現在,我想要查詢訂單幷包括顧客信息和地址,以使事情變得有點複雜,對訂單的查詢可以包括對顧客地址的匹配,例如,獲取這些地址的訂單。SQL Server - 從子 - 父 - 子選擇並返回多個結果集

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. 有沒有更好,更簡單,更優化的方法來實現我想要的?

-------- UPDATE --------------- 爲了澄清更多關於我如何在我的應用程序使用返回的數據:

  1. 我使用ADO.NET,SqlConnection,SqlCommand和SqlDataReader來解析結果。 (我不希望使用實體框架或任何其他高級DB這裏框架)

  2. 我的模型對象是T2(訂單)的集合,其中包含T1(客戶信息)和T3(CustomerAddresses)

OrderClass: 的OrderId,訂購日期,客戶ID,客戶名稱,CustomerAddresses [],...

CustomerAddresses類: 街,郵編,....

我發現人們通常會在一個表中返回一個冗餘數據的單個select語句中返回所有結果。我更喜歡的是返回僅包含相關信息的表格(T1,T2和T3),然後我可以在我的應用程序中處理它以創建模型。

另一種解決方案是從select語句的ID插入到一個臨時表,然後返回多個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) 
+0

我不是100%確定爲什麼你希望在單個事務中分離出2個從數據庫返回的相關結果集?我只能想象你的意圖是在以後重新使用所有的客戶地址?但是,根據您的需求獲取已經過濾的單個結果集可能會更高效,然後在運行時從邏輯上將該集分離爲您所需的實體模型。 – Geewers

+0

@Geewers如果有更好的方式,你可以向我解釋一個很好的答案,基本上我將使用所有這些信息,並將它們作爲多個結果 - 構造對象 - 在我的應用程序中。 –

+0

Sure @zaidsafadi,我的第一個評論是關於利用現有的概念,即使用模型層中的工廠模式來構建業務實體,以供技術堆棧上方的域對象使用。爲了提供更多細節,我需要更多地瞭解您的具體情況。例如,您是使用ADO.NET實體框架DBContexts還是ADO.NET SQLConnection和相關對象? – Geewers

回答

0

好的,首先......對你來說會更容易,建議用你的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()

此方法將數據讀取器推進到下一個結果集,如下所示僞代碼塊。

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將返回你的存儲過程進行把爲3個獨立的數據表來完成。 Here是文檔的鏈接。

希望這會有所幫助!

+0

謝謝@Geewers這個解決方案。實際上.NET的部分幾乎和我現在的一樣:)。至於SQL解決方案,我試過了,它肯定以我想要的方式返回數據,這太棒了!你知道這種方法是否比僅將主鍵插入臨時表/表值參數更有效,然後從原始表中選擇記錄? (我認爲你的解決方案更好,因爲臨時表中的行數遠小於單個選擇語句的原始表)。絕對也使用存儲過程;) –

+0

@zaidsafadi,過濾索引列始終是最好的方法 – Geewers

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個地址,則會有m×n個結果。

所以你也提到的方法是一個很好的方法。你所說的是從第一個查詢中獲取customer_id,並使用這些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

所有這些查詢都應該在一個事務中完成,並且您需要注意事務隔離級別,這會進一步使問題複雜化。

+0

我不是一個數據庫管理員,所以我認爲我的問題在一開始是常見的,並有一個直接的解決方案,顯然,從多個表查詢時存在真正的挑戰。感謝您提及表格值參數,我一定會考慮到這一點。 –