SqlServer 垂直分表
当单表数据太多时。我们能够水平划分,參考 SqlServer 分区视图实现水平分表 ,水平划分能够提高表的一些性能。
而 垂直分表 则相对非常少见到和用到,由于这可能是数据库设计上的问题了。假设数据库中一张表有部分字段差点儿从不不更改但常常查询,而部分字段的数据频繁更改。这样的设计放到同一个表中就不合理了,相互影响太大了。在已存在改情况的表的时候,能够考虑按列拆分表,即垂直拆分。
由于垂直分表的案例比較少,近期由于存在这种表,所以个人捣鼓了一下。
源表设计结构:
-- 源表
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)) 来推断更新的是那一列,就更新对应的基表即可,其它分表不更新。
最好的情况就是,拆分后的表都是“独立”的。不用联合视图,查询和更改都独立,这须要更改逻辑层。
本文出自“Hello.KK (SQL Server)”的博客,转载请务必保留此出处http://blog.csdn.net/kk185800961/article/details/46740315
SqlServer 垂直分表的更多相关文章
- SqlServer垂直分表 如何减少程序改动
当单表数据太多时,我们可以水平划分,参考 SqlServer 分区视图实现水平分表 ,水平划分可以提高表的一些性能. 而 垂直分表 则相对很少见到和用到,因为这可能是数据库设计上的问题了.如果数据库中 ...
- mysql中的优化, 简单的说了一下垂直分表, 水平分表(有几种模运算),读写分离.
一.mysql中的优化 where语句的优化 1.尽量避免在 where 子句中对字段进行表达式操作select id from uinfo_jifen where jifen/60 > 100 ...
- mysql数据库的水平分表与垂直分表实例讲解
mysql语句的优化有局限性,mysql语句的优化都是围绕着索引去优化的,那么如果mysql中的索引也解决不了海量数据查询慢的状况,那么有了水平分表与垂直分表的出现(我就是记录一下自己的理解) 水平分 ...
- (转)mysql水平分表和垂直分表和数据库分区
坚信数据库的物理设计在对高级数据库的性能影响上远比其他因素重要.给大家说一下经过专家对Oracle的研究,他们解释了为什么拙劣的物理设计是数据库停机(无论是有计划的还是没计划的)背后的主要原因.但在这 ...
- mysql水平分表和垂直分表的优缺点
表分割有两种方式: 1.水平分割:根据一列或多列数据的值把数据行放到两个独立的表中. 水平分割通常在下面的情况下使用. •表很大,分割后可以降低在查询时需要读的数据和索引的页数,同时也降低了索引的层数 ...
- SQLserver分库分表
https://blog.csdn.net/ExceptionalBoy/article/details/78851327
- MySQL 分库分表及其平滑扩容方案
转自:https://kefeng.wang/2018/07/22/mysql-sharding/ 众所周知,数据库很容易成为应用系统的瓶颈.单机数据库的资源和处理能力有限,在高并发的分布式系统中,可 ...
- 采用Sharding-JDBC解决分库分表
源码:Sharding-JDBC(分库分表) 一.Sharding-JDBC介绍 1,介绍 Sharding-JDBC是当当网研发的开源分布式数据库中间件,从 3.0 开始Sharding-JDBC被 ...
- 分库分表ShardingSphere-JDBC笔记整理
一.分库分表解决的现状问题 解决数据库本身瓶颈 连接数: 连接数过多时,就会出现'too many connections'的错误,访问量太大或者数据库设置的最大连接数太小的原因 Mysql默认的最大 ...
随机推荐
- Strut 2 ValueStack传送带机制
源码与jar包下载(将rar改成jar,直接放在WEB_INF\lib目录中即可) 众所周知,Strut 2的Action类通过属性可以获得所有相关的值,如请求参数.Action配置参数.向其他 ...
- 错误:支持“EFDbContext”上下文的模型已在数据库创建后发生更改。请考虑使用 Code First 迁移更新数据库(http://go.microsoft.com/fwlink/?LinkId
支持"EFDbContext"上下文的模型已在数据库创建后发生更改.请考虑使用 Code First 迁移更新数据库(http://go.microsoft.com/fwlink/ ...
- 【bzoj4836】[Lydsy2017年4月月赛]二元运算 分治+FFT
题目描述 定义二元运算 opt 满足 现在给定一个长为 n 的数列 a 和一个长为 m 的数列 b ,接下来有 q 次询问.每次询问给定一个数字 c 你需要求出有多少对 (i, j) 使得 a_ ...
- 【bzoj4810】[Ynoi2017]由乃的玉米田 莫队算法+STL-bitset
题目描述 由乃在自己的农田边散步,她突然发现田里的一排玉米非常的不美.这排玉米一共有N株,它们的高度参差不齐. 由乃认为玉米田不美,所以她决定出个数据结构题 这个题是这样的: 给你一个序列a,长度为n ...
- Java简明教程 11.异常
异常的感性认识 在没有异常机制的语言中,经常通过返回值来表示调用该函数产生的各种问题(异常),比如c语言. divide.c #include <stdio.h> int main() { ...
- Codeforces Round #364 (Div. 2) A 水
A. Cards time limit per test 1 second memory limit per test 256 megabytes input standard input outpu ...
- poj 3053 优先队列处理
Fence Repair Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 39029 Accepted: 12681 De ...
- 【08】Vue 之 vue-cli
8.1. 前置知识学习 npm 学习 官方文档 推荐资料 npm入门 npm介绍 需要了解的知识点 package.json 文件相关配置选项 npm 本地安装.全局安装.本地开发安装等区别及相关命令 ...
- cf 341D lahub and xors
题目大意 给定初始值为\(0\)的\(n*n\)矩阵 两种操作 矩形内异或一个值 求矩阵内异或和 \(n\le 1000\) 分析 二维线段树标记不下传貌似直接可做 有没有更简便的方法? 考虑异或的特 ...
- shell for参数
关于shell中的for循环用法很多,一直想总结一下,今天网上看到上一篇关于for循环用法的总结,感觉很全面,所以就转过来研究研究1. for((i=1;i<=10;i++));do echo ...