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
SQL SERVER中常用日期函数的具体使用
Apr 08 SQL Server
SQL Server2019数据库之简单子查询的具有方法
Apr 27 SQL Server
SQL Server作业失败:无法确定所有者是否有服务器访问权限的解决方法
Jun 30 SQL Server
SQL Server2019数据库备份与还原脚本,数据库可批量备份
Nov 20 SQL Server
SQL SERVER存储过程用法详解
Feb 24 SQL Server
通过T-SQL语句创建游标与实现数据库加解密功能
Mar 16 SQL Server
MSSQL基本语法操作
Apr 11 SQL Server
SQL Server中的游标介绍
May 20 SQL Server
SQL Server删除表中的重复数据
May 25 SQL Server
SQL解决未能删除约束问题drop constraint
May 30 SQL Server
sqlserver2017共享功能目录路径不可改的解决方法
SQLServer2008提示评估期已过解决方案
SQLServer2019 数据库的基本使用之图形化界面操作的实现
SQLServer2019 数据库环境搭建与使用的实现
SQLServer 日期函数大全(小结)
Apr 08 #SQL Server
SQL SERVER中常用日期函数的具体使用
SQL Server连接查询的实用教程
You might like
留言板翻页的实现详解
2006/10/09 PHP
php中$_REQUEST、$_POST、$_GET的区别和联系小结
2011/11/23 PHP
Notice: Trying to get property of non-object problem(PHP)解决办法
2012/03/11 PHP
PHP zip扩展Linux下安装过程分享
2014/05/05 PHP
七种PHP开发环境搭建工具
2020/06/28 PHP
关于JavaScript与HTML的交互事件
2013/04/12 Javascript
js实现瀑布流的一种简单方法实例分享
2013/11/04 Javascript
JavaScript实现简单图片滚动附源码下载
2014/06/17 Javascript
Bootstrap每天必学之简单入门
2015/11/19 Javascript
jQuery自动完成插件completer附源码下载
2016/01/04 Javascript
第五篇Bootstrap 排版
2016/06/21 Javascript
echarts多条折线图动态分层的实现方法
2019/05/24 Javascript
微信小程序自定义navigationBar顶部导航栏适配所有机型(附完整案例)
2020/04/26 Javascript
vue在App.vue文件中监听路由变化刷新页面操作
2020/08/14 Javascript
浅析Python的Django框架中的Memcached
2015/07/23 Python
python 转换 Javascript %u 字符串为python unicode的代码
2016/09/06 Python
详解python3实现的web端json通信协议
2016/12/29 Python
python运行其他程序的实现方法
2017/07/14 Python
Python中关键字global和nonlocal的区别详解
2018/09/03 Python
解决pycharm运行时interpreter为空的问题
2018/10/29 Python
在双python下设置python3为默认的方法
2018/10/31 Python
Python常用爬虫代码总结方便查询
2019/02/25 Python
python使用协程实现并发操作的方法详解
2019/12/27 Python
python使用numpy实现直方图反向投影示例
2020/01/17 Python
如何真正的了解python装饰器
2020/08/14 Python
Scrapy-Redis之RedisSpider与RedisCrawlSpider详解
2020/11/18 Python
使用Python判断一个文件是否被占用的方法教程
2020/12/16 Python
英国领先的高街书籍专家:Waterstones
2018/02/01 全球购物
编写strcpy函数
2014/06/24 面试题
学校介绍信范文
2014/01/14 职场文书
《骑牛比赛》教后反思
2014/04/22 职场文书
忠诚与背叛观后感
2015/06/04 职场文书
大学生村官驻村工作心得体会
2016/01/23 职场文书
Go语言-为什么返回值为接口类型,却返回结构体
2021/04/24 Golang
quickjs 封装 JavaScript 沙箱详情
2021/11/02 Javascript
Eclipse+Java+Swing+Mysql实现电影购票系统(详细代码)
2022/01/18 Java/Android