我正在开发一个应用程序,用户可以将“组件”标记为工作流程的一部分。在许多情况下,他们最终会得到几个彼此同义的标签。他们希望将这些组合在一起,以便当一个标签添加到组件时,组中的其余标签也可以添加。
我决定将标签组分解为组中每对标签之间的双向关系。因此,如果一个组有标签1和2,则会有一条如下所示的记录:
ID TagID RelatedTagID
1 1 2
2 2 1
基本上,一个组被表示为其中每个标签的笛卡尔积。将其扩展为3个标签:
ID Name
1 MM
2 Managed Maintenance
3 MSP
我们的关系如下:
ID TagID RelatedTagID
1 1 2
2 2 1
3 1 3
4 3 1
5 2 3
6 3 2
我有几种方法可以将它们组合在一起,但它们不是很好。首先,我编写了一个视图,列出了每个标记以及其组中的标记列表:
SELECT
TagKey AS ID,
STUFF
((SELECT ',' + cast(RelatedTagKey AS nvarchar)
FROM RelatedTags rt
WHERE rt.TagKey = t.TagKey
FOR XML PATH('')), 1, 1, '') AS RelatedTagKeys
FROM (
SELECT DISTINCT TagKey
FROM RelatedTags
) t
这个问题是每个组在结果中出现的次数与其中的标记一样多,我无法想到在单个查询中解决的方法。所以它让我回来了:
ID RelatedTagKeys
1 2,3
2 1,3
3 1,2
然后在我的后端,我丢弃包含在另一个组中发生的密钥的所有组。标签没有添加到多个组中,因此可行,但我不喜欢我正在拉下多少无关数据。
我想出的第二个解决方案就是这个LINQ查询。用于对标签进行分组的密钥是组本身的列表。这可能比我原先想象的要糟糕得多。
from t in Tags.ToList()
where t.RelatedTags.Any()
group t by
string.Join(",", (new List<int> { t.ID })
.Concat(t.RelatedTags.Select(i => i.Tag.ID))
.OrderBy(i => i))
into g
select g.ToList()
我真的很讨厌通过调用string.Join
的结果进行分组,但是当我尝试按键列表进行分组时,它没有正确分组,将每个标记放在一个组中。此外,它生成的SQL是 monstrous 。我不会在这里粘贴它,但是LINQPad显示它在我的测试数据库上生成大约12,000行单独的SELECT语句(我们在RelatedTags中有1562个标记和67个记录)。
这些解决方案有效,但它们非常幼稚且效率低下。不过,我不知道还有什么地方可以做到这一点。有什么想法吗?
答案 0 :(得分:1)
如果您的每个代码都有groupId
,我认为使用您的数据变得更容易,因此相关的代码会共享相同的groupId
值。
为了解释我的意思,我在数据集中添加了第二组相关标签:
INSERT INTO tags ([ID], [Name]) VALUES
(1, 'MM'),
(2, 'Managed Maintenance'),
(3, 'MSP'),
(4, 'UM'),
(5, 'Unmanaged Maintenance');
和
INSERT INTO relatedTags ([ID], [TagID], [RelatedTagID]) VALUES
(1, 1, 2),
(2, 2, 1),
(3, 1, 3),
(4, 3, 1),
(5, 2, 3),
(6, 3, 2),
(7, 4, 5),
(8, 5, 4);
然后,一个包含以下信息的表应该会使很多其他事情变得更容易(我首先解释表的内容,然后如何使用查询来获取它):
tagId | groupId
------|--------
1 | 1
2 | 1
3 | 1
4 | 4
5 | 4
该数据包括两组相关标签,即{1,2,3}
和{4,5}
。因此,上表标记的属于同一组的标记具有相同的groupId
,即1
为{1,2,3}
,4
为{4,5}
。
要实现这样的视图/表,您可以使用以下查询:
with rt as
( (select r2.tagId, r2.relatedTagId
from relatedTags r1 join relatedTags r2 on r1.tagId = r2.relatedTagId)
union
(select r3.tagId, r3.tagId as relatedTagId from relatedTags r3)
)
select rt.tagId, min(rt.relatedTagId) as groupId from rt
group by tagId
当然,您还可以使用tags
属性扩展主groupId
- 表,而不是引入新的表/视图。
希望这有帮助。
答案 1 :(得分:0)
我真的不明白这种关系。你没解释得很好。但我得到了相同的结果。不确定我是否做对了。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication41
{
class Program
{
static void Main(string[] args)
{
Data.data = new List<Data>() {
new Data() { ID = 1, TagID = 1, RelatedTagID = 2},
new Data() { ID = 2, TagID = 2, RelatedTagID = 1},
new Data() { ID = 3, TagID = 1, RelatedTagID = 3},
new Data() { ID = 4, TagID = 3, RelatedTagID = 1},
new Data() { ID = 5, TagID = 2, RelatedTagID = 3},
new Data() { ID = 6, TagID = 3, RelatedTagID = 2}
};
var results = Data.data.GroupBy(x => x.RelatedTagID)
.OrderBy(x => x.Key)
.Select(x => new {
ID = x.Key,
RelatedTagKeys = x.Select(y => y.TagID).ToList()
}).ToList();
foreach (var result in results)
{
Console.WriteLine("ID = '{0}', RelatedTagKeys = '{1}'", result.ID, string.Join(",",result.RelatedTagKeys.Select(x => x.ToString())));
}
Console.ReadLine();
}
}
public class Data
{
public static List<Data> data { get; set; }
public int ID { get; set; }
public int TagID { get; set; }
public int RelatedTagID { get; set; }
}
}