我正在开发一个应用程序,用户可以在其中将“组件”标记为工作流程的一部分。在许多情况下,它们最终会带有几个彼此同义的标签。他们希望将这些标签组合在一起,以便在将一个标签添加到组件时,也可以添加组中的其余标签。
我决定将标签组分解为组中每对标签之间的双向关系。因此,如果一个组具有标签 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 非常可怕。我不打算在这里粘贴它,但 LINQPad 显示它在我的测试数据库中生成了大约 12,000 行单独的 SELECT 语句(我们在 RelatedTags 中有 1562 个标签和 67 条记录)。
这些解决方案有效,但它们非常幼稚且效率低下。不过,我不知道还能去哪里。有任何想法吗?