2013-03-21 61 views
0

我有一个有趣的问题,将表分成一组。我有一群游客 - 每个人说一种语言和/或是家庭的一部分。 我需要将表格分组,但我想将家庭和类似的语言说话者放在一起。分区表,但基于多列组合在一起

假设我想将游客分成最多3人的团体(如果一个团体必须更大,那是可以接受的)。该解决方案不必非常聪明,以至于完全填补所有组织,但我正在尽力而为。

输入:

TouristID | LanguageID | FamilyID 
--------------------------------- 
    1  |  1  | 1 
    2  |  1  | 1 
    3  |  1  | 1 
    4  |  2  | 1 
    5  |  3  | 2 
    6  |  4  | 2 
    7  |  5  | 3 
    8  |  5  | 4 
    9  |  7  | 5 

期望的结果:

TouristID | GroupID 
------------------- 
    1  | 1 
    2  | 1 
    3  | 1 
    4  | 1 
    5  | 2 
    6  | 2 
    7  | 3 
    8  | 3 
    9  | 2 

组1是由所有语言1个扬声器,包括不能被排除在一个家族成员形成。

组2由两个家庭成员形成(5,6)和一个随机构件(9),以使该组的3

组3由两个相同的语言扬声器(7,8形成)

我所做的

INSERT TouristGroup 
SELECT 
    t.TouristID, 
    DENSE_RANK() OVER (ORDER BY GroupID) AS [GroupID] 
FROM Tourists t 
CROSS APPLY (
    SELECT MIN(TouristID) AS [GroupID] 
    FROM Tourists t2 
    WHERE 
    (t2.LanguageID = t.LanguageID 
    OR t2.FamilyID = t.FamilyID) 
) x; 

INSERT Groups 
SELECT GroupID, COUNT(*) 
FROM TouristGroup 
GROUP BY GroupID; 

declare 
    @matchID int = 0, 
    @currentCount int, 
    @desiredCount int = 0, 
    @candidateGroupID int = null, 
    @chunk int = 1 

while exists (
    select null 
    from Groups g 
    left join Matches m 
    on m.GroupID = g.GroupID 
    where m.GroupID is null 
) 
begin 
    set @currentCount = null 
    set @candidateGroupID = null 

    select 
    @currentCount = isnull(SUM([Count]), 0) 
    from Matches m 
    join Groups g 
    on g.GroupID = m.GroupID 
    where m.MatchID = @matchID 

    if @CurrentCount is not null 
    begin 
    set @desiredCount = @chunk - @desiredCount 

    select top 1 
     @candidateGroupID = g.GroupID 
    from Groups g 
    left join Matches m 
     on m.GroupID = g.GroupID 
    where g.[Count] <= @desiredCount 
     and m.GroupID is null 
    order by [Count] DESC 

    if @candidateGroupID is not null 
    begin 
     insert Matches 
     select @matchID, @candidateGroupID 
    end 
    else begin 
     set @matchID = @matchID + 1 
    end 
    end 
    else begin 
    set @matchid = @matchID + 1 
    end 
end   

问题

是否有更好的方法来分区表,但基于多列将行分组在一起?

+0

你说的是实际的[table partitioning](http://msdn.microsoft.com/en-us/library/ms190787.aspx)?或者为结果集分组数据? – supergrady 2013-03-21 02:39:00

+0

分组。在我上面创建的例子中,我需要将游客分成几组,但我想让家人和类似语言的人聚在一起。对于那些不符合3人小组的人,他们被合并。即游客9与5和6结合 – 2013-03-21 05:30:56

+0

什么版本的SQL Server? – 2013-03-21 19:50:54

回答

1

这将产生你的“第1步”。也许它比现在好(没有循环)。

SELECT t.TouristID, DENSE_RANK() OVER (ORDER BY x.GroupNum) as GroupId 
FROM Tourists t 
CROSS APPLY (SELECT MIN(TouristId) AS GroupNum 
      FROM @Tourist t2 
      WHERE t2.LanguageId = t.LanguageId OR t2.FamilyId = t.FamilyId 
      ) x 

至于至少获得至少三个组的成员,如果可能的话,你可能需要做类似于你在做什么,一个循环的其他需求(我不知道它是否能得到改善,因为你没有分享)。

[更新]下面是我为“步骤2”的建议:

DECLARE @MinGroupSize int = 3, @rc int = 1 
WHILE @rc>0 
BEGIN 
    WITH GroupCount AS (
    SELECT GroupID, COUNT(*) AS GroupCount 
    FROM TouristGroup 
    GROUP BY GroupID 
    ), CandidateGroups AS (
    SELECT TOP 1 gc1.GroupID AS ShortGroupId, singleton.GroupID as SingletonGroupID 
    FROM GroupCount gc1 
    CROSS APPLY (SELECT TOP 1 GroupID 
       FROM GroupCount AS gc2 
       WHERE gc2.GroupCount = 1 AND gc2.GroupID != gc1.GroupID 
       ORDER BY gc2.GroupID 
       ) AS singleton 
    WHERE gc1.GroupCount < @MinGroupSize 
    ORDER BY GroupCount DESC, gc1.GroupID ASC 
    ) 
    UPDATE tg 
    SET GroupID = cg.ShortGroupID 
    FROM TouristGroup tg 
    JOIN CandidateGroups cg ON cg.SingletonGroupID = tg.GroupID; 
    SET @rc = @@ROWCOUNT; 
END 
-- 
-- If you're anal like me and want to eliminate gaps in GroupID values 
-- 
UPDATE tg 
SET GroupID = tg2.GroupID 
FROM TouristGroup tg 
JOIN (SELECT TouristID, DENSE_RANK() OVER (ORDER BY GroupID) AS [GroupID] 
     FROM TouristGroup) AS tg2 ON tg2.TouristID = tg.TouristID 
WHERE tg.GroupID != tg2.GroupID; 

这将找到比所需最低组尺寸更小的组,并找到一个单组(仅1个成员),并更新与该单其他GroupID,一个接一个地做,直到没有候选人。按顺序选择较小的组(按GroupCount降序,然后按GroupID升序),以便首先填充较大的组。只选择单身人士进行更新,以免自然群体被分解。

+0

我将我的解决方案发布到“第2步”。感谢“第1步” - 这是完美的。 – 2013-03-22 07:20:47

+0

我已将我的解决方案添加到“第2步”。 – GilM 2013-03-22 17:39:34