Sql 这如何不使varchar2变得低效?

Sql 这如何不使varchar2变得低效?,sql,database,database-design,rdbms,varchar,Sql,Database,Database Design,Rdbms,Varchar,假设我有一个列名为varchar(20)的表,并且我存储了一个名为“abcdef”的行 在这种情况下,name的内存分配是如何完成的? 我可以想到两种方法: a) 分配了20个字节,但只使用了6个字节。在这种情况下,varchar2与char相比,在内存分配方面没有任何显著优势 b) 只分配了6个字节。如果是这样的话,我在这一行后面加了几行 INSERT INTO tab(id, name) values(13, 'yyyy'); INSERT INTO tab(id, name) values

假设我有一个列名为varchar(20)的表,并且我存储了一个名为“abcdef”的行

在这种情况下,
name
的内存分配是如何完成的?

我可以想到两种方法:

a)

分配了20个字节,但只使用了6个字节。在这种情况下,
varchar2
char
相比,在内存分配方面没有任何显著优势

b)

只分配了6个字节。如果是这样的话,我在这一行后面加了几行

INSERT INTO tab(id, name) values(13, 'yyyy');
INSERT INTO tab(id, name) values(14, 'zzzz');
然后我做一个更新

UPDATE tab SET name = 'abcdefghijkl' WHERE id = 12;
DBMS从何处获得所需的额外6个字节?可能存在下一个6个字节不可用的情况(如果最初仅分配了6个字节,则下一个字节可能已分配给其他字节)


除了把这排人换到一个新地方,还有别的办法吗?在索引组织的表中,即使是移位也会有问题(对于堆组织的表来说可能没问题)。

它肯定不会分配比需要更多的空间,这会破坏使用可变长度类型的意义

在您提到的情况下,我认为下面的行必须在页面上向下移动,也许这是某种优化。我真的不知道确切的细节,也许其他人可以进一步评论。

编辑出于某种原因,我认为这是标记为Microsoft SQL Server的。不过,我认为答案仍然相关

这就是为什么

  • 当列数据项的大小一致时,使用char
  • 当列数据项的大小差异很大时,使用varchar
  • 当列数据项的大小不同时,使用varchar(max) 相当大,而且尺寸可能很大 超过8000字节
这是设计表结构时需要考虑的一个折衷。也许你需要考虑更新频率VS在这个计算中也


值得注意的是,对于
char
a
NULL
值仍然使用所有存储空间。Management Studio有一个名为的加载项,可让您轻松查看行的存储方式。

根据您使用的rdbms,可能会有不同的存储方式,但通常:

仅分配存储在
varchar
字段中的实际数据。大小只是允许的最大值,与分配的数量无关

我认为在某些系统上,
char
字段也是如此。可变大小数据类型的处理效率足够高,因此在分配最大值时不再有任何收益


如果更新记录以使其需要更多空间,则同一分配块中的记录会下移,如果记录不再适合该块,则会分配另一个块,并在块之间分配记录。这意味着分配块内的记录是连续的,但磁盘上的块不必是连续的。

这可能严重依赖于数据库

不过有两点:观察数据库实际上并不会更新磁盘或内存缓存中的数据。它们插入带有更新数据的新行,并将旧行标记为已从上的某个事务中删除。一段时间后,被删除的行对任何事务都不可见,并且被回收

对于空间存储问题,通常采用
1-4字节的头+数据(+填充)


对于字符,数据被填充以达到足够的长度。在varchar或text的情况下,标题存储以下数据的长度。

鉴于问题标题中的VARCHAR2,我假设您的问题的重点是Oracle。在Oracle中,可以使用PCTFREE子句为数据块内的行扩展保留空间。这有助于减轻更新导致行变长的影响

但是,如果Oracle在块中没有足够的可用空间回写行,那么它所做的就是所谓的行迁移;它将原始地址单独留在磁盘上(因此不一定需要更新索引),但它不将数据存储在原始位置,而是存储指向该行新地址的指针


在表被索引大量访问的情况下,如果大量行已迁移,这可能会导致性能问题,因为它会添加额外的I/O以满足查询。

这取决于实现,但很可能在更新时,新行写入的位置与旧行完全不同。即使没有varchar,情况也是如此。在Oracle和DB2系统上,NOT NULL CHAR(n字节)字段将始终消耗n字节。您对MVCC数据库工作方式的描述与Oracle的工作方式不匹配。Oracle将就地更新行,同时写入信息以撤消回滚段或撤消表PCE中的更改,以便可以读取以前的版本。以及写入重做日志,以便在发生故障时不会丢失更改。
UPDATE tab SET name = 'abcdefghijkl' WHERE id = 12;