use*_*570 18 sql-server datatypes import
在将数据导入到 SQL Server 时,人们通常无法考虑导入的字符串字段有多大。我是否可以懒惰地继续对 char 字段定义使用较大的值?如果我花精力寻找合适的 char 字段最大大小,它会对性能和速度产生任何影响吗?
如果重要的话,我有 SQL Server 2016 和 2019。
Mic*_*een 29
SQL Server 通常必须分配工作内存来运行查询。授予的内存量取决于许多因素,包括列数据类型和长度。对于 (n)varchar,它猜测实际值平均为声明长度的一半,并相应地计算所需的内存。
因此,过大的列可能会导致内存预留过大、吞吐量降低和系统性能变差。
为了说明这一点,我创建了一系列表格。全部都是形式
create table dbo.t10(c varchar(10) null);
:
create table dbo.t2000(c varchar(2000) null);
Run Code Online (Sandbox Code Playgroud)
列的长度因表而异。最短为 10(如图所示),最长为 2,000。
每个表都填充有一百万行。每行由单个字母“a”组成。因此,所有表都保存相同数量的数据并占用相同数量的磁盘空间 ( exec sp_spaceused 'dbo.t10';
)。
我使用一个非常简单的查询
select c from dbo.t10 order by c option(maxdop 1);
Run Code Online (Sandbox Code Playgroud)
该排序意味着将请求内存授予。使用 maxdop 1 可以避免某些计划并行并使比较复杂化的风险。
我对每个表 t10 到 t2000 运行此命令,捕获实际执行计划并记录 MemoryGrantInfo。下图显示了所需内存(以 KB 为单位)随列长度的变化情况。我觉得还是很有说服力的。
10 到 2,000 的大小范围没有任何意义。它们只是任意的,是为了方便而选择的,而不是为了说明任何特定的观点。
同样,字母“a”是我手指输入的第一个内容。
我的系统有足够的内存。每次测试中的 Desired、Requested 和 Granted 都是相同的。我选择绘制所需的内存,因为如果存在差异,它将是最大的。
据推测,导入数据的目的是为了允许某人使用它。如果是这样,那么就必须有人计算出要分配多少空间。在绝大多数情况下,在导入过程中执行一次比在下游执行多次更有意义,因为报告、应用程序和其他服务会尝试弄清楚它们是否真的需要允许 4000 个字符。通常,这些下游应用程序将根据声明的列大小来分配内存缓冲区,在这种情况下,您可能会为它们带来性能和可扩展性问题。
忽视下游用户的痛苦,将一切定义为[n]varchar(4000)
使得索引变得痛苦。对于 SQL Server 2016,聚集索引键只能有 900 字节,非聚集索引键只能有 1700 字节。如果您定义了适当长度的列,您就会知道您要创建的索引是否会超出这些限制。如果您将所有内容都定义为[n]varchar(4000)
,您今天也许可以定义索引,但明天您的加载将失败,因为它试图插入键太长的行。设计和调试一个由列长度告诉您实际限制的系统比使用一组随机索引定义该限制的系统要容易得多。