集体父母与同一个孩子

时间:2016-07-29 08:02:15

标签: sql sql-server-2005 aggregate

编辑:这更难解释我,但是,根据评论不断编辑。感谢大家的兴趣。

我有一张这样的表

ID  Type                    ParentID
1   ChildTypeA              1
2   ChildTypeB              1
3   ChildTypeC              1
4   ChildTypeD              1

5   ChildTypeA              2 
6   ChildTypeB              2
7   ChildTypeC              2

8   ChildTypeA              3 
9   ChildTypeB              3
10  ChildTypeC              3
11  ChildTypeD              3

12  ChildTypeA              4
13  ChildTypeB              4
14  ChildTypeC              4

我希望对有相同孩子的父母进行分组 - 这意味着同一类型的孩子数量相同。

从父母的角度来看,有一组有限的可能配置(最多10个)。

如果任何父级具有相同的子集(通过ChildType),我想将它们组合在一起(在我所谓的配置中)。

ChildTypeA-D = ConfigA
ChildTypeA-C = ConfigB
ChildTypeA, B, E, F = ConfigX
etc. 

我需要的输出是按配置分组的父项。

Config Group    ParentID
ConfigA         1   
ConfigA         3   
ConfigB         2
ConfigB         4

我不知道从哪里开始。

2 个答案:

答案 0 :(得分:1)

我把你的桌子命名为t。如果这是你想要的,请尝试。 它的表现与之匹配且无与伦比。 它正在寻找具有相同行数(t1.cnt = t2.cnt)并且所有行都匹配(having COUNT(*) = t1.cnt)的parentids。 你可以尝试here

    ;with t1 as (select parentid, type, id, count(*) over (partition by parentid order by parentid) cnt from t),
t3 as 
     (
        select t1.parentid parentid1, t2.parentid parentid2, count(*) cn, t1.cnt cnt1, t2.cnt cnt2, ROW_NUMBER () over (order by t1.parentid) rn
          from t1 join t1 as t2 on t1.type = t2.type and t1.parentid <> t2.parentid and t1.cnt = t2.cnt
      group by t1.parentid, t2.parentid, t1.cnt, t2.cnt
      having COUNT(*) = t1.cnt
      ),
notFound as (
          select t1.parentid, ROW_NUMBER() over(order by t1.parentid) rn
           from t1
          where not exists (select 1 from t3 where t1.parentid = t3.parentid1)
          group by t1.parentid
          )
 select 'Config'+char((select min(rn)+64 from t3 as t4 where t3.parentid1 in (t4.parentid1 , t4.parentid2))) config, t3.parentid1
   from t3
 union all
  select 'Config'+char((select max(rn)+64+notFound.rn from t3)) config, notFound.parentid
   from notFound

<强>输出

config  parentid1
ConfigA 1
ConfigA 3
ConfigB 2
ConfigB 4

如果id 14是ChildTypeZ,那么parentid 2和4将不匹配。这将是输出:

config  parentid1
ConfigA 1
ConfigA 3
ConfigC 2
ConfigD 4

答案 1 :(得分:0)

我碰巧有类似的任务。我正在使用的数据规模有点大,所以我必须找到一种有效的方法。基本上我找到了两种工作方法。

一个是纯SQL - 这是一个核心查询。基本上,它为您提供了具有相同子集的最小ParentID,然后您可以将其用作组ID(您也可以使用row_number枚举它)。作为一个小注释 - 我在这里使用cte,但在现实世界中,我建议将分组父项放入临时表并在表中添加索引。

;with cte_parents as (
    -- You can also use different statistics to narrow the search
    select
        [ParentID],
        count(*) as cnt,
        min([Type]) as min_Type,
        max([Type]) as max_Type
    from Table1
    group by
        [ParentID]
)
select
    h1.ParentID,
    k.ParentID as GroupID
from cte_parents as h1
    outer apply (
        select top 1
            h2.[ParentID]
        from cte_parents as h2
        where
            h2.cnt = h1.cnt and
            h2.min_Type = h1.min_Type and
            h2.max_Type = h1.max_Type and
            not exists (
                select *
                from (select tt.[Type] from Table1 as tt where tt.[ParentID] = h2.[ParentID]) as tt1
                    full join (select tt.[Type] from Table1 as tt where tt.[ParentID] = h1.[ParentID]) as tt2 on
                        tt2.[Type] = tt1.[Type]
                where
                    tt1.[Type] is null or tt2.[Type] is null
            )
        order by
            h2.[ParentID]
    ) as k

ParentID    GroupID
----------- --------------
1           1
2           2
3           1
4           2

另一个有点棘手,使用它时必须小心。但令人惊讶的是,它并没有那么糟糕。这个想法是将子连接成大字符串然后按这些字符串分组。您可以使用任何可用的串联方法(如果您有SQL Server 2017,则使用xml技巧或clr)。重要的是你必须使用有序连接,所以每个字符串都会精确地代表你的组。我为此创建了一个特殊的CLR函数(dbo.f_ConcatAsc)。

;with cte1 as (
    select
        ParentID,
        dbo.f_ConcatAsc([Type], ',') as group_data
    from Table1
    group by
        ParentID
), cte2 as (
    select
        dbo.f_ConcatAsc(ParentID, ',') as parent_data,
        group_data,
        row_number() over(order by group_data) as rn
    from cte1
    group by
        group_data
)
select
    cast(p.value as int) as ParentID,
    c.rn as GroupID,
    c.group_data
from cte2 as c
    cross apply string_split(c.parent_data, ',') as p

ParentID    GroupID              group_data
----------- -------------------- --------------------------------------------------
2           1                    ChildTypeA,ChildTypeB,ChildTypeC
4           1                    ChildTypeA,ChildTypeB,ChildTypeC
1           2                    ChildTypeA,ChildTypeB,ChildTypeC,ChildTypeD
3           2                    ChildTypeA,ChildTypeB,ChildTypeC,ChildTypeD