SqlServer 垂直分表(减少程序改动)

由于sqlserver的设计特殊性,一般大量数据一般都是采用水平分表,而垂直分表只是把text、图片都较大数据放到单独的表中,这样数据设计会更合理,相对于mysql可能要好一点,mssql本来就是一个文件,基本上提升不大,目前来看几十万的数据没有分不分表没有任何影响,对于千万以上数据还是采用水平分表比较好。

Posted in SQL Server onApril 16, 2021

而 垂直分表 则相对很少见到和用到,因为这可能是数据库设计上的问题了。如果数据库中一张表有部分字段几乎从不不更改但经常查询,而部分字段的数据频繁更改,这种设计放到同一个表中就不合理了,相互影响太大了。在已存在改情况的表的时候,可以考虑按列拆分表,即垂直拆分。

由于垂直分表的案例比较少,最近因为存在这样的表,所以个人捣鼓了一下。

源表设计结构:

--  源表
CREATE TABLE [dbo].[DemoTab](
[Guid] [uniqueidentifier] NOT NULL,
[UserName] [nvarchar](30) NOT NULL,
[Password] [nvarchar](30) NOT NULL,
[UserAccount] [varchar](30) NOT NULL,
[Amount] [numeric](18, 4) NULL,
CONSTRAINT [PK_DemoTab] PRIMARY KEY CLUSTERED ([Guid])
)
GO
 
 
ALTER TABLE [dbo].[DemoTab] 
ADD CONSTRAINT [DF_DemoTab_Guid] DEFAULT (newsequentialid()) FOR [Guid]
GO
 
--  原来是访问视图的(好处就是视图层不变)
CREATE VIEW [dbo].[VDemoTab]
AS
SELECT [Guid],[UserName],[Password],[UserAccount],[Amount]
FROM [dbo].[DemoTab]
GO

注:拆分后各表的主键都是相同了,而且拆分后的表是规范化的。

现在拆成两张表:

注意选择一张表作为基表,其他表都有与该表的外键。

--  分表【1】,以该表为"主表",其他拆分出的表为"子表"
CREATE TABLE [dbo].[DemoTab001](
[Guid] [uniqueidentifier] NOT NULL,
[UserName] [nvarchar](30) NOT NULL,
[Password] [nvarchar](30) NOT NULL,
CONSTRAINT [PK_DemoTab001] PRIMARY KEY CLUSTERED ([Guid])
)
GO
 
--  主键默认值可以不需要,因为插入数据前需要确定主键值
--ALTER TABLE [dbo].[DemoTab001] 
--ADD CONSTRAINT [DF_DemoTab001_Guid] DEFAULT (newsequentialid()) FOR [Guid]
--GO
 
--  分表【2】,"子表"
CREATE TABLE [dbo].[DemoTab002](
[Guid] [uniqueidentifier] NOT NULL,
[UserAccount] [varchar](30) NOT NULL,
[Amount] [numeric](18, 4) NULL,
CONSTRAINT [PK_DemoTab002] PRIMARY KEY CLUSTERED ([Guid])
)
GO
 
--  主键默认值可以不需要,因为插入数据前需要确定主键值
--ALTER TABLE [dbo].[DemoTab002] 
--ADD CONSTRAINT [DF_DemoTab002_Guid] DEFAULT (newsequentialid()) FOR [Guid]
--GO
 
 
--  若主表变更主键则级联更新或删除(主键通常是不更新的,也可省去 ON UPDATE CASCADE)
ALTER TABLE [dbo].[DemoTab002] 
ADD CONSTRAINT [FK_DemoTab002_DemoTab001_Guid] FOREIGN KEY ([Guid]) 
REFERENCES [DemoTab001]([Guid]) ON UPDATE CASCADE ON DELETE CASCADE
GO

如果之前是对单个表或者视图操作,拆分之后逻辑层改动可能很多,为保持改动最小,可以用联合视图操作。怎么连接表依个人情况而定。

--  拆分后使用联合视图(INNER JOIN 也可以)
ALTER VIEW [dbo].[VDemoTab]
AS
SELECT T1.[Guid],T1.[UserName],T1.[Password],T2.[UserAccount],T2.[Amount]
FROM [dbo].[DemoTab001] T1 LEFT JOIN [dbo].[DemoTab002] T2 ON T1.[Guid]=T2.[Guid]
GO

