表结构修改的内幕

经常会人有这样问我:DBA,我需要修改表结构,你评估一下。往往针对不同的修改,都需要去审核和给出建议。表结构修改的类型是很多种,下文总结一些基本的点。

并不是任何一个ALTER TABLE操作都需要将所有的数据行修改一遍。ALTER TABLE三种基本的实现方式:

  • 只修改元数据
  • 为了保证修改的兼容性需要验证每一行数据,然后只修改元数据
  • 物理性的修改每一行

很多时候,SQL Server只需要通过修改元数据去完成表结构的修改,而不需要修改行数据。比如:

  • 删除列
  • 新增允许为NULL的列
  • 不允许为NULL的列修改成允许为NULL的列
  • 变长列增加宽度

需要注意:删除列只修改元数据,也就意味着列所使用的存储空间不会被回收。可以通过在表上创建或重建聚集索引来回收,或ALTER TABLE REBUILD来回收。

有些修改表结构的操作只需要验证被修改的数据,然后修改元数据,而不需要修改实际数据。例如:

  • NULL列修改为非NULL列,需要验证所有数据是否有NULL值存在。如果有,则修改失败。
  • 减少变长列的宽度,需要验证所数据是否符合新的宽度。不符合,则修改失败。
  • 减少定长列的宽度,也需要验证所数据是否符合新的宽度。减少定长列的宽度后,只是从逻辑上限制了写入此列的数据范围,但是实际占用仍然是按之前列宽度来计算的。除非重建表,才会将数据缩减为新的较窄的列宽度存储。

如果表的数据量比较大,数据验证也是一种资源敏感型操作,需要一定的时间。

其它的修改表结构的操作,需要物理修改数据。

既然修改了数据,当然还要写事务日志。比如修改列的类型为其它存储格式的类型(如int改成varchar)。

还有一种情况需要注意:增加列的宽度时,实际是增加一个新列,将旧列的数据复制到新列,原来列仍然会存在。

CREATE TABLE change
(col1 smallint, col2 char(10), col3 char(5));
go
SELECT c.name AS column_name, column_id, max_inrow_length, pc.system_type_id, leaf_offset
 FROM sys.system_internals_partition_columns pc
 JOIN sys.partitions p
 ON p.partition_id = pc.partition_id
 JOIN sys.columns c
 ON column_id = partition_column_id
 AND c.object_id = p.object_id
WHERE p.object_id=object_id(‘change‘);
go
ALTER TABLE change
 ALTER COLUMN col1 int;
 go
 SELECT c.name AS column_name, column_id, max_inrow_length, pc.system_type_id, leaf_offset
 FROM sys.system_internals_partition_columns pc
 JOIN sys.partitions p
 ON p.partition_id = pc.partition_id
 JOIN sys.columns c
 ON column_id = partition_column_id
 AND c.object_id = p.object_id
WHERE p.object_id=object_id(‘change‘);
go

在上面中示例中,将列col1从smallint修改为int后,col1的偏移量从4变成21。(4,6]区间仍然存原来的smallint列,修改后的列从21开始。

删除列,不会实际删除数据,只是修改元数据,所以原来列还占据行容量(最大行容量=8KB-96B(行头)-36B(行偏移矩阵最小值)=8060)。如果列的宽度总合超过了最大行容量,则会报错。

CREATE TABLE bigchange
(col1 smallint, col2 char(2000), col3 char(1000));
ALTER TABLE bigchange
 ALTER COLUMN col2 char(3000);
--上面修改成功执行,下面的修改会失败
Msg 1701, Level 16, State 1, Line 1
Creating or altering table ‘bigchange‘ failed because the minimum row size would be 9009, including 7 bytes of internal overhead. This exceeds the maximum allowable table row size of 8060 bytes.

第一次修改后行容量=2+2000+1000+3000=60002,第二次修改需要的容量:2+2000+1000+3000+3000+7=9009超过了8060,报错。修改表时不允许超过8060,但是在创建表时可以超过此限制:

CREATE TABLE nochange
(col1 smallint, col2 char(3000), col3 char(1000), col4 char(3000));

增加新列时,新列会使用新的更大的列号(column_id),SQL Server输出字段时的顺序是使用列的逻辑顺序,也就是column_id从小到大。所以如果需要按特定顺序输出列,可以:

  • 不要使用Select *,Select中指定需要的列顺序
  • 创建一个视图并在视图中指定列顺序,然后Select * from 视图
  • 创建一个新表,将原表的数据导入新表,删除旧表,交换表名

引用:

《Microsoft SQL Server Internals》

时间: 2024-10-12 05:21:22

表结构修改的内幕的相关文章

