2010-01-29 134 views
3

我有一個MS SQL Server 2005數據庫,父和子,其中父可能與許多子記錄相關的兩個表。 [Child.parent_id]與[parent.id]有關。子表還有列[foo],我需要將父表中的所有記錄帶回到[child.foo]匹配各個參數中的每一個。例如,我希望所有父級記錄的[child.foo]值爲'fizz',[child.foo]值爲'buzz'。 我已經嘗試了下面的查詢,但它只返回匹配的記錄。SQL查詢獲取具有子記錄列表的父表的記錄

SELECT  Parent.ID 
FROM   Parent 
INNER JOIN Child ON Parent.ID = Child.parent_id 
WHERE  (Child.foo = 'fizz') 
UNION ALL 
SELECT  Parent_1.ID 
FROM   Parent AS Parent_1 
INNER JOIN Child AS Child_1 ON Parent_1.ID = Child_1.parent_id 
WHERE  (Child_1.foo = 'buzz') 
+0

我已指定數據庫。我只是假定解決方案是數據庫不可知的,但我不需要它。 –

+0

遞歸CTE是ANSI標準,但實現滯後 - 只有3 dbs支持'WITH':Oracle,SQL Server和DB2。 –

回答

6

這將返回所有父記錄其[至少]一個孩子有'fizz'foo,[至少]一個孩子有'buzz'foo。我認爲這是問題所要求的。

此外,雖然可能是次優的,但這個查詢是通用的,它可以與大多數SQL實現一起工作,而不僅僅是支持CTE,子查詢和相關結構的更現代的查詢。

SELECT DISTINCT Parent.ID 
    FROM Parent 
    JOIN Child C1 ON Parent.ID = C1.parent_Id 
    JOIN Child C2 ON Parent.ID = C2.parent_id 
    WHERE C1.foo = 'fizz' 
     AND C2.foo = 'buzz' 

編輯
現在,喬爾·波特固定他的回答查詢,我們可能同意他的做法在上面列出的查詢有幾個優點(請給他幾+銷售代表)。特別是:

  • 結構查詢當我們添加不改變,或刪除,有針對性值列foo中。
  • 該查詢可能更容易優化[由服務器本身]
  • 該查詢的結構允許它處理過濾器定義的變化。例如,我們可以查詢所有有孩子的家長,這樣可以說5個可能的foo值中有2個存在。

以下是喬爾在這裏的查詢,稍作修改,以顯示它如何擴展超過2個foo值。

SELECT Parent.Id 
FROM Parent 
INNER JOIN Child on Parent.Id = child.parent_id 
WHERE Child.foo IN ('fizz', 'buzz') -- or say, ... IN ('fizz', 'buzz', 'bang', 'dong') 
GROUP BY Parent.Id 
HAVING COUNT(DISTINCT Child.foo) = 2 -- or 4 ... 
5

我相信你想要這樣的東西。

Select 
    Parent.Id 
From Parent 
    inner join Child on Parent.Id = child.parent_id 
Where 
    Child.foo = 'fizz' or Child.foo = 'buzz' 
Group By 
    Parent.Id 
Having 
    count(distinct Child.foo) > 1 

這裏的測試腳本:

Create Table #parent (id int) 
Create Table #child (parent_id int, foo varchar(32)) 

insert into #parent (id) values (1) 
insert into #parent (id) values (2) 
insert into #parent (id) values (3) 

insert into #child (parent_id, foo) values (1, 'buzz') 
insert into #child (parent_id, foo) values (2, 'buzz') 
insert into #child (parent_id, foo) values (3, 'buzz') 
insert into #child (parent_id, foo) values (1, 'fizz') 


Select 
    #parent.Id 
From #parent 
    inner join #child on #parent.id = #child.parent_id 
Where 
    #child.foo = 'fizz' or #child.foo = 'buzz' 
Group By 
    #parent.Id 
Having 
    count(distinct #child.foo) > 1   

drop table #parent 
drop table #child 

僅返回ID爲1

+0

如果一個給定的Parent有一個foo值爲'fizz'的兩個(或多個)孩子,這個查詢就會失敗 – mjv

+0

@mjv:你說的沒錯。將'count(*)'替換爲'count(distinct Child.foo)'來解決這個問題。 – Joel

+0

對。我編輯了我的答案,指出了你現在的固定解決方案,因爲我發現它更通用,更自動地優化。然而,對於這種查詢類型的查詢計劃很大程度上取決於具體的SQL實現和數據的特定統計概況。 – mjv

0

這應該得到你想要的結果:

SELECT p.ID 
FROM Parent p 
WHERE EXISTS 
(
    SELECT 1 FROM Child c WHERE c.parent_id = p.ID AND c.foo IN ('fizz','buzz') 
) 
+0

雖然這個問題很模糊,但我認爲OP會希望那些既有'嘶嘶'也有'嗡嗡'孩子的父母。上面的查詢將返回父母,其中有'嘶嘶聲'或''嗡嗡聲'的孩子。 – mjv

+0

啊,你可能是對的。因此,我正在投票你的答案。 –

0

我想分享一下這個簡單概括Joel的優秀答案。這裏的想法是能夠將任意'target'子表作爲表值參數或split分隔字符串傳遞給過程。雖然這很好,但也可以使用LIKE而不是IN來匹配類似的查詢。

--Parents whose children contain a subset of children 

--setup 
create table #parent (id int) 
create table #child (parent_id int, foo varchar(32)) 

insert into #parent (id) values (1) 
insert into #parent (id) values (2) 
insert into #parent (id) values (3) 

insert into #child (parent_id, foo) values (1, 'buzz') 
insert into #child (parent_id, foo) values (1, 'buzz') 
insert into #child (parent_id, foo) values (1, 'fizz') 
insert into #child (parent_id, foo) values (2, 'buzz') 
insert into #child (parent_id, foo) values (2, 'fizz') 
insert into #child (parent_id, foo) values (2, 'bang') 
insert into #child (parent_id, foo) values (3, 'buzz') 

--create in calling procedure 
declare @tblTargets table (strTarget varchar(10)) 
insert into @tblTargets (strTarget) values ('fizz') 
insert into @tblTargets (strTarget) values ('buzz') 

--select query to be called in procedure; 
-- pass @tblTargets in as TVP, or create from delimited string via splitter function 
select #parent.id  --returns 1 and 2 
    from #parent 
     inner join #child on #parent.id = #child.parent_id 
    where #child.foo in (select strTarget from @tblTargets) 
    group by #parent.id 
    having count(distinct #child.foo) = (select COUNT(*) from @tblTargets)   

--cleanup 
drop table #parent 
drop table #child