在不牺牲性能的情况下存储大型文本字段的可维护方法是什么?

时间:2009-02-17 22:08:02

标签: sql-server database

我一直在围绕这个问题跳舞,但它一直在上升。我们有一个系统,我们的表可以从最初存储为NVARCHAR(150)的描述开始,然后我得到一张要求将字段大小扩展到250,然后是1000等等的票据...... / p>

在我们添加到大多数表格的“note”字段和/或“description”字段上重复此循环。当然,我关注的是性能并打破了页面的8k限制。但是,我的另一个问题是通过将系统中的每个表中的这些字段分解为延迟加载的引用来降低系统的可维护性。

所以在这里我面对的是两个一直盯着我看的选项。 (欢迎其他人)请借给我你的意见。

  1. 将所有可能的备注和/或说明更改为NVARCHAR(MAX),并确保我们在所有列表中排除这些字段。基本上永远不会做:SELECT * FROM [TableName],除非它只检索一条记录。

  2. 删除所有注释和/或说明字段,并将其替换为[Notes]表的forign键引用。

      

    CREATE TABLE [dbo].[Notes] (
    [NoteId] [int] NOT NULL,
    [NoteText] [NVARCHAR]
    MAXNOT NULL )

  3. 显然我更喜欢使用选项1,因为如果我们选择2,它会在我们的系统中发生很大变化。但是如果选项2真的是唯一的好方法,那么至少我可以说这些变化是必要的,我做完了作业。


    更新: 我在一个包含100,000条记录的示例数据库上运行了几次测试。我发现由于集群索引扫描选项1所需的IO大约是选项2所需的IO的两倍。如果我选​​择大量记录(1000或更多),即使我做了选项1也是两倍慢不包括选择中的大文本字段。随着我请求更少的行,线条模糊更多。我是一个网页应用程序,其中页面大小为50左右是常态,因此选项1将起作用,但我将在(非常)不久的将来将所有实例转换为选项2以实现可伸缩性。

5 个答案:

答案 0 :(得分:5)

选项2更好有几个原因:

  1. 查询表时,大 文本字段快速填充页面, 强制数据库扫描更多 用于检索数据的页面。这是 当你不这样做时尤其沉重 实际上需要返回文本 数据。
  2. 如你所说,它给你 干净的休息时间来改变数据 一举打造。微软有 在SQL Server 2008中弃用了TEXT, 所以你应该坚持下去 VARCHAR / VARBINARY。
  3. 单独的文件组。有 你的所有文字数据都比较慢, 更便宜的存储位置可能是 你决定追求的东西 未来。如果没有,没有伤害,没有 犯规。
  4. 虽然选项1现在更容易,但选项2将为您提供更长期的灵活性。我的建议是实现一个简单的概念验证,其中“notes”信息与主表分开,并对两个示例执行一些查询。比较针对这些表的一些查询的执行计划,客户端统计信息和逻辑I / O读取(SET STATISTICS IO ON)。

    对于那些建议使用MSDN中的TEXT / NTEXT的人的快速说明:

      

    此功能将在a中删除   Microsoft SQL的未来版本   服务器。避免使用此功能   新的发展工作,并计划   修改当前使用的应用程序   此功能。使用varchar(max),   nvarchar(max)和varbinary(max)数据   而是类型。欲获得更多信息,   请参阅使用大值数据类型。

答案 1 :(得分:2)

我选择选项2。

您可以创建一个连接两个表的视图,以便在每个人上更轻松地进行转换,然后进行清理过程,删除视图并尽可能使用单个表。

答案 2 :(得分:2)

您想要使用TEXT字段。 TEXT字段不直接存储在行中;相反,它存储指向文本数据的指针。这对查询是透明的 - 如果你要求TEXT字段,它将返回实际文本,而不是指针。

基本上,在两个解决方案之间使用TEXT字段。它使您的表行比使用varchar小得多,但如果可能的话,您仍然希望避免在查询中询问它们。

答案 3 :(得分:1)

TEXT / NTEXT数据类型的长度实际上是无限的,而在记录中几乎没有任何内容。

它带有一些附加的字符串,就像字符串函数的特殊行为一样,但对于辅助“注释/描述”类型的字段,这些可能不是问题。

答案 4 :(得分:1)

仅扩展选项2

你可以:

将现有的MyTable重命名为MyTable_V2

将Notes列移动到已连接的Notes表(使用1:1加入ID)

创建一个名为MyTable的VIEW,它连接MyTable_V2和Notes表

在MyTable视图上创建一个INSTEAD OF触发器,将Notes列保存到Notes表中(IF NULL然后删除任何现有的Notes行,如果NOT NULL则删除如果未找到则插入,否则更新)。在MyTable_V2表上执行适当的操作

注意:我们在MyTable_V2中存在Computed列的情况下遇到了这个问题(我认为这是问题所在,无论哪种方式我们在使用“异常”表格时都遇到障碍)

应编写所有新的插入/更新/删除代码,以便直接在MyTable_V2和Notes表上运行

可选:让MyTable上的INSERT OF触发器记录它被调用的事实(它可以最低限度地执行此操作,仅当现有行的日期> 24小时时,使用GetDate()更新预先存在的日志表行 - 所以每天只会做一次更新。

当您不再获取任何日志记录时,可以在MyTable视图中删除INSTEAD OF触发器,现在您完全符合MyTable_V2!

你猜测,实施的麻烦很多。

或者搜索所有对MyTable的引用的代码并将它们更改为MyTable_V2,仅将VIEW替换为MyTable for SELECT,而不是创建INSTEAD OF触发器。

我的计划是修复所有引用现已弃用的MyTable的Insert / Update / Delete语句。对我来说,这会更容易一些,因为我们对数据库中的所有表和列使用唯一的名称,并且我们在所有应用程序代码中使用相同的名称,因此确保通过简单的FIND找到所有实例都会很高。 / p>

P.S。如果你有任何SELECT *,那么选项2也是首选。我们有一些客户,当他们将大型Text / Blob列添加到现有表时,应用程序性能快速下降 - 因为“lazy”SELECT *语句。希望这不是你店里的情况!