2009-06-18 111 views
3

SQL:我有一個SQL查詢(MSSQLSERVER),我使用子查詢列添加到結果集選擇的子查詢

SELECT P.name, 
(select count(*) from cars C where C.type = 'sports') AS sportscars, 
(select count(*) from cars C where C.type = 'family') AS familycars, 
(select count(*) from cars C where C.type = 'business') AS businesscars 
FROM people P 
WHERE P.id = 1; 

查詢以上僅僅是從測試設置,這是一個有點廢話,但它以我想象中的例子爲例。我實際上正在處理的查詢跨越了許多複雜的表格,這些表格只會分散眼前的問題。

在上面的例子中,表格「people」中的每條記錄還有三列:「wantsSportscar」,「wantsFamilycar」和「wantsBusinesscar」。現在我想要做的只是如果people表中的各個「want .....」字段設置爲「true」,則只需執行每個附加列的子選擇。換句話說,如果P.wantsSportscar對特定人員設置爲true,我只想執行第一個子查詢。第二和第三個子查詢應該以類似的方式工作。

所以這個查詢應該工作的方式是它顯示了一個特定的人的名字以及他想要擁有的汽車類型可用的模型數量。值得注意的是,我的最終結果集總是隻包含一個記錄,即一個特定用戶的記錄。

重要的是,如果某人對某種類型的汽車不感興趣,則該類型的列將不會包含在最終結果集中。一個例子,以確保這是明確的:

如果人A想要一輛跑車和一輛家庭轎車,結果將包括列「名稱」,「跑車」和「家庭轎車」。

如果B人想要一輛商務車,結果將包括「名稱」和「businesscar」列。

我一直在嘗試使用IF,CASE和EXISTS語句的各種組合,但到目前爲止我還沒有能夠得到一個語法正確的解決方案。有誰知道這是否可能?請注意,該查詢將存儲在一個存儲過程中。

回答

5

在你的情況下,有8列布局可能和做到這一點,你將需要8單獨的查詢(或動態構建您的查詢)。

無法在單個查詢中更改結果集佈局。

相反,你可以按照如下設計你的查詢:

SELECT P.name, 
     CASE WHEN wantssport = 1 THEN (select count(*) from cars C where C.type = 'sports') ELSE NULL END AS sportscars, 
     CASE WHEN wantsfamily = 1 THEN (select count(*) from cars C where C.type = 'family') ELSE NULL END AS familycars, 
     CASE WHEN wantsbusiness = 1 THEN (select count(*) from cars C where C.type = 'business') ELSE NULL END AS businesscars 
FROM people P 
WHERE P.id = 1 

將在適當的列中選擇NULL如果一個人不想要它,並在客戶端解析這些NULL的。

請注意,關係模型根據relations回答查詢。

在你的情況下,關係如下:「這個人的需求對於這麼多的跑車,這麼多的商務車以及這麼多的家用轎車都很滿意。

關係模型總是以四元關係回答這個具體問題。

它不會忽略任何關係成員:相反,它只是將它們設置爲NULL這就是SQL的方式來表明關係的成員沒有定義,適用或有意義。

+0

我想這解決了我的問題(見我的評論回覆ChrisCM的答案)。此解決方案將允許我檢查編程代碼中不同列的值,並採取適當的措施。這對我來說足夠了。謝謝。 – Jerry 2009-06-18 12:17:17

0

我大部分都是Oracle的人,但同樣適用的可能性很大。除非我誤解了,否則你想要的東西在這個層次上是不可能的 - 你總會有一個固定的列數。您的查詢可以控制列是否爲空,但由於在查詢的最外面部分中指定了X列,因此您可以保證在結果集中獲得X列。

正如我所說,我不熟悉MS SQL Server,但我猜會有一些執行動態SQL的方法,在這種情況下,您應該研究一下,因爲它應該允許您構建更靈活的查詢。

+0