这时问题来了,要对表进行DML操作,insert , update , delete 怎么解决?因为要求主键是分散在多个表并且是相同的!

这时只能用考虑触发器来保证一致性了,触发器则定义在视图上,使用的是 INSTEAD OF 类型的触发器。

insert 触发器:

视图 [VDemoTab] 中的 [Guid] 为表 插入时值,在插入触发器中,虚拟表[inserted]的[Guid]是唯一的,所以在触发器中可以同时使用该 [Guid] 插入到多个分表中,保证了多个分表的[Guid]是相同的!

--  insert 触发器
CREATE TRIGGER [dbo].[tgr_VDemoTab_insert]
ON [dbo].[VDemoTab] 
INSTEAD OF INSERT
AS 
BEGIN
 INSERT INTO [dbo].[DemoTab001]([Guid],[UserName],[Password])
 SELECT [Guid],[UserName],[Password] FROM inserted;
 
 INSERT INTO [dbo].[DemoTab002]([Guid],[UserAccount],[Amount])
 SELECT [Guid],[UserAccount],[Amount] FROM inserted;
END
GO

update 触发器:

同理,更新时涉及虚拟表 deleted 和 inserted,而更新是对视图[VDemoTab]更新的,所以虚拟表inserted包括了所有的字段,所以需要触发器分别更新多个分表。

--  update 触发器
CREATE TRIGGER [dbo].[tgr_VDemoTab_update]  
ON [dbo].[VDemoTab]   
INSTEAD OF UPDATE  
AS
BEGIN
 UPDATE T1 SET 
 T1.[UserName] = T2.[UserName], 
 T1.[Password] = T2.[Password]
 FROM [dbo].[DemoTab001] AS T1, inserted AS T2 WHERE T1.[Guid] = T2.[Guid] 
 
 UPDATE T1 SET 
 T1.[UserAccount] = T2.[UserAccount], 
 T1.[Amount] = T2.[Amount]
 FROM [dbo].[DemoTab002] AS T1, inserted AS T2 WHERE T1.[Guid] = T2.[Guid] 
END
GO

delete 触发器:

删除视图[VDemoTab]记录,涉及多个表则不允许删除,因此只要删除"主表"的记录即可,其他分表都会级联删除。

--  delete 触发器
CREATE TRIGGER [dbo].[tgr_VDemoTab_delete]  
ON [dbo].[VDemoTab]   
INSTEAD OF DELETE  
AS
BEGIN
    DELETE FROM [dbo].[DemoTab001]
    WHERE [Guid] IN (SELECT [Guid] FROM deleted)
END
GO

设计基本就完成了,现在进行测试。

INSERT INTO [dbo].[VDemoTab]([Guid],[UserName],[Password],[UserAccount],[Amount])
SELECT NEWID(),'user01','pw01','account01',100
UNION ALL
SELECT NEWID(),'user02','pw02','account02',99
UNION ALL
SELECT NEWID(),'user03','pw03','account03',0
GO
 
UPDATE [VDemoTab] SET [Password]='pw',[Amount]='10'
WHERE [Amount] >=0 AND [Amount]<100 AND [UserName] LIKE '%3'
GO
 
DELETE FROM [VDemoTab] WHERE [UserName] = 'user03'
GO
 
SELECT * FROM [dbo].[DemoTab001] 
SELECT * FROM [dbo].[DemoTab002] 
SELECT * FROM [dbo].[VDemoTab]

基本操作都是正常的!垂直分表完成!

性能怎么样呢?

由于 Guid 作为主键,使用的是 NEWID() 而不是  NEWSEQUENTIALID(),新增记录时聚集索引都可能重新排序较多数据。

分表之后,单个数据页能存储的数据更多了,但是分成多个表中,数据页也增多了,同时 Guid 在每个表都存在,所以查询数据时IO会更多。

对于更新数据,在触发器中是两个表同时更新的,即使更新其中一个分表,其他分表都会影响。如果分表之后不同时更新,可以在触发器中使用 if(update(col)) 来判断更新的是那一列,就更新相应的基表就行,其他分表不更新。

最好的情况就是,拆分后的表都是“独立”的,不用联合视图,查询和更改都独立,这需要更改逻辑层。

