选择所有标签的所有帖子

时间:2014-02-13 23:12:28

标签: c# sql .net sql-server dapper

关于此问题有很多问题,但没有足够明确的答案,尤其是在我遇到的情况下使用SQL Server。

我有3个表来表示与之关联的博客帖子和标签。我想运行一个查询,它将获得所有帖子和每个帖子标签 - 我需要标签ID和名称。结果需要很容易地序列化到我的C#类中。我也在尝试使用Dapper.net,但这不是最重要的部分。

显然很多网站都是这样做的,我想知道最好的方式以及如何在现实世界中完成它?我可以获得所有帖子,然后为每个帖子运行多个查询,这将返回每个帖子的标签。但肯定有更好的方法吗?

如果我只是试图获得一个帖子,那么我只会写多个选择。一个用于帖子,一个用于标签。但我想要所有的帖子。有没有办法在不复制返回的每个标记行的帖子信息的情况下执行此操作?

如果每个帖子的标签被序列化为一个以逗号分隔的列,例如,如何才能获得id和名称?编码字符串?

SELECT * FROM dbo.Posts    
SELECT * FROM dbo.Tags
SELECT * FROM dbo.PostTags

Posts
Id  Title       Content
===============================
1   First Post  First Content
3   Second      Second Content

Tags
Id  Name
============
1   C#
2   SQL
3   IIS
4   Steam
5   OpenID

PostTags
PostId  TagId
=============
1       1
1       2
3       3
3       4

只需使用以下查询加入表格:

SELECT p.*, t.Name
FROM dbo.Posts p
LEFT JOIN dbo.PostTags pt ON p.id = pt.PostId
LEFT JOIN dbo.Tags t ON t.id = pt.TagId

通过重复与其关联的每个标记的帖子内容,提供了大量冗余数据:

Id  Title       Content         Name
======================================
1   First Post  First Post      C#
1   First Post  First Post      SQL
3   Second Post Second Content  IIS
3   Second Post Second Content  Steam

3 个答案:

答案 0 :(得分:1)

这纯粹是一种练习,让我先说一下,重复的数据很可能并不是什么大问题。虽然如果帖子的大小非常大并且有很多帖子,但它确实开始更有意义,以避免重复。

此外,使用C#Linq-to-Sql或Entity Framework,将为您制定对象关系,您的Post实体将具有您可以访问的List<Tag>属性。

然而,如果你想要推出自己的类型的东西,一个只涉及一次数据包往返而没有数据重复的选项就是编写一个存储过程,它会让你回到2个记录集(2个独立的选择语句) - 一个带有发布内容,以及标记内容。

创建一个代表Post的C#类并且只有一个List<Tag>并从存储的proc结果中提取它会非常简单。

Create Procedure GetPostTags
As

-- We will use the GotTags column here to loop through and get tabs later
Declare @Posts Table (
    PostID varchar(50), 
    PostTitle varchar(50), 
    PostContent varchar(50),
    GotTags bit default 0
)

/* Assuming you care about the ID's, this will get you all of 
   the tags without duplicating any post content */
Declare @PostTags Table (
    PostID int,
    TagID int,
    TagName varchar(50)
)

-- Populate posts from the main table
Insert Into @Posts (PostID, PostTitle, PostContent)
Select * From Posts

-- Now loop through and get the tags for each post. 
Declare @CurrentPostID int
Set @CurrentPostID = (Select Top 1 PostID From @Posts Where GotTags = 0)
While @CurrentPostID Is Not Null
    Begin
        Insert Into @PostTags (PostId, TagID, TagName)
        Select pt.postid, pt.tagid, t.name
        From Tags t 
            Join PostTags pt
                On t.id = pt.tagid
        Where pt.postid = @CurrentPostID

        -- Set next loop
        Update @Posts Set GotTags = 1 Where PostID = @CurrentPostID
        Set @CurrentPostID = (Select Top 1 PostID From @Posts Where GotTags = 0)
    End

-- Return 2 recordsets, which are related by the PostID column found in both sets
Select * from @Posts
Select * From @PostTags

我更喜欢这种类型的解决方案,而不是将字符串连接成一个字符串,然后再拆分它们;它可以更容易地以这种方式处理数据,允许在C#中更加面向对象,并且可以让您更容易跟踪标记ID,以防标记需要被删除或添加到帖子中,您不需要通过名称查找标签或匹配,因为您已拥有该ID。

答案 1 :(得分:0)

  

显然很多网站都这样做,我想知道最好的方式..

最好的方法:没有一个。

  

以及如何在现实世界中完成它?

实体框架会按照您的建议构建查询并实现您需要的对象。是的,存在重复数据,但更常见的是,重复数据与相关数据相比更好,然后再次尝试将信息重新关联在一起。优点是代码更易读,更易于使用c#like语言进行查询,具有相关记录和更改跟踪(默认情况下)。

Dapper can do the same thing - A parent object with it's children objects。它是faster,但它没有更改跟踪,并且语句不是c#,它们(据我所见)直接SQL,这使得编写动态查询变得更加困难。

  

但肯定有更好的方法吗?

我不知道更好是什么。是否更有效,更少的内存开销,更少的网络数据包/大小,更易于维护,更具可读性?

  

有没有办法在不复制返回的每个标记行的帖子信息的情况下执行此操作?

是的,您可以编写存储过程来返回多个记录集,实现对象,并手动连接

This sounds like you are trying to optimize something you don't have a problem with.

答案 2 :(得分:0)

我会写一个查询来返回多个记录集。在进行性能测试之前,我不会担心过度优化。

我不确定Dapper最近支持一对多或多对多查询,但您可能想要查看Insight.Database 4.0中的新功能。现在有一个预先发布的nuget。

查看预发布文档。我会喜欢一些反馈。

https://github.com/jonwagner/Insight.Database/wiki/Proposed-4.0-Changes