查找子集合包含列表中所有元素的行

时间:2016-01-15 03:38:17

标签: c# linq nhibernate

给定简单的博客引擎,其中包含与帖子相关的帖子和​​标签。 数据库中有两个表:PostTag,还有PostTag表,用于多对多关系。

我有标签列表,我想查找包含所有这些标签的所有帖子(因此.IsIn()不在此处工作)

问题:如何使用nhibernate实现它? (理想情况下使用.QueryOver<>()方法)

这里的问题是我甚至不知道从哪里开始以及如何在纯SQL中实现它。我有两个想法:

  1. 获取所有帖子,然后使用LINQ(即使用.IsSupersetOf()函数)
  2. 过滤它们
  3. 在SQL中,对列表中的每个项目使用WHERE EXISTS
  4. 但我相信还有更优雅的方式

    表格结构

        CREATE TABLE Post (
            Id INT PRIMARY KEY, 
            Title NVARCHAR(255) NOT NULL
        );
    
        CREATE TABLE Tag (
            Id INT PRIMARY KEY, 
            Tag NVARCHAR(50) NOT NULL
        );
    
        CREATE TABLE PostTag (
            PostId INT NOT NULL REFERENCES Post(Id),
            TagId INT NOT NULL REFERENCES Tag(Id)
        );
    
        INSERT INTO Post(Id, Title) VALUES (1, 'Post A');
        INSERT INTO Post(Id, Title) VALUES (2, 'Post B');
        INSERT INTO Post(Id, Title) VALUES (3, 'Post C');
    
        INSERT INTO Tag(Id, Tag) VALUES (1, 'tagA');
        INSERT INTO Tag(Id, Tag) VALUES (2, 'tagB');
    
        INSERT INTO PostTag (PostId, TagId) VALUES (1, 1);
        INSERT INTO PostTag (PostId, TagId) VALUES (2, 2);
        INSERT INTO PostTag (PostId, TagId) VALUES (3, 1);
        INSERT INTO PostTag (PostId, TagId) VALUES (3, 2);
    

    我希望通过给定的标签ID列表获得id 3的帖子:(1,2)

2 个答案:

答案 0 :(得分:2)

LINQ解决方案(nhibernate 应该能够翻译它)

var tags = new[] { 1 , 2 };

var postIds = PostTags
    .Where(pt => tags.Contains(pt.TagId))
    .GroupBy(pt => pt.PostId)
    .Where(g => g.Count() == tags.Length)
    .Select(g => g.Key);

SQL解决方案:

SELECT PostId
FROM (
    SELECT COUNT(*) AS count, PostId
    FROM [PostTag]
    WHERE TagId IN (1, 2) --List of tags
    GROUP BY PostId
    ) as t1
WHERE [t1].[count] = 2 --Length of list

说明:我们过滤PostTag仅包含我们关注的标记。然后我们按邮件分组。如果分组的计数等于标记列表的长度,则帖子包含所有标记。

答案 1 :(得分:2)

使用Queryover解决方案应该如下,

Tag tagAlias = new Tag();
Post postAlias = new Post();

Tag tagAliasInner = new Tag();
Post postAliasInner = new Post();

var subQuery = QueryOver.Of(() => postAliasInner)
    .JoinAlias(() => postAliasInner.Tags, () => tagAliasInner)
    .Where(Restrictions.EqProperty(Projections.Property(() => postAliasInner.Id),
        Projections.Property(() => postAlias.Id)))
    .Where(Restrictions.In(Projections.Property(() => tagAliasInner.Id), ids.ToArray()))
    .Select(Projections.Count(Projections.Property(() => tagAliasInner.Id)));

var query = session.QueryOver(() => postAlias)
    .JoinAlias(() => postAlias.Tags, () => tagAlias)
    .Where(Restrictions.In(Projections.Property(() => tagAlias.Id), ids.ToArray()))
    .WithSubquery.WhereValue(ids.Count).Eq<Post>(subQuery);

var results = query.List();

这导致SQL,

SELECT this_.Id as Id3_1_,
 this_.Title as Title3_1_,
 tags3_.Post_id as Post1_,
 tagalias1_.Id as Tag2_,
 tagalias1_.Id as Id5_0_,
 tagalias1_.Text as Text5_0_ 
FROM "Post" this_
 inner join PostTag tags3_ on this_.Id=tags3_.Post_id 
 inner join "Tag" tagalias1_ on tags3_.Tag_id=tagalias1_.Id 
WHERE tagalias1_.Id in (?, ?) 
 and ? = (SELECT count(tagaliasin1_.Id) as y0_ 
            FROM "Post" this_0_
            inner join PostTag tags3_ on this_0_.Id=tags3_.Post_id 
            inner join "Tag" tagaliasin1_ on tags3_.Tag_id=tagaliasin1_.Id 
            WHERE this_0_.Id = this_.Id and tagaliasin1_.Id in (?, ?))