2011-11-05 77 views
4

我用父數據顯示一個網格,如果存在相關的子行,則需要顯示圖標。 我的數據庫在SQL Server 2008中。讓我簡化了,我有以下兩個表 -簡單的SQL來檢查父項是否有任何子行

訂單(PK:ID)

文件(PK:寫到FileID,FK:訂單)

Order可以有零或更多與它相關的文件。 File表有一列OrderID,其中包含FK參考Order。現在,我正在顯示一個網格,其中列出了所有Orders,並且我想要顯示一個圖標圖像,指示Order是否有任何子行(文件)。

這裏有一個取巧的辦法我已經試驗,但不知道它是如何有效的/可擴展性 -

SELECT DISTINCT o.ID, o.OrderNum, ... 
,(CASE f.ID/f.ID WHEN 1 THEN 1 ELSE 0 END) as FilesExist 
... 
FROM Order AS o LEFT OUTER JOIN dbo.FileHeader as f ON f.OrderID = o.ID 

CASE聲明似乎需要很好地工作。如果存在一個或多個文件,則返回1,否則爲0.如果存在多個文件行,則它將嘗試重複已添加了DISTINCT的Order行,並且我不選擇f.ID,而是選擇f.ID/f.ID其中將爲1(它存在)和0爲空(不存在)。我已經瞭解到JOIN優於內聯SELECT COUNT(*)聲明。

我已經測試過,它能正常工作,但我需要專家意見,並且需要確保這是做這件事的最佳方式。這是一個非常簡單的例子,但我的SELECT語句很複雜,因爲有很多查找,並且它將成爲一個昂貴的獲取,所以我需要確保它的可擴展性。

謝謝。

EDIT#1: 總括 - 要麼將是內部SELECT用COUNT(*)

SELECT c.ClaimNo,(SELECT COUNT(*) FROM dbo.FileHeader AS f WHERE f.ClaimID = c.ID) AS FilesHExist 
FROM dbo.Claim AS c 

Internal Select

LEFT OUTER JOIN方法我已經提到過。

SELECT DISTINCT c.ClaimNo, (CASE fh.ID/fh.ID WHEN 1 THEN 1 ELSE 0 END) AS FilesHExist    
FROM dbo.Claim AS c LEFT OUTER JOIN dbo.FileHeader AS fh ON fh.ClaimID = c.ID 

My JOIN approach

連接的查詢執行計劃的兩個圖像。請建議哪一個更好。

+1

你正在使用什麼類型的SQL?另外它似乎是一個左外連接到'文件'會產生'NULL'值,這是由''dbo.FileHeader'用'ISNULL()'來處理?還有,你的意思是'f.ID/o.ID'?顯然'f.ID/f.ID'會*總是* == 1? – Gibron

+0

我的數據庫在SQL Server 2008中。我的想法是當JOIN產生一個NULL值時有0。所以,如果它存在,f.ID/f.ID將始終爲1,如果它爲NULL,則始終爲0。這就是CASE聲明所暗示的 - 它不需要包含任何中間SELECT *或COUNT語句即可達到目的。 –

+0

請標記答案是,如果你有答案 –

回答

12

爲了選擇幾排,這應該是最快:

SELECT o.ID 
     ,o.OrderNum 
     ,CASE WHEN EXISTS (SELECT * FROM dbo.FileHeader f WHERE f.OrderID = o.ID) 
      THEN 1 
      ELSE 0 
     END AS FilesHExist 
FROM Order o; 

對於SELECT包括大部分dbo.FileHeader這應該執行更好

SELECT o.ID 
     ,o.OrderNum 
     ,CASE WHEN f.OrderID IS NULL THEN 0 ELSE 1 END AS FilesHExist 
FROM Order o 
LEFT JOIN 
     (SELECT OrderID FROM dbo.FileHeader GROUP BY OrderID) f ON f.OrderID = o.ID 

OrderID先組合後再左連接應該更便宜。最後不需要DISTINCT。

決不使用:

(CASE f.ID/f.ID WHEN 1 THEN 1 ELSE 0 END) 

它通過零異常可能讓你來劃分的。用上面演示的NULL的支票替換它。

+0

不會執行 - 你有兩個連續的逗號','在那裏... – Yahia

+0

@Yahia謝謝,修正。 –

+0

附加查詢執行計劃。請讓我知道你的最終裁決。 –

0

嘗試此

select A.Id OrderId ,case when isnull(B.FileCounts,0)>0 then 1 else 0 end FilesExist from [Order] A left join (select OrderId, COUNT(1) FileCounts from [File] group by OrderId)B on A.Id=B.OrderId 
1

如果你可以用子女使用

SELECT o.ID 
     ,o.OrderNum 
     ,(SELECT COUNT(*) FROM dbo.FileHeader f WHERE f.OrderID = o.ID) AS FilesCount 
FROM Order o; 

數量的工作,否則使用

SELECT o.ID 
     ,o.OrderNum 
     ,CASE WHEN (SELECT COUNT(*) FROM dbo.FileHeader f WHERE f.OrderID = o.ID) > 0 THEN 1 ELSE 0 END AS FilesExist 
FROM Order o; 

雖然推薦:

每當你想知道到底發生了什麼的DB比較不同版本查詢的PLAN - 一切都充其量只是一個受過教育的猜測...... PLAN向你展示了你真正的w生病了(例如,在回答你的問題時,需要回收多少行,以及其他我們什麼都沒有的東西)。

假設您正在使用SQL Server,這在SSMS中可用...同樣適用於Oracle - 它在SQL Developer中可用...大多數DB都有這樣的選項。

+0

感謝您的建議,我會這樣做,但在目前我的數據庫記錄太少。無論如何,前段時間我從SO學到,JOIN的中間SELECT語句比較便宜。我試圖用簡單的JOIN完成它,並避免GROUP BY。最後我用DISTINCT來獲取獨特的記錄。請建議。 –

+0

@HemantTank DISTINCT通常在DB內部實現爲GROUP BY ... GROUP BY本身並不壞,而「中間SELECT」有點醜陋,但仍然不應該放棄......只是看到你在SQL 2008上 - 檢查不同查詢的執行計劃,即使只有一小組記錄,PLAN概述了Db在內部使用的不同路徑,這些路徑反過來提供了有用的提示。 – Yahia

+0

附加查詢執行計劃。請讓我知道你的最終裁決。 –

相關問題