分组来自两个领域的来回链

时间:2012-03-28 21:58:26

标签: c# .net sql linq group-by

我的设置如下:

CREATE TABLE MessageGrouping(MessageGroupingId bigint IDENTITY(1,1),
                             StoredMessageId bigint, MessageGroupId bigint)

INSERT INTO [MessageGrouping]([StoredMessageId], [MessageGroupId])
SELECT 1, 301 UNION ALL
SELECT 1, 302 UNION ALL
SELECT 2, 302 UNION ALL
SELECT 3, 303 UNION ALL
SELECT 4, 304 UNION ALL
SELECT 2, 305 UNION ALL
SELECT 6, 305 UNION ALL
SELECT 7, 303
GO

我需要这样来返回组(StoredMessageId s)这样的组:

[1,2,6], [3, 7], [4]
  

更新: 第一个分组的逻辑如下: StoredMessageId 1上有MessageGroupId 301和302。但是MessageGroupId 302也有StoredMessageId 2。因此StoredMessageId 2必须包含在该组中。

     

但是StoredMessageId 2也有MessageGroupId 305。并且MessageGroupId 305有StoredMessageId 6,所以它也包含在组中。

我在LinqPad尝试了这个并且空了。只有MessageGroupings.GroupBy(x=>x.MessageGroupId)的{​​{1}}组(正如预期的那样)。

添加MessageGroupId s(StoredMessageId只会将每一行放在自己的组中。

有没有办法在Linq中进行链式分组,就像我在找?我也可以在SQL中完成它。

我希望能够在像Dapper这样的微型ORM中使用它,但如果我需要实体框架,我也可以这样做。

1 个答案:

答案 0 :(得分:3)

好的 - 与我的想法相反,你可以使用一个递归CTE来做到这一点。
不是很漂亮......

请注意,所有测试都是在DB2上完成的,它没有办法在行中返回数组。我不知道SQL Server是否可以(因此您可能需要解析结果)。我 NOT 建议尝试使用Linq执行此操作 - 如果有的话,应该直接在数据库上运行。

WITH Mappings(beginId, storedId, groupId, storedPath, groupPath) as (
     SELECT a.StoredMessageId, a.StoredMessageId, a.MessageGroupId,
            a.StoredMessageId + "|", a.MessageGroupId + "|"
     FROM MessageGrouping as a
     WHERE NOT EXISTS (SELECT 1
                       FROM MessageGrouping as b
                       WHERE b.StoredMessageId < a.StoredMessageId
                       AND b.MessageGroupId = a.MessageGroupId)
     AND NOT EXISTS (SELECT 1
                     FROM MessageGrouping as b
                     WHERE b.MessageGroupId < a.MessageGroupId
                     AND b.StoredMessageId = a.StoredMessageId)
     UNION ALL
     SELECT a.beginId, a.storedId, b.MessageGroupId,
            a.storedPath, a.groupPath + b.MessageGroupId + "|"
     FROM Mappings as a
     JOIN MessageGrouping as b
     ON b.StoredMessageId = a.storedId
     AND b.MessageGroupId <> a.groupId
     AND a.groupPath NOT LIKE "%" + b.MessageGroupId + "%"
     UNION ALL
     SELECT a.beginId, a.StoredMessageId, a.groupId,
            a.storedPath + b.StoredMessageId + "|", a.groupPath
     FROM Mappings as a
     JOIN MessageGrouping as b
     ON b.MessageGroupId = a.groupId
     AND b.StoredMessageId <> a.storedId
     AND a.storedPath NOT LIKE "%" + b.StoredMessageId + "%"),

     Result_Rows (ids, num) as (
     SELECT storedPath, 
            ROW_NUMBER() OVER(PARTITION BY beginId
                              ORDER BY LENGTH(storedPath) DESC)
     FROM Mappings)
SELECT ids
FROM Result_Rows
WHERE num = 1

收益率ids

1|2|6|
3|7|  
4|