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

Sql server 在不牺牲性能的情况下存储大型文本字段的可维护方式是什么?,sql-server,database,Sql Server,Database,我一直在围绕这个问题跳舞有一段时间了,但它不断地出现。我们有一个系统,我们的表可能从最初存储为NVARCHAR(150)的描述开始,然后我们得到一张票,要求将字段大小扩展到250,然后是1000等等 在我们添加到大多数表中的“备注”字段和/或“说明”字段上重复此循环。当然,我关心的是性能和打破页面的8k限制。然而,我的另一个担忧是,通过将系统中每个表中的这些字段分解为延迟加载的引用,降低了系统的可维护性 所以在这里,我面临着同样的两个选项,它们一直盯着我看。(欢迎其他人)请把你的意见告诉我 将所

我一直在围绕这个问题跳舞有一段时间了,但它不断地出现。我们有一个系统,我们的表可能从最初存储为
NVARCHAR(150)
的描述开始,然后我们得到一张票,要求将字段大小扩展到250,然后是1000等等

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

所以在这里,我面临着同样的两个选项,它们一直盯着我看。(欢迎其他人)请把你的意见告诉我

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

  • 删除所有注释和/或说明字段,并将其替换为对
    [notes]
    表的外键引用

    创建表[dbo].[Notes](
    [NoteId][int]不为空,
    [NoteText][NVARCHAR]
    MAX
    非空)

  • 显然,我更喜欢使用选项1,因为如果使用选项2,我们的系统会发生很大的变化。然而,如果选择2真的是唯一的好方法,那么至少我可以说这些改变是必要的,我已经完成了家庭作业


    更新: 我在一个包含100000条记录的样本数据库上运行了几个测试。我发现,由于集群索引扫描,选项1所需的IO“大约”是选项2的两倍。如果我选择了大量记录(1000条或更多),则选项1的速度是选项1的两倍,即使在“选择”中没有包含大文本字段。我要求的行数越少,线条越模糊。我是一个网页大小通常为50左右的web应用程序,因此选项1可以工作,但为了可扩展性,我将在不久的将来将所有实例转换为选项2。

    我选择选项2


    您可以创建一个视图,将两个表连接起来,使每个人都能更轻松地进行转换,然后执行一个清理过程,删除该视图并尽可能使用单个表。

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


    它附带了一些字符串,比如字符串函数的特殊行为,但是对于次要的“notes/description”类型的字段,这些可能不是什么问题。

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


    本质上,使用文本字段在某种程度上介于两种解决方案之间。它使您的表行比使用varchar小得多,但如果可能的话,您仍然希望避免在查询中询问这些行。

    选项2更好,原因如下:

  • 查询表时,大的 文本字段快速填充页面, 强制数据库扫描更多内容 用于检索数据的页面。这是 特别是当你不需要的时候 实际上需要返回文本 数据
  • 正如你提到的,它给你 更改数据的完全中断 一次输入。微软有 SQL Server 2008中已弃用的文本, 所以你应该坚持 VARCHAR/VARBINARY
  • 单独的文件组。有 所有文本数据都以较慢的速度显示, 更便宜的存储位置可能是 你决定追求的东西 未来。如果没有,就没有伤害,没有危险 犯规
  • 虽然选项1目前比较容易,但从长远来看,选项2将为您提供更大的灵活性。我的建议是使用从主表中分离出来的“notes”信息实现一个简单的概念证明,并对这两个示例执行一些查询。将一些查询的执行计划、客户端统计信息和逻辑I/O读取(将统计信息IO设置为ON)与这些表进行比较

    对于建议使用MSDN中的TEXT/NTEXT的人,请注意:

    此功能将在以后的操作中删除 Microsoft SQL的未来版本 服务器。避免在中使用此功能 新的开发工作,并计划 修改当前使用的应用程序 这项功能。使用varchar(最大值), nvarchar(max)和varbinary(max)数据 而不是打字。欲了解更多信息, 请参见使用大值数据类型


    只是为了扩展选项2

    你可以:

    将现有MyTable重命名为MyTable_V2

    将“注释”列移动到已联接的注释表中(具有1:1联接ID)

    创建一个名为MyTable的视图,该视图连接MyTable_V2和Notes表

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

    注意:在MyTable_V2中有一个计算列的情况下,我们在执行此操作时遇到了问题(我认为这就是问题所在,无论哪种方式,我们在使用“异常”表执行此操作时都遇到了障碍)

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

    (可选)在MyTable日志上插入触发器以显示调用它的事实(它可以至少做到这一点,仅当现有行的日期大于24小时时,才使用GetDate()更新预先存在的日志表行,因此每天只更新一次)

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

    正如您所猜测的,要实现的麻烦太多了

    或者,拖网代码以查找对MyTable的所有引用,并将它们更改为MyT