2013-03-21 58 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