好吧,列的靜態數字聽起來很合理。這讓我想到了,因爲列可以存在,但是如果相關的「want ....」列設置爲false,我可以在結果集列中放置一個特定值。 是否有可能將列留在那裏,但根據「want ....」字段中的值將其值設置爲-1或類似的值? – Jerry 2009-06-18 12:11:04

0

您可以通過首先將值選爲單獨的行選擇到臨時表中,然後在該表上執行PIVOT(將行轉換爲列)來完成您想要的操作。

0

重要的是,如果一個人沒有 感興趣的某一類車, ,對於這種類型的列將不 被包含在最終的結果集。一個 的例子,以確保這是明確的:

你將無法在普通的SQL中做到這一點。我建議你只是將這個列設置爲NULL或ZERO。

如果您希望查詢在添加新車時動態展開,那麼PIVOTing可以幫助您。

0

爲了使這項工作變得簡單,您想學習三個基本原則。第一個是數據規範化,第二個是GROUP BY,第三個是PIVOT。

首先,數據標準化。人們的表格設計不是第一種正常形式。 「wantports」,「wantfamily」,「wantbusiness」這些專欄實際上是一個重複的小組,雖然他們看起來不像一個。如果您可以修改表格設計,您會發現創建第三個表格的好處是,可以稱其爲「peoplewant」,並帶有兩個關鍵列,personid和cartype。如果你願意,我可以詳細說明爲什麼這個設計更靈活和更強大,但我現在要跳過這個。

在GROUP BY上。這使您可以生成一個結果,該結果彙總了結果的一行中的每個組。

SELECT 
    p.name, 
    c.type, 
    c.count(*) as carcount 
FROM people p, 
    INNER JOIN peoplewant pw ON p.id = pw.personid 
    INNER JOIN cars c on pw.cartype = c.type 
WHERE 
    p.id = 1 
GROUP BY 
    p.name, 
    c.type 

這(未經測試)查詢給你你想要的結果,但結果對人要各車種單獨一行。

最後,PIVOT。您的DBMS中的PIVOT工具允許您將此結果轉換爲只有一行的表單,並且該人員想要的每個cartype都有一個單獨的列。我自己並沒有使用PIVOT,所以我會讓其他人編輯這個響應來提供一個使用PIVOT的例子。

如果您使用相同的技術在一次掃描中檢索多個人的數據,請記住,對於每個想要的類型都會顯示一列,任何人都想要的列將出現在PIVOT結果中,想要一個在結果列中的汽車類型。

0

剛剛通過谷歌搜索發現這篇文章,所以我意識到我遲到了這個派對了一點,但是..確定這真的是可能做...但是,我不會建議實際上是這樣做的,因爲它通常被認爲是一件非常糟糕的事情(tm)。

動態SQL是你的答案。

在我說如何做到這一點之前,我想說一句,動態SQL是一個非常危險的東西,如果你沒有從應用程序中消毒你的輸入。

所以,因此,謹慎行事:

declare @sqlToExecute nvarchar(max); 
declare @includeSportsCars bit; 
declare @includeFamilyCars bit; 
declare @includeBusinessCars bit; 

set @includeBusinessCars = 1 
set @includeFamilyCars = 1 
set @includeSportsCars = 1 

set @sqlToExecute = 'SELECT P.name ' 

if @includeSportsCars = 1 
    set @sqlToExecute = @sqlToExecute + '(select count(*) from cars C where C.type = ''sports'') AS sportscars, '; 
if @includeFamilyCars = 1 
    set @sqlToExecute = @sqlToExecute + '(select count(*) from cars C where C.type = ''family'') AS familycars, '; 
if @includeBusinessCars = 1 
    set @sqlToExecute = @sqlToExecute + '(select count(*) from cars C where C.type = ''business'') AS businesscars ' 

set @sqlToExecute = @sqlToExecute + ' FROM people P WHERE P.id = 1;'; 

exec(@sqlToExecute)