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 相关文章推荐
SqlServer: 如何更改表的文件组?(进而改变存储位置)
Apr 05 SQL Server
SQL Server代理:理解SQL代理错误日志处理方法
Jun 30 SQL Server
SQL Server中使用判断语句(IF ELSE/CASE WHEN )案例
Jul 07 SQL Server
SQL SERVER实现连接与合并查询
Feb 24 SQL Server
Sql Server之数据类型详解
Feb 28 SQL Server
sqlserver连接错误之SQL评估期已过的问题解决
Mar 23 SQL Server
MSSQL基本语法操作
Apr 11 SQL Server
SQLServer权限之只开启创建表权限
Apr 12 SQL Server
SQL使用复合索引实现数据库查询的优化
May 25 SQL Server
SQL解决未能删除约束问题drop constraint
May 30 SQL Server
SQL中的连接查询详解
Jun 21 SQL Server
sqlserver2017共享功能目录路径不可改的解决方法
SQLServer2008提示评估期已过解决方案
SQLServer2019 数据库的基本使用之图形化界面操作的实现
SQLServer2019 数据库环境搭建与使用的实现
SQLServer 日期函数大全(小结)
Apr 08 #SQL Server
SQL SERVER中常用日期函数的具体使用
SQL Server连接查询的实用教程
You might like
PHP获取网址的顶级域名函数代码
2012/09/24 PHP
PHP+JS实现大规模数据提交的方法
2015/07/02 PHP
JSON+JavaScript处理JSON的简单例子
2013/03/20 Javascript
JavaScript实现的in_array函数
2014/08/27 Javascript
JavaScript日期时间与时间戳的转换函数分享
2015/01/31 Javascript
基于jquery实现日历签到功能
2020/09/11 Javascript
vue2.0 根据状态值进行样式的改变展示方法
2018/03/13 Javascript
axios 处理 302 状态码的解决方法
2018/04/10 Javascript
解决vue数组中对象属性变化页面不渲染问题
2018/08/09 Javascript
angularjs1.5 组件内用函数向外传值的实例
2018/09/30 Javascript
vue基础之事件v-onclick=&quot;函数&quot;用法示例
2019/03/11 Javascript
使用React手写一个对话框或模态框的方法示例
2019/04/25 Javascript
jQuery实时统计输入框字数及限制
2020/06/24 jQuery
通过滑动翻页效果实现和移动端click事件问题
2021/01/26 Javascript
[01:02:00]DOTA2-DPC中国联赛 正赛 Elephant vs IG BO3 第三场 1月24日
2021/03/11 DOTA
python实现爬虫下载美女图片
2015/07/14 Python
Python+matplotlib实现填充螺旋实例
2018/01/15 Python
解决Matplotlib图表不能在Pycharm中显示的问题
2018/05/24 Python
python读取LMDB中图像的方法
2018/07/02 Python
python中数组和矩阵乘法及使用总结(推荐)
2019/05/18 Python
Python数据可视化 pyecharts实现各种统计图表过程详解
2019/08/15 Python
tensorflow生成多个tfrecord文件实例
2020/02/17 Python
浅谈python print(xx, flush = True) 全网最清晰的解释
2020/02/21 Python
python 动态绘制爱心的示例
2020/09/27 Python
纯css3实现效果超级炫的checkbox复选框和radio单选框
2014/09/01 HTML / CSS
Topshop法国官网:英国快速时尚品牌
2018/04/08 全球购物
Giglio俄罗斯奢侈品购物网:男士、女士、儿童高级时装
2018/07/27 全球购物
Tuckernuck官网:经典的美国品质服装、鞋子和配饰
2021/01/11 全球购物
如何写出好的Java代码
2014/04/25 面试题
应届生求职推荐信
2013/10/28 职场文书
信息专业本科生个人的自我评价
2013/10/28 职场文书
2014年计生标语
2014/06/23 职场文书
学生会竞选演讲稿怎么写
2014/08/26 职场文书
餐厅感恩节活动策划方案
2014/10/11 职场文书
涨价通知怎么写
2015/04/23 职场文书
管理者日常工作必备:22条企业管理流程模板!
2019/07/12 职场文书