数字数据类型 - 存储

时间:2017-09-08 18:40:55

标签: sql sql-server

根据Microsoft Site ,数字类型为Numeric(10,2) - 10表示精度应该有9个字节。

但是当我这样做时:

DECLARE @var as numeric(10,0) = 2147483649

SELECT @var, DATALENGTH(@var)

DATALENGTH(@var)返回5个字节而不是10.有人可以解释为什么吗?

2 个答案:

答案 0 :(得分:0)

文档指定:

  

根据精确度,最大存储容量会有所不同。

对于给定的精度,存储不是常量。实际存储取决于值。

注意,这与整数无关。以下还返回5:

(a + b[:,None])[0,0]
#array([[ 2.,  2.],
#       [ 2.,  2.]])

(a + b[:,None])[0,1]
#array([[ 1.,  1.],
#       [ 1.,  1.]])

(a + b[:,None])[1,0]
#array([[ 1.,  1.],
#       [ 1.,  1.]])

(a + b[:,None])[1,1]
#array([[ 0.,  0.],
#       [ 0.,  0.]])

实际上,SQL Server似乎使用了所需的存储量,而不是该类型的最大值。您可以通过将“10”更改为“20”并注意数据长度不会改变来轻松看到这一点。

编辑:

如果你运行,你可以看到对价值的依赖:

declare @var numberic(11, 1) = 214483649.8

这两个长度不一样。

答案 1 :(得分:0)

@GordonLinoff的另一个答案是错误的,或者至少是误导性的。 Numeric不存储可变数量的字节,但具有固定大小的特定精度。

在SQL Server 2017上尝试此操作会得到相同的结果。

您最初为numeric链接的文档对于存储不同精度的数字所需的字节数是正确的。

此存储要求仅基于numeric列的精度。换句话说,就是使用了多少字节的存储空间。它不是最大值取决于该行中的值。 所有行对该列使用相同的字节数。

此变体的关键是DATALENGTH的文档说明了此功能

  

Returns the number of bytes used to represent any expression.

似乎DATALENGTH并不代表'代表',就像磁盘上的'代表'一样,而是代表内存中的'代表'。 关于numeric的其他文档是关于numeric的磁盘存储。

这可能是因为DATALENGTH主要用于var *类型或其他BLOB类型。

因此,虽然numeric(20,1)需要13个字节的存储空间,但根据值,SQL Server可以在内存中以较小的字节数表示它,即DATALENGTH计算它时。

正如我在其他评论中所指出的,尽管numeric具有不同的大小,但它是固定大小的数据类型,因为对于特定表中的特定列,每个值占用相同的存储量。 / p>

粗略地说,SQL Server行有4个部分:

  1. 4字节标题
  2. 固定尺寸​​数据
  3. 抵消可变大小数据
  4. 可变大小数据
  5. Numerics&其他固定大小类型存储在2中,var*存储在4中,长度为3。

    此脚本显示表格的元数据,其中包含一些固定的&变量列。

    declare @a numeric(20, 1) = '123.1';
    declare @b numeric(20, 1) = '1234567890123456789.0';
    select datalength(@a) union select datalength(@b);
    
    create table #numeric(num1 numeric(20,1), text1 varchar(10), char2 char(6));
    insert into #numeric(num1, text1, char2) values ('123.1', 'hello', 'first'), ('1234567890123456789.0', 'there', '2nd');
    
    select datalength(num1) from #numeric;
    
    select
        t.name as table_name,
        c.name as column_name,
        pc.partition_column_id,
        pc.max_inrow_length,
        pc.max_length,
        pc.precision,
        pc.scale,
        pc.collation_name,
        pc.leaf_offset
    from tempdb.sys.tables as t
    join tempdb.sys.partitions as p
        on(t.object_id=p.object_id)
    join tempdb.sys.system_internals_partition_columns as pc
        on(pc.partition_id=p.partition_id)
    join tempdb.sys.columns as c
        on((c.object_id=p.object_id)and(c.column_id=pc.partition_column_id))
    where (t.object_id=object_id('tempdb..#numeric'));
    
    drop table #numeric;
    

    请注意leaf_offset列。这表示原始二进制数据中值的起始位置。 第一列在4字节标题后立即开始。 根据SQL文档,第二个固定列在13个字节后启动。 varchar列的偏移量为-1,表示它是一个可变长度的列&它在字节数组中的位置不固定。 在这种情况下,它可以修复,因为只有1个var列,但alter table语句可以添加另一个列&转移事物。

    如果你想进一步研究,最好的来源是Kalen Delaney的一本名为SQL Server Internals的书。她是编写SQL Server的团队的一员。