SQL Server 2008 中的 varchar 存储和比较

Dea*_*ean 2 sql-server-2008 sql-server varchar

我有一张大表,其结构与此类似:

Id       bigint primary key
Sequence int
Parent   int foreign key
Data1    varchar(20)
Data2    varchar(20)
Data3    varchar(20)
Data4    varchar(20)
Data5    varchar(20)
Run Code Online (Sandbox Code Playgroud)

数据 1-5 是文本字段(主要是数字和破折号),可用于帮助防止意外记录重复项,但数据实际上偶尔会重复。我们必须让用户确认是的,它是重复的。

对于数百万行,这种重复检查可能非常耗时。我的任务是确保我们可以处理将我们保存在这个数据库中的数据量增加三倍。以前的开发人员告诉我,当前的流程无法处理那么多数据(当然,处理数据的内容比我在这里包括的要多得多,尽管查找重复项的实际查询非常简单系统的这一部分显然对额外的延迟很敏感)。我自己没有做过实验来证明这一点,但我相信他们的判断,无论如何我都想尽可能地减少影响。不幸的是,直到过程的后期,我才可以测试大量的数据。

所有 5 个字段都必须匹配才能算作重复。我怀疑这永远不会改变,但有人告诉我,在可预见的未来,所有 5 个都必须完全匹配。我在想,通过添加一个作为组合值散列的索引列,我将能够更快地找到潜在的重复项。不过,我仍然需要比较各个值以解决哈希冲突。通过结合这些价值本身,我会得到什么吗?那么一个散列列和一个包含所有 5 个值的分隔字符串中的单个列,我用来比较而不是分别比较每个值?

似乎只有当 varchar 不与行的其余部分一起存储时,我才会受益,我一直认为它们是。然而,情况似乎并非如此。我很难找到特定信息,但似乎如果我的行少于 8060 字节,则将在行中分配完整的 20 个字符。只有当行超过 8060 时,才会选择一些 varchar 列移动到单独的页面中。

任何解释如何存储小型 varchars 的文档,以及关于是否将列组合成单个列是否会对性能产生影响的任何建议,都将不胜感激。

编辑:该表将有数百万行(例如估计为 1000 万行)。每行的最大长度肯定会小于 8060 字节,但我现在不能给你一个确切的数字。

Han*_*non 7

添加一个包含CHECKSUM5 个字段的持久计算字段,并使用它来执行比较。

CHECKSUM对于特定的字段组合,该字段将是唯一的,并存储为 一个INT,从而使WHERE子句中的比较目标变得更加容易。

USE tempdb; /* create this in tempdb since it is just a demo */

CREATE TABLE dbo.t1
(
    Id       bigint constraint PK_t1 primary key clustered identity(1,1)
    , Sequence int
    , Parent   int not null constraint df_T1_Parent DEFAULT ((0))
    , Data1    varchar(20)
    , Data2    varchar(20)
    , Data3    varchar(20)
    , Data4    varchar(20)
    , Data5    varchar(20)
    , CK AS CHECKSUM(Data1, Data2, Data3, Data4, Data5) PERSISTED
);

GO

INSERT INTO dbo.t1 (Sequence, Parent, Data1, Data2, Data3, Data4, Data5)
VALUES (1,1,'test','test2','test3','test4','test5');

SELECT *
FROM dbo.t1;
GO
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

/* this row will NOT get inserted since it already exists in dbo.t1 */
INSERT INTO dbo.t1 (Sequence, Parent, Data1, Data2, Data3, Data4, Data5)
SELECT 2, 3, 'test', 'test2', 'test3', 'test4', 'test5'
WHERE Checksum('test','test2','test3','test4','test5') NOT IN (SELECT CK FROM t1);

/* still only shows the original row, since the checksum for the row already
exists in dbo.t1 */
SELECT *
FROM dbo.t1;
Run Code Online (Sandbox Code Playgroud)

为了支持大量行,您需要在CK字段上创建一个非唯一索引。

顺便说一句,您忽略了在此表中您期望的行数;这些信息将有助于提出重要建议。

行内数据限制为最大 8060字节,这是单页数据的大小,减去每页所需的开销。任何大于该值的单行都将导致行数据的一些页外存储。我确信http://dba.stackexchange.com 的其他贡献者可以为您提供关于大行存储的引擎内部结构的更简洁的定义。目前你最大的一排有多大?

如果项目Data1, Data2, Data3...具有以不同顺序出现的相同值,则校验和将不同,因此您可能需要考虑这一点。

在与了不起的Mark Storey-SmithThe Heap上进行了简短的讨论之后,我想提供一个类似但可能更好的选择来计算相关字段的散列。您可以交替使用HASHBYTES()计算列中的函数。 HASHBYTES()有一些问题,例如必须将字段连接在一起,包括字段值之间的某种类型的分隔符,以便传递HASHBYTES()单个值。有关 的更多信息HASHBYTES(),马克推荐了这个站点。显然,MSDN 在http://msdn.microsoft.com/en-us/library/ms174415.aspx 上也有一些很棒的信息

  • 生成校验和/散列的费用是 CPU 时间,即使这样也不是很高,所以与写入新/更新行的一般成本相比应该是微不足道的。在数据层做这件事意味着数据库可以强制它总是得到更新,而不是冒着以后代码接触表的风险,而有人忘记了这需要完成。 (3认同)