【转】Mysql千万级数据表结构修改

当需要对表进行ddl操作如加索引.增删列时,数据量小时直接在线修改表结构影响不大当表达到百万.千万数据就不能直接在线修改表结构 下面是具体的过程:1.备份数据select * from ih_order into outfile '/bak/order.txt';mysql> select * from ih_order into outfile 'D:/bak/order.txt';Query OK, 10001000 rows affected (1 min 30.18 sec) 2.复制原

Oracle表结构修改触发视图无法正常使用问题

一.问题描述 当对视图使用的基表进行表结构修改后,会触发视图的无效以及编译出错问题,必须重建视图解决. 二.问题再现 1.Oracle10g环境 1.1 创建视图测试用两张基表:TestTable和TestUser 1.2  创建测试视图VW_TABLEUSERALL和VW_TABLEUSER 前者使用了TestTable.*, 后者显示指定列. 查询视图的状态如下:全部是Valid状态 1.3 修改TestTable表结构:增加一列Col1 再次查询视图的状态如下:全部是INVALID状态  

MySql表结构修改详解

修改表的语法 先创建两个表: 表一:tbl_department 部门表 create table tbl_department ( dept_id int(10) not null unsigned auto_increment, dept_name varchar(20) not null, dept_describ varchar(100) , primary key(dept_id) ) 表二:tbl_person 人员信息表 create table tbl_person( p_id

dblink 的源数据表结构修改后在存储过程里执行报错

原情况:A服务器表A服务器B也有一张表A服务器B上有一个存储过程要把本地的head表向A服务器表里插入数据.变更后:在A服务器表里增加了一个字段inserttime,服务器B存储过程本地表向A服务器插入时,记录插入的时间.问题修改语句如下:insert into [email protected]  select t.*,sysdate from A;这个语句单独执行没有问题.但在存储过程里执行一直报错,报值过多. 解决: 猜想可能是dblink的问题,把原来的dblink删除,重新新建一个db

DDL表结构修改

*1)创建表 create table 表名( 字段名 类型, .... ); //以现有表复制一个新表 create table j012 as select id,name,salary from j010 where 1<>1; 2)删除表 drop table 表名;//删除,放入回收站 drop table 表名 purge;//直接删除,不放入回收站 *3)修改表结构 a.添加一列字段 alter table 表名 add (字段名 类型); //向j014表中添加一列sex al

15.表结构修改-修改表定义和表名

修改列定义和表名 修改列定义 ALTER  TABLE  S MODIFY  type TINYINT  UNSIGNED  NOT NULL;(modify是调整 稍作修改的意思,UNSIGNED表示是无符号的,是一个正数如果直接不用UNSIGNED,那int可以是正数负数和零) 演示: 修改表字段 --type(字段名称)  数据类型:tinyint(小整型) 默认是1 将type int ,默认值为2 ALETER TABLE member MODIFY  type  INT UNSIGN

表结构修改以及sql增删改查

修改表结构 修改表名 alter table 表名 rename 新名 增加字段 alter table 表名 add 字段名 数据类型 约束 删除字段 alter table 表名 drop 字段名 修改字段 alter table 表名 change 旧字段名 新字段名 数据类型 约束条件 修改字段顺序 alter table 表名 add 字段名 数据类型 约束条件 first #将该字段放在第一行 alter table 表名 add 字段名 数据类型 约束条件 after 字段名2 #

数据库表的创建、管理和数据操作(实验一),数据库创建

数据库表的创建.管理和数据操作(实验一),数据库创建 今天我们就以实验的形式对表的创建.管理和数据操作进行学习,上课吧. [实验目的]:了解SQL语言的使用,进一步理解关系运算,巩固数据库的基础知识.[实验要求]:利用SQL语言进行数据库表的各种操作:1.数据库表的创建.修改和删除操作.2.向表中进行数据的插入.删除和修改操作.[实验内容]1. 利用数据定义语句在实验一创建的stu_DB库中建立学生管理系统的三个表:Student.Course.SC.2.利用INSERT.UPDATE和DELE

mysql远程表链接

FEDERATED简介 FEDERATED存储引擎是访问远程数据库中的表,在平时开发中可以用此特性来访问远程库中的参数表之类的,还是非常方便的.使用时直接在本地构建一个federated表来链接远程数据表,配置好之后本地数据库可以直接和远程数据表进行同步,实际上这个数据库并不是真实存放数据,所需要的数据都是存放在远程服务器上. 配置条件 mysql版本需要5.1以上: 在安装是需要把federated引擎已经安装; 配置步骤 1.开启federated引擎: 在mysql终端执行: show e