到此这篇关于SqlServer 垂直分表(减少程序改动)的文章就介绍到这了,更多相关SqlServer 垂直分表内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

SQL Server 相关文章推荐
SQL Server——索引+基于单表的数据插入与简单查询【1】
Apr 05 SQL Server
SQLServer2019 数据库的基本使用之图形化界面操作的实现
Apr 08 SQL Server
SQLServer2008提示评估期已过解决方案
Apr 12 SQL Server
SQL Server2019数据库之简单子查询的具有方法
Apr 27 SQL Server
SQLServer中JSON文档型数据的查询问题解决
Jun 27 SQL Server
使用SQL实现车流量的计算的示例代码
Feb 28 SQL Server
通过T-SQL语句创建游标与实现数据库加解密功能
Mar 16 SQL Server
SQL Server实现分页方法介绍
Mar 16 SQL Server
SQL Server 忘记密码以及重新添加新账号
Apr 26 SQL Server
SQL Server中的游标介绍
May 20 SQL Server
SQL Server中锁的用法
May 20 SQL Server
SQL Server删除表中的重复数据
May 25 SQL Server
sqlserver2017共享功能目录路径不可改的解决方法
SQLServer2008提示评估期已过解决方案
SQLServer2019 数据库的基本使用之图形化界面操作的实现
SQLServer2019 数据库环境搭建与使用的实现
SQLServer 日期函数大全(小结)
Apr 08 #SQL Server
SQL SERVER中常用日期函数的具体使用
SQL Server连接查询的实用教程
You might like
PHP 输出缓存详解
2009/06/20 PHP
php获取网卡的MAC地址支持WIN/LINUX系统
2014/04/30 PHP
PHP中绘制图像的一些函数总结
2014/11/19 PHP
thinkPHP中volist标签用法示例
2016/12/06 PHP
php7 安装yar 生成docker镜像
2017/05/09 PHP
php压缩文件夹最新版
2018/07/18 PHP
通过javascript设置css属性的代码
2009/12/28 Javascript
dtree 网页树状菜单及传递对象集合到js内,动态生成节点
2012/04/14 Javascript
在父页面调用子页面的JS方法
2013/09/29 Javascript
JQuery中serialize()用法实例分析
2015/02/06 Javascript
AngularJS中的API(接口)简单实现
2016/07/28 Javascript
JavaScript条件判断_动力节点Java学院整理
2017/06/26 Javascript
webpack构建vue项目的详细教程(配置篇)
2017/07/17 Javascript
使用Bootrap和Vue实现仿百度搜索功能
2017/10/26 Javascript
最新Javascript程序员面试试题和解题方法
2017/11/23 Javascript
JavaScript 替换所有匹配内容及正则替换方法
2020/02/12 Javascript
Vue ElementUI实现:限制输入框只能输入正整数的问题
2020/07/31 Javascript
[46:20]DOTA2-DPC中国联赛 正赛 PSG.LGD vs LBZS BO3 第二场 1月22日
2021/03/11 DOTA
Python图形绘制操作之正弦曲线实现方法分析
2017/12/25 Python
python 接口测试response返回数据对比的方法
2018/02/11 Python
Python实现监控键盘鼠标操作示例【基于pyHook与pythoncom模块】
2018/09/04 Python
Python线程池模块ThreadPoolExecutor用法分析
2018/12/28 Python
python绘制漏斗图步骤详解
2019/03/04 Python
Django CSRF跨站请求伪造防护过程解析
2019/07/31 Python
sklearn+python:线性回归案例
2020/02/24 Python
使用 css3 实现圆形进度条的示例
2017/07/05 HTML / CSS
德国奢侈品网上商城:Mytheresa
2016/08/24 全球购物
加拿大廉价机票预订网站:CheapOair.ca
2018/03/04 全球购物
个人简历自我鉴定
2013/10/11 职场文书
大学生毕业自我评价范文分享
2013/11/11 职场文书
优秀生推荐信范文
2013/11/28 职场文书
小学红领巾中秋节广播稿
2014/01/13 职场文书
保卫科工作岗位职责
2014/03/01 职场文书
学雷锋志愿者活动方案
2014/08/21 职场文书
超市督导岗位职责
2015/04/10 职场文书
新学期开学寄语2016
2015/12/04 职场文书