将 table 双向关系分成不同的组
Dividing up a table of two-way relationships into distinct groups
我正在开发一个应用程序,用户可以在其中标记 "components" 作为工作流程的一部分。在许多情况下,他们最终会得到几个互为同义词的标签。他们希望将这些组合在一起,以便在将一个标签添加到组件时,也可以添加该组中的其余标签。
我决定将标签组分解为组中每对标签之间的双向关系。因此,如果一个组具有标签 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 条记录)。
这些解决方案有效,但它们非常幼稚且效率低下。不过,我不知道还能去哪里。有什么想法吗?
我真的不明白其中的关系。你解释的不是很好。但是我以某种方式得到了相同的结果。不确定我是否做对了。
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; }
}
}
如果每个标签都有一个 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);
然后,一个 table 持有以下信息应该会使很多其他事情变得更容易(我首先解释 table 的内容,然后如何使用查询获取它):
tagId | groupId
------|--------
1 | 1
2 | 1
3 | 1
4 | 4
5 | 4
数据包含两组相关标签,即{1,2,3}
和{4,5}
。因此,上面的 table 标记属于同一组的标签具有相同的 groupId
,即 1
代表 {1,2,3}
,4
代表 {4,5}
。
要实现这样的 view/table,您可以使用以下查询:
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
当然,除了引入新的 table / 视图,您还可以通过 groupId
属性扩展您的主要 tags
-table。
希望这对您有所帮助。
我正在开发一个应用程序,用户可以在其中标记 "components" 作为工作流程的一部分。在许多情况下,他们最终会得到几个互为同义词的标签。他们希望将这些组合在一起,以便在将一个标签添加到组件时,也可以添加该组中的其余标签。
我决定将标签组分解为组中每对标签之间的双向关系。因此,如果一个组具有标签 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 条记录)。
这些解决方案有效,但它们非常幼稚且效率低下。不过,我不知道还能去哪里。有什么想法吗?
我真的不明白其中的关系。你解释的不是很好。但是我以某种方式得到了相同的结果。不确定我是否做对了。
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; }
}
}
如果每个标签都有一个 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);
然后,一个 table 持有以下信息应该会使很多其他事情变得更容易(我首先解释 table 的内容,然后如何使用查询获取它):
tagId | groupId
------|--------
1 | 1
2 | 1
3 | 1
4 | 4
5 | 4
数据包含两组相关标签,即{1,2,3}
和{4,5}
。因此,上面的 table 标记属于同一组的标签具有相同的 groupId
,即 1
代表 {1,2,3}
,4
代表 {4,5}
。
要实现这样的 view/table,您可以使用以下查询:
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
当然,除了引入新的 table / 视图,您还可以通过 groupId
属性扩展您的主要 tags
-table。
希望这对您有所帮助。