给定简单的博客引擎,其中包含与帖子相关的帖子和标签。
数据库中有两个表:Post
和Tag
,还有PostTag
表,用于多对多关系。
我有标签列表,我想查找包含所有这些标签的所有帖子(因此.IsIn()
不在此处工作)
问题:如何使用nhibernate实现它? (理想情况下使用.QueryOver<>()
方法)
这里的问题是我甚至不知道从哪里开始以及如何在纯SQL中实现它。我有两个想法:
.IsSupersetOf()
函数)WHERE EXISTS
但我相信还有更优雅的方式
表格结构
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)
答案 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 (?, ?))