简体   繁体   English

SQL Query用于获取具有子记录列表的父表的记录

[英]SQL Query to get records of parent table that have a list of child records

I have two tables in an MS SQL Server 2005 database, parent and child, where the parent may be related to many child records. 我在MS SQL Server 2005数据库(父和子)中有两个表,其中父表可能与许多子记录相关。 [Child.parent_id] is related to [parent.id]. [Child.parent_id]与[parent.id]相关。 The child table also has column [foo] I need to bring back all records in the parent table where [child.foo] matches on each of one to many parameters. 子表还有列[foo]我需要将父表中的所有记录带回来,其中[child.foo]匹配每个参数。 For example I would like all parent records that have a [child.foo] value of 'fizz' and a [child.foo] value of 'buzz.' 例如,我希望所有父记录的[child.foo]值为'fizz',[child.foo]值为'buzz'。 I have tried the below query, but it is returning records that match on only one. 我已经尝试了以下查询,但它返回的记录只匹配一个。

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')

This will return all Parent records which have [at least] one child with a 'fizz' foo AND [at least] one child with 'buzz' foo. 这将返回所有父记录,其中[至少]一个孩子有'fizz'foo和[至少]一个孩子有'buzz'foo。 Which is what I think is required in the question. 这是我认为在问题中需要的内容。

Also, while being potentially sub-optimal, this query is generic in a sense that it will work with most SQL implementation, not only the more modern ones, where the support of CTE, subqueries and related constructs are supported. 此外,虽然可能是次优的,但这个查询在某种意义上是通用的,它可以与大多数SQL实现一起使用,而不仅仅是支持CTE,子查询和相关结构的更现代的SQL实现。

   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'

Edit : 编辑
Now that Joel Potter has fixed the query in his answer, we probably agree that his approach has several advantages over the query listed above (please give him a few +reps). 现在Joel Potter在他的回答中修复了查询,我们可能同意他的方法比上面列出的查询有几个优点(请给他几个+代表)。 In particular: 尤其是:

  • the structure of the query doesn't change when we add, or remove, targeted values for the column foo. 当我们添加或删除列foo的目标值时,查询的结构不会更改。
  • the query is probably more easily optimized [by the server itself] 查询可能更容易优化[由服务器本身]
  • the structure of the query allows it to handle variations on the definition of the filter. 查询的结构允许它处理过滤器定义的变化。 For example we can query all Parents which have children such that say 2 of 5 possible values of foo are present. 例如,我们可以查询所有有孩子的父母,例如说foo的5个可能值中的2个存在。

Following is Joel's query here, slightly modified, to show how it can be expanded for more than 2 foo values. 以下是Joel的查询,略有修改,以显示如何扩展超过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 ...  

I believe you want something like this. 我相信你想要这样的东西。

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

Here's the test script: 这是测试脚本:

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

Returns only Id 1. 仅返回Id 1。

This should get the results you want: 这应该得到你想要的结果:

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')
)

I wanted to share this simple generalization of Joel's excellent answer. 我想分享这个简单概括的Joel的优秀答案。 The idea here is to be able to pass an arbitrary table of 'target' children into a procedure as either a table valued parameter or split delimited string. 这里的想法是能够将“目标”子项的任意表传递给过程,作为表值参数或拆分分隔字符串。 While this is nice, it would also be nice to have a similar query that matched using LIKE instead of IN. 虽然这很好,但使用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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM