SQL Server 冗余维护
介绍
冗余是维护的魔鬼, 是性能优化的天使
常见的冗余有
1. computed column
2. principal 的识别字段
3. cross computed
4. cascade soft delete
维护冗余的方案有很多. 比如 computed column, trigger, view, 甚至在应用层写 event bus.
但不同情况利弊也不同. 还得看场景决定.
我目前使用 computed column 和 trigger 来维护冗余.
对比在应用层维护, 好处是可以直接修改 SQL, 冗余一样可以正常 working (在业务还不稳定的情况下, 直接使用数据库来做信息管理可以提高效率和节约试错成本)
另一个好处是不需要在应用层额外的开发一套维护方案, 要知道 EF core 并没有现成的方案,甚至连 trigger 机制都没有 build-in 的.
Computed Column Same Row
比如 Subtotal, TotalAmount 这类的字段.
比较简单的 computed column 是依赖同一个 row 里面的字段, 比如 FullName, Subtotal
ALTER TABLE InvoiceItem DROP COLUMN Subtotal;
GO
ALTER TABLE InvoiceItem ADD Subtotal as (CAST(Qty as DECIMAL(19)) * UnitPrice) PERSISTED NOT NULL;
GO
用普通的 computed column 就可以解决了, 只能依赖同行, 而且依赖的字段不可以是 computed column
Cross Table Computed Column
如果需要跨表, 比如 TotalAmount 要 SUM 子表.
就要使用 Trigger 监听所有依赖字段, 然后重新跑 Computed 方法.
例子:
GO
CREATE OR ALTER TRIGGER TR_Contract_AfterInsert_ForCrossComputed_Project_ProjectBiddingCost ON [Contract]
AFTER INSERT
AS
IF (ROWCOUNT_BIG() = 0) RETURN;
SET NOCOUNT ON;
UPDATE [ParentTable] SET [ProjectBiddingCost] = ISNULL(
(SELECT SUM([ContractBiddingCost]) FROM [Contract]
WHERE [ProjectId] = [ParentTable].[ProjectId] AND (([Deleted] = 0))), 0
)
FROM [Project] AS [ParentTable]
INNER JOIN inserted ON [ParentTable].[ProjectId] = inserted.[ProjectId];
GO GO
CREATE OR ALTER TRIGGER TR_Contract_AfterDelete_ForCrossComputed_Project_ProjectBiddingCost ON [Contract]
AFTER DELETE
AS
IF (ROWCOUNT_BIG() = 0) RETURN;
SET NOCOUNT ON;
UPDATE [ParentTable] SET [ProjectBiddingCost] = ISNULL(
(SELECT SUM([ContractBiddingCost]) FROM [Contract]
WHERE [ProjectId] = [ParentTable].[ProjectId] AND (([Deleted] = 0))), 0
)
FROM [Project] AS [ParentTable]
INNER JOIN deleted ON [ParentTable].[ProjectId] = deleted.[ProjectId];
GO GO
CREATE OR ALTER TRIGGER TR_Contract_AfterUpdate_ForCrossComputed_Project_ProjectBiddingCost ON [Contract]
AFTER UPDATE
AS
IF (ROWCOUNT_BIG() = 0) RETURN;
SET NOCOUNT ON;
UPDATE [ParentTable] SET [ProjectBiddingCost] = ISNULL(
(SELECT SUM([ContractBiddingCost]) FROM [Contract]
WHERE [ProjectId] = [ParentTable].[ProjectId] AND (([Deleted] = 0))), 0
)
FROM deleted
INNER JOIN inserted ON deleted.[ContractId] = inserted.[ContractId]
INNER JOIN [Project] AS [ParentTable] ON deleted.[ProjectId] = [ParentTable].[ProjectId] OR inserted.[ProjectId] = [ParentTable].[ProjectId]
WHERE (((deleted.[Deleted] <> inserted.[Deleted]) OR (deleted.[Deleted] IS NULL OR inserted.[Deleted] IS NULL)) AND (deleted.[Deleted] IS NOT NULL OR inserted.[Deleted] IS NOT NULL))
OR (((deleted.[ProjectId] <> inserted.[ProjectId]) OR (deleted.[ProjectId] IS NULL OR inserted.[ProjectId] IS NULL)) AND (deleted.[ProjectId] IS NOT NULL OR inserted.[ProjectId] IS NOT NULL))
OR (((deleted.[ContractBiddingCost] <> inserted.[ContractBiddingCost]) OR (deleted.[ContractBiddingCost] IS NULL OR inserted.[ContractBiddingCost] IS NULL)) AND (deleted.[ContractBiddingCost] IS NOT NULL OR inserted.[ContractBiddingCost] IS NOT NULL));
GO
Principal Table 识别字段
比如 Name, Code, Number 之类的. 由于 foreign table 是依靠 Id 作为 foreign key, 而 Id 对业务来说不具备识别能力, 所以一般上会需要一些识别字段
每次 join table 获取识别字段对性能很伤, 语句也不好了, 所以就有了 Principal 识别字段的冗余.
同样可以使用 Trigger 来维护
例子:
GO
CREATE OR ALTER TRIGGER [TR_PaymentInvoice_AfterInsert_ForPrincipalProperty_PaymentInvoice_ProjectNumber] ON [PaymentInvoice]
AFTER INSERT
AS
IF (ROWCOUNT_BIG() = 0) RETURN;
SET NOCOUNT ON; UPDATE [PaymentInvoice] SET [ProjectNumber] = [PurchaseOrder].[ProjectNumber]
FROM [PaymentInvoice]
INNER JOIN inserted ON [PaymentInvoice].[PurchaseOrderId] = [inserted].[PurchaseOrderId]
INNER JOIN [PurchaseOrder] ON [PaymentInvoice].[PurchaseOrderId] = [PurchaseOrder].[PurchaseOrderId];
GO GO
CREATE OR ALTER TRIGGER [TR_PaymentInvoice_AfterUpdate_ForPrincipalProperty_PaymentInvoice_ProjectNumber] ON [PaymentInvoice]
AFTER Update
AS
IF (ROWCOUNT_BIG() = 0) RETURN;
SET NOCOUNT ON; UPDATE [PaymentInvoice] SET [ProjectNumber] = [PurchaseOrder].[ProjectNumber]
FROM deleted
INNER JOIN inserted ON deleted.[PaymentInvoiceId] = inserted.[PaymentInvoiceId]
INNER JOIN [PurchaseOrder] ON [inserted].[PurchaseOrderId] = [PurchaseOrder].[PurchaseOrderId]
WHERE (((deleted.[PurchaseOrderId] <> inserted.[PurchaseOrderId]) OR (deleted.[PurchaseOrderId] IS NULL OR inserted.[PurchaseOrderId] IS NULL)) AND (deleted.[PurchaseOrderId] IS NOT NULL OR inserted.[PurchaseOrderId] IS NOT NULL));
GO GO
CREATE OR ALTER TRIGGER [TR_PurchaseOrder_AfterUpdate_ForPrincipalProperty_PaymentInvoice_ProjectNumber] ON [PurchaseOrder]
AFTER UPDATE
AS
IF (ROWCOUNT_BIG() = 0) RETURN;
SET NOCOUNT ON; UPDATE [PaymentInvoice] SET [ProjectNumber] = inserted.[ProjectNumber]
FROM deleted
INNER JOIN inserted ON deleted.[PurchaseOrderId] = inserted.[PurchaseOrderId]
INNER JOIN [PaymentInvoice] ON [inserted].[PurchaseOrderId] = [PaymentInvoice].[PurchaseOrderId]
WHERE (((deleted.[ProjectNumber] <> inserted.[ProjectNumber]) OR (deleted.[ProjectNumber] IS NULL OR inserted.[ProjectNumber] IS NULL)) AND (deleted.[ProjectNumber] IS NOT NULL OR inserted.[ProjectNumber] IS NOT NULL));
GO
Cascade soft delete
SQL Server 支持 Cascade delete, 但如果希望 soft delete 就没有 build-in 支持了.
用 trigger 也是可以解决
例子:
GO
CREATE OR ALTER TRIGGER [TR_TradeItem_AfterUpdate_ForCascadeSoftDelete_PurchaseRequisition] ON [TradeItem]
AFTER UPDATE
AS
IF (ROWCOUNT_BIG() = 0) RETURN;
SET NOCOUNT ON; UPDATE [PurchaseRequisition]
SET [DeletedBy] =
CASE
WHEN deleted.[DateDeleted] IS NULL AND inserted.[DateDeleted] IS NOT NULL
THEN
CASE
WHEN [PurchaseRequisition].[Deleted] = 1 THEN [PurchaseRequisition].[DeletedBy]
ELSE inserted.DeletedBy
END
ELSE
CASE
WHEN [PurchaseRequisition].[DateDeleted] = deleted.[DateDeleted] THEN NULL
ELSE [PurchaseRequisition].[DeletedBy]
END
END,
DateDeleted =
CASE
WHEN deleted.[DateDeleted] IS NULL AND inserted.[DateDeleted] IS NOT NULL
THEN
CASE
WHEN [PurchaseRequisition].[Deleted] = 1 THEN [PurchaseRequisition].[DateDeleted]
ELSE inserted.[DateDeleted]
END
ELSE
CASE
WHEN [PurchaseRequisition].[DateDeleted] = deleted.[DateDeleted] THEN NULL
ELSE [PurchaseRequisition].[DateDeleted]
END
END
FROM deleted
INNER JOIN inserted
ON deleted.[TradeItemId] = inserted.[TradeItemId]
INNER JOIN [PurchaseRequisition] ON inserted.[TradeItemId] = [PurchaseRequisition].[TradeItemId]
WHERE (((deleted.[DateDeleted] <> inserted.[DateDeleted]) OR (deleted.[DateDeleted] IS NULL OR inserted.[DateDeleted] IS NULL)) AND (deleted.[DateDeleted] IS NOT NULL OR inserted.[DateDeleted] IS NOT NULL));
GO
SQL Server 冗余维护的更多相关文章
- SQL Server 索引维护(1)——系统常见的索引问题
前言: 在很多系统中,比如本人目前管理的数据库,索引经常被滥用,甚至使用DTA(数据库引擎优化顾问)来成批创建索引(DTA目前个人认为它的真正用处应该是在发现缺失的统计信息,在以前的项目中,用过一次D ...
- SQL Server 日常维护经典应用
SQL Server日常维护常用的一些脚本整理. 1.sql server开启clr权限: GO RECONFIGURE GO ALTER DATABASE HWMESTC SET TRUSTWORT ...
- SQL Server 索引维护:系统常见的索引问题
在很多系统中,比如本人目前管理的数据库,索引经常被滥用,甚至使用DTA(数据库引擎优化顾问)来成批创建索引(DTA目前个人认为它的真正用处应该是在发现缺失的统计信息,在以前的项目中,用过一次DTA,里 ...
- SQL Server 索引维护(1)——如何获取索引使用情况
前言: 在前面一文中,已经提到了三类常见的索引问题,那么问题来了,当系统出现这些问题时,该如何应对? 简单而言,需要分析现有系统的行为,然后针对性地对索引进行处理: 对于索引不足的情况:检查缺少索引的 ...
- SQL Server索引维护
索引维护的两个重要方面是索引碎片和统计信息. 一:索引碎片 降低碎片的产生,当索引上的页不在具有物理连续性时,就会产生碎片,下面的情景会产生碎片: INSERT操作.UPDATE操作.DBCC SHR ...
- SQL Server 索引维护sql语句
使用以下脚本查看数据库索引碎片的大小情况: 复制代码代码如下: DBCC SHOWCONTIG WITH FAST, TABLERESULTS, ALL_INDEXES, NO_INFOMSGS 以 ...
- SQL Server 日常维护--查询当前正在执行的语句、死锁、堵塞
查询当前正在执行的语句: SELECT der.[session_id],der.[blocking_session_id], sp.lastwaittype,sp.hostname,sp.progr ...
- SQL SERVER 索引维护
-- 全数据库索引重建 DECLARE @name varchar(100)DECLARE authors_cursor CURSOR FOR Select [name] from sysobject ...
- SQL Server调优系列进阶篇(如何维护数据库索引)
前言 上一篇我们研究了如何利用索引在数据库里面调优,简要的介绍了索引的原理,更重要的分析了如何选择索引以及索引的利弊项,有兴趣的可以点击查看. 本篇延续上一篇的内容,继续分析索引这块,侧重索引项的日常 ...
- SQL Server调优系列进阶篇 - 如何维护数据库索引
前言 上一篇我们研究了如何利用索引在数据库里面调优,简要的介绍了索引的原理,更重要的分析了如何选择索引以及索引的利弊项,有兴趣的可以点击查看. 本篇延续上一篇的内容,继续分析索引这块,侧重索引项的日常 ...
随机推荐
- [oeasy]python0020换行字符_feed_line_lf_反斜杠n_B语言_安徒生童话
换行字符 回忆上次内容 struct包可以让我们使用封包格式 把数字封包到字节里 pack函数负责封包 unpack函数负责解封 我们通过封到不同的字节状态 遍历了一次ascii码 编辑 还是 ...
- [oeasy]python0011_ 字符序号_ordinal_ord
序号(ordinal) 回忆上次内容 hello world 不是从来就有的 来自于unix和c 虽然我们今天有各种先进的学习手段 最早的高级语言学习是从最早的那张打字机用纸 ...
- 只会建数据库怎么写API?database2api 能帮到你!
database2api 意为 DataBase to API,即只要有数据库,就可以生成开放 API. database2api 是一款强大而便捷的工具,主要功能是依据现有的数据库自动生成开放的 A ...
- BI 工具如何助力市政设计公司实现数字化转型?
一.前言 近年来,国家出台多个政策文件来鼓励和发展数字化和智能化,如<十四五规划>提出要推进产业数字化转型.<交通强国建设纲要>提出要大力发展智慧交通.上海市发布的<关于 ...
- Python列表、元组、字典和集合的用法
1.列表 标志符号是[],元素可以修改.删除和新增 1.1 提取元素(索引从0开始计算) testList=['A','B','C',1,'D'] print(testList[1]) #打印索引区间 ...
- Tomcat日志信息有乱码的处理方法
1.问题描述 1.1.Idea中的tomcat日志有乱码 1.2.直接启动tomcat的日志有乱码 1.3.原因分析 问题是由于tomcat使用的编码和操作系统使用的编码不一致导致: Windows1 ...
- mujoco_py 运行example报错:ERROR: GLEW initalization error: Missing GL version ——— 解决方法
mujoco的安装与mujoco_py的安装参见: https://www.cnblogs.com/devilmaycry812839668/p/16004320.html mujoco_py安装成功 ...
- 再探 游戏 《 2048 》 —— AI方法—— 缘起、缘灭(7) —— Python版本实现的《2048》游戏的TDL算法
<2048>游戏在线试玩地址: https://play2048.co/ 如何解决<2048>游戏源于外网的一个讨论帖子,而这个帖子则是讨论如何解决该游戏的最早开始,可谓是&q ...
- python调用第三方java包实例
先看结果: 对于python与java的互调,我一开始是用的py4j,但是后来发现在使用方法的时候,不知道如何在python中导入jar包,然后网上的资料也比较少.后来想不出来办法,又看到有Jpype ...
- 告别卡顿,畅享GitHub:国内开发者必看的五大加速访问与下载技巧
告别卡顿,畅享GitHub:国内开发者必看的五大加速访问与下载技巧 本文介绍了五种加速在国内访问和下载 GitHub 的方法,包括:使用 Gitee 平台加速克隆代码.修改 hosts 文件.使用油猴 ...