分组按所有子记录

时间:2013-05-15 21:47:00

标签: sql sql-server group-by sql-server-2012

如果我有以下数据,其中一条规则可以有多个条件:

-------------------
RuleId   CriteriaId
-------------------
1        1
1        2
1        3
2        1
2        2
2        3
3        1
3        2

如何在对规则的所有条件进行分组时获得最小 RuleId。换句话说,由于规则1和规则2具有完全相同的标准,因此它们将位于一个组中,但由于规则3不具有相同的标准,因此它将位于不同的组中。

我希望得到以下结果:

-------------------
RuleId   CriteriaId
-------------------
1        1
1        2
1        3
3        1
3        2

在RuleId上使用MIN进行直接的GROUP BY在这里不起作用,因为这样可以反过来:

-------------------
RuleId   CriteriaId
-------------------
1        1
1        2
1        3

感谢您的帮助。

4 个答案:

答案 0 :(得分:1)

示例数据

create table rules (RuleId int, CriteriaId int);
insert into rules values
(1 ,1),
(1 ,2),
(1 ,3),
(2 ,1),
(2 ,2),
(2 ,3),
(3 ,1),
(3 ,2);

您的查询

;with flattened as (
        select r.ruleid, (select ',' + rtrim(r2.criteriaid)
                            from rules r2
                           where r2.RuleId = r.RuleId
                        order by r2.criteriaid
                             for xml path(''), type).value('/','varchar(max)') list
        from rules r
    group by r.ruleid
)
  select r3.*
    from rules r3
    join (
         select min(ruleid) min_ruleid
           from flattened
       group by list) r4 on r4.min_ruleid = r3.ruleid
order by r3.ruleid, r3.CriteriaId;

答案 1 :(得分:1)

我不确定这是绝对最好的方法,但它有效。

CREATE TABLE GroupingTest (RuleId int, CriteriaId int)
INSERT INTO GroupingTest VALUES 
    (1, 1),
    (1, 2),
    (1, 3),
    (2, 1),
    (2, 2),
    (2, 3),
    (3, 1),
    (3, 2)

----------------------------------------------------
WITH MergedGroupingCriteria AS (
    SELECT DISTINCT RuleId, 
        STUFF((SELECT ', ' + CAST(CriteriaId AS varchar)
                FROM GroupingTest GT
                WHERE GT.RuleId = MergeGroup.RuleId
                FOR XML PATH(''),TYPE).value('.','VARCHAR(MAX)')
            , 1, 2, '') AS MergedGrouping
    FROM GroupingTest MergeGroup )
SELECT MIN(GroupingTest.RuleId), GroupingTest.CriteriaId
FROM GroupingTest
JOIN MergedGroupingCriteria
    ON GroupingTest.RuleId = MergedGroupingCriteria.RuleId
GROUP BY MergedGroupingCriteria.MergedGrouping, GroupingTest.CriteriaId
ORDER BY MIN(GroupingTest.RuleId), GroupingTest.CriteriaId

答案 2 :(得分:1)

我的使用SQL Server's LISTAGG/GROUP_CONCAT emulation

SELECT MIN(list.ruleid) minrule, rules
FROM 
(SELECT RuleId,
stuff((
    SELECT ', ' + CriteriaId
    FROM Table1 as t1
    where t1.RuleId = t2.RuleId
    FOR XML PATH('')
    ), 1, 2, '') Rules
FROM Table1 as t2
GROUP BY RuleId) list
GROUP BY Rules
ORDER BY minrule;

这是 SQL Fiddle

我假设两个字段都为varchar,我的结果仍然连接在一起。

修改

以下查询没有假设,并提供了正确的结果格式:

SELECT * 
FROM Table1
WHERE RuleId IN

(SELECT MIN(list.ruleid) minrule
FROM 
(SELECT RuleId,
stuff((
    SELECT ', ' + CAST(CriteriaId AS varchar)
    FROM Table1 as t1
    where t1.RuleId = t2.RuleId
    FOR XML PATH('')
    ), 1, 2, '') Rules
FROM Table1 as t2
GROUP BY RuleId) list
GROUP BY Rules)

ORDER BY RuleId
;

更新了 SQL Fiddle

答案 3 :(得分:0)

这是一种方法。首先,创建一个具有完全匹配条件的所有规则对的列表。如果每条规则上的数字相同,则标准匹配。并且,当我们计算匹配的数量时,匹配的数量与总数相同。

以下查询找到这些对:

select driver.ruleid1, driver.ruleid2
from (select rc1.ruleid as ruleid1, rc2.ruleid as ruleid2,
             rc1.numCriteria as Num1, rc2.numCriteria as Num2
      from (select ruleid, COUNT(*) as numCriteria from rc group by ruleid) rc1 join
           (select ruleid, COUNT(*) as numCriteria from rc group by ruleid) rc2
           on rc1.ruleid <= rc2.ruleid and
              rc1.numCriteria = rc2.numCriteria
     ) driver left outer join
     rc rc1
     on driver.ruleid1 = rc1.ruleid left outer join
     rc rc2
     on rc2.ruleid = driver.ruleid2 and
        rc1.criteriaId = rc2.criteriaid
group by driver.ruleid1, driver.ruleid2
having max(driver.Num1) = COUNT(distinct rc1.ruleid) and
       MAX(driver.Num1) = COUNT(distinct rc2.ruleId)

driver子查询执行第一次传递,使所有规则对具有相同数量的条件。然后加入标准。我意识到这创造了一个小笛卡尔积。但是,要计算条件数,查询将使用count(distinct)

要分配唯一的组ID,只需使用rule1的最小值即可。像这样:

with pairs as (
      <above subquery>
     )
select ruleid2, min(ruleid1) as groupnum
from pairs
group by ruleid2