带你深入了解T-SQL的十一种设计模式

2024-04-04 07:32

本文主要是介绍带你深入了解T-SQL的十一种设计模式,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、ITERATOR(迭代)

这种模式提供一种在相似对象列表中遍历对象的标准化方法。在SQL Server数据库中的同义词是游标。

DECLARE tables CURSOR

FOR SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES

FOR READ ONLY

DECLARE @table varchar(40)

OPEN tables

FETCH tables INTO @table

WHILE (@@FETCH_STATUS = 0)

BEGIN

EXEC sp_help @table

FETCH tables INTO @table

END

CLOSE tables

DEALLOCATE tables

注:游标的清理代码:在CLOSE后紧跟DEALLOCATE,实际上可以只运行DEALLOCATE,并且游标也能自动关闭。但这不是最自然,也不是最常见的方法。大家可以理解为:CLOSE抵消OPEN,DEALLOCATE与DECLARE则相反,这样可以使代码保持对称并且合乎逻辑。

二、INTERSECTOR(交集)

这种模式是表示集合交集的一种模板。

1、推荐方法:

SELECT c.companyname,o.orderid

FROM customer c INNER JOIN orders o ON c.customerid = o.customerid

2、旧式语法(不推荐使用)

SELECT c.companyname,o.orderid

FROM customer c ,orders o

WHERE c.customerid = o.customerid

注:实现集合交集还有许多变种方法。但是惯例方法就是方法1,方法2在实现左(右)联接时,条件的表示及结果都可能出现问题,SQL SERVER的后续版本将会取消此种联接方式。

三、QUALIFIER(限定)

限定数据等价于筛选查询所返回的行数。

1、常用法:WHERE子句限定

SELECT city,count(*) AS NumberCity

FROM customers

WHERE city like 'A%'

GROUP BY city

2、不自然的筛选:HAVING子句限定

SELECT city,count(*) AS NumberCity

FROM customers

GROUP BY city

HAVING city like 'A%'

注:HAVING子句的目的是在结果集被检索出来后再筛选查询。实际上,SQL SERVER内在地转换HAVING子句为WHERE子句(两种方法查询的执行计划是相同的),如果SQL SERVER不执行此优化,则针对包含大量数据行的表,因需要在筛选前从表中检索所有行,则性能方面可能会遭受重大损失。

四、EXECTOR(运行)

提供创建并执行动态T-SQL字符串的模板

--中断除当前连接之外的所有用户连接

DECLARE @s int,@sql nvarchar(128)

DECLARE spids CURSOR FOR

SELECT spid

FROM master..sysprocesses

WHERE spid <> @@SPID AND net_address<>''

FOR READ ONLY

OPEN spids

FETCH spids INTO @s

WHILE (@@FETCH_STATUS = 0)

BEGIN

SET @sql = 'KILL ' + CAST(@s AS varchar)

EXEC sp_executesql @sql

FETCH spids INTO @s

END

CLOSE spids

DEALLOCATE spids

注:上述语句中的sp_executesql可以用exec()替换,但推荐使用sp_executesql,因为与exec()相比,sp_executesql支持参数化查询,并可从动态T-SQl调用返回一个结果代码。如果动态代码产生一个严重级达到或超过11的错误,sp_executesql将在它的结果代码中返回错误码。

 

五、Conveyor(传送)

提供一种通过存储过程链传送信息的机制。与GoF的责任链模式(Chain of Responsibility)相类似。

1、传送返回码

CREATE PROC procC

AS

IF OBJECT_ID('no_exist') IS NOT NULL

SELECT * FROM no_exist

ELSE

RETURN (-1)

GO

CREATE PROC procB

AS

DECLARE @res int

EXEC @res = procC

RETURN (@res)

GO

CREATE PROC procA

AS

DECLARE @res int

EXEC @res = procB

SELECT @res

GO

EXEC procA

注:上述代码使用了存储过程的结果代码从过程向过程传递原始返回码的方法,即A调用B,B又调用C,C运行时如出现了错误,则将错误代码-1传送给A。

2、通过输出参数传送消息

CREATE PROC procC

@msg varchar(128) OUT

AS

IF OBJECT_ID('no_exist') IS NOT NULL

SELECT * FROM no_exist

ELSE

SET @msg = 'Table dosen''t exist!'

GO

CREATE PROC procB

@msg varchar(128) OUT

AS

EXEC procC @msg OUT

GO

CREATE PROC procA

AS

DECLARE @msg varchar(128)

EXEC procB @msg OUT

SELECT @msg

GO

EXEC procA

注:可以使用任何数据类型(包括游标)来返回任何想要的信息

3、传送真实错误代码

CREATE PROC procC

AS

DECLARE @err int

IF @@TRANCOUNT = 0 --此全局变量返回当前连接的活动事务数

ROLLBACK TRAN --有意设置的出错语句,因未使用BEGIN TRANSACTION语句

SET @err = @@ERROR

RETURN (@err)

GO

CREATE PROC procB

AS

DECLARE @res int

EXEC @res = procC

RETURN (@res)

GO

CREATE PROC procA

AS

DECLARE @res int

EXEC @res = procB

SELECT @res

GO

EXEC procA

六、Restorer(恢复)

此模式提供一种在出错时清理资源的机制。为避免孤立一个事务,当事务活动时,适当地处理出错条件极其重要。

1、出错时回滚事务

IF OBJECT_ID('procR') IS NOT NULL

DROP PROC procR

GO

CREATE PROC procR

AS

DECLARE @err int

BEGIN TRAN

UPDATE customers SET city = 'Dallas'

SELECT 1/0 --设置一个错误

SET @err = @@ERROR

IF @err <> 0

BEGIN

ROLLBACK TRAN

RETURN (@err)

END

COMMIT TRAN

GO

DECLARE @res int

EXEC @res = procR

SELECT @res

注:此模式的关键部分是将错误码@@error缓存至变量@err中,如果不缓存@@error,下一执行成功的语句将重置@@error,缓存它后,如出现错误,将检查@errr的值并回滚该活动事务。

2、出错时清除临时表

CREATE PROC procR

AS

DECLARE @err int

CREATE TABLE ##myglobal(c1 int)

INSERT ##myglobal DEFAULT VALUES

SELECT 1/0 --设置一个错误

SET @err = @@ERROR

IF @err <> 0

BEGIN

DROP TABLE ##myglobal

RETURN (@err)

END

DROP TABLE ##myglobal

GO

DECLARE @res int

EXEC @res = procR

SELECT @res

3、主动执行恢复模式

CREATE PROC procR

AS

IF @@TRANCOUNT <> 0 --启动新事务前先回滚旧事务

ROLLBACK TRAN

DECLARE @err int

BEGIN TRAN

UPDATE customers SET city = 'Dallas'

SELECT 1/0 --设置一个错误

SET @err = @@ERROR

IF @err <> 0

BEGIN

ROLLBACK TRAN

RETURN (@err)

END

COMMIT TRAN

GO

DECLARE @res int

EXEC @res = procR

SELECT @res

注:通过@@TRANCOUNT<>0可知有活动事务,执行ROLLBACK回滚当前活动连接的所有事务。当SQL Server使用连接池时(对WEB服务器而言相当常见),

在实际应用中编写此种逻辑就非常重要。由于一个虚连接可以留下一个打开的事务,该事务会影响使用同一物理连接的后续用户,因此,通过主动地

执行Restorer模式,让代码知道如何保护自己免受“无赖”事务和其他意外残余的影响。

 

重要提示

T-SQL的错误处理结构也并非无懈可击,它经常不是按预期的方式或它应该的方法运行。例如,存在许多严重的足以中断当前命令批处理的错误,当这些错误出现时,它让那些可能紧跟在其后的错误处理代码根本没有机会去执行。因此,当出现问题时,即使使用@@ERROR执行代码检查并调用ROLLBACK,还会有错误禁止ROLLBACK执行。这可能是导致孤立事务存在的根本原因,而且也是在开始一个事务前应检查孤立事务的原因。 

七、PROTOTYPE(原型)

此模式的目标:使用一种原型实例指定要创建对象的类型,并且通过复制原型创建新的对象。

1、最常见的实现方式是SELECT...INTO结构

SELECT *

INTO newCustomers

FROM Customers

SELECT *

INTO newCustomers

FROM Customers

WHERE country='UK'

注:通过指定一个列列表、WHERE子句、GROUP BY或HAVING子句,可在传送过程中修改原型。

2、复制表结构(T-SQL惯例中也曾提过)

SELECT *

INTO newCustomers

FROM Customers

WHERE 1 = 2

SELECT TOP 0 *

INTO newCustomers

FROM Customers

注:通过错误的WHERE条件或不存在的行实现了复制表结构的功能

3、复制表时指定新数据

SELECT IDENTITY(int,1,1) AS CustNo,*

INTO newCustomers

FROM Customers

注:还可指定新列、通过联接选取来自其他表或视图的列、约束或函数等许多的可能性。

八、Singleton(单例)

此模式目标:确保在任何给定时间只存在一个类实例并且提供访问该实例的路径。

严格说,在关系数据库中,对于面向对象类的等价物是表。类的一个实例就是表中的一行。因此,Singleton模式的最显而意见的实现就是确保表中只包含一行。

CREATE TABLE LastCustNo

(LastCustNo int)

GO

INSERT LastCustNo VALUES(1)

GO

CREATE TRIGGER LastCustNoInsert ON LastCustNo

FOR INSERT

AS

IF (SELECT COUNT(*) FROM LastCustNo) > 1

BEGIN

RAISERROR('You May Not insert more than one row into this table!',16,10)

ROLLBACK TRAN

END

GO

INSERT LastCustNo VALUES(2) --由于触发器的原因,插入失败

GO

SELECT * FROM LastCustNo

注:由于触发器的原因,在任何时刻只允许在表中插入一行,如果表中已包含至少一行,在试图插入新的一行时将导致错误并回滚事务。

(1)IF (SELECT COUNT(*) FROM LastCustNo)必须用 > 1 ,而不能用=1,因为除Instead Of触发器外,T-SQL触发器只在操作已完成,但还没提交给数据库前运行,这表明从触发器角度看,在事务回滚前,LastCustNo表看起来总是包含两行。

(2)禁止使用IF EXISTS(SELECT COUNT(*) FROM LastCustNo)测试表中的行,因对于触发器来说,新插入的行直到事务被回滚才出现在表中,因此,即使在插入前表为空,也将禁止向表中插入行。

实际应用:禁止一个应用的多个实例连接至服务器

方法1:应用程序锁

--锁定应用程序资源

DECLARE @res int

BEGIN TRAN

EXEC @res = sp_getapplock @Resource = 'Check Writer',@LockMode = 'Exclusive'

--返回到应用程序

--当检测到应用程序时执行以下代码(释放锁资源)

EXEC @res = sp_releaseapplock @Resource = 'Check Writer'

ROLLBACK TRAN

注:可在启动应用程序时启用一个锁,在关闭时释放该锁。通过以独占方式启用锁,可在释放该锁前禁止运行应用程序的另一个实例。

但这种方法让一个事务长期保持为打开状态。一般来说,不应该长时间或当一个用户被提示输入时让一个事务保持打开状态。

方法2:使用SET CONTEXT_INFO(推荐方法)

IF EXISTS (SELECT * FROM master..sysprocesses WHERE context_info = 0x123456)

RAISERROR('You Can run only one copy of this application at a time',20,1) WITH LOG

ELSE

SET CONTEXT_INFO 0x123456

注:使用SET CINTEXT_INFO命令在启动时间向sysprocesses插入一个用户自定义值,每次启动程序时检查该值,如果存在,则包含特定的记号连接已存在,因此产生一个错误并中止自己的连接。如不存在,则将该值保存在sysprocesses中,并继续加载应用。

九、FACADE(外观)

此模式目标:它给位于子系统的一个接口集合提供统一的接口。

在T-SQL中与此模式类似的是包含INSTEAD OF触发器的视图(INSTEAD OF触发器接受对视图的更新,并将它们分配给适当的底层表)。

CREATE TABLE AussieArtists

(ArtistID int identity,

LastName varchar(30),

FirstName varchar(30))

GO

INSERT AussieArtists VALUES('Gibb','Barry')

INSERT AussieArtists VALUES('Crowe','Russell')

INSERT AussieArtists VALUES('Hogan','Paul')

GO

CREATE VIEW VAussieArtists

AS

SELECT FirstName + '' + LastName AS Name FROM AussieArtists

GO

CREATE TRIGGER VAussieArtists_Insert ON VAussieArtists INSTEAD OF INSERT

AS

INSERT AussieArtists(FirstName,LastName)

SELECT LEFT(Name,ISNULL(NULLIF(CHARINDEX(' ',NAME),0),255)-1),

SUBSTRING(Name,NULLIF(CHARINDEX(' ',Name),0)+1,255)

FROM inserted

GO

INSERT VAussieArtists(Name) VALUES('Gerg Ham')

GO

SELECT * FROM AussieArtists

GO

DROP TABLE AussieArtists

DROP VIEW VAussieArtists

GO

注:因想在加入到基表前处理数据,所以使用INSTEAD OF触发器分析输入并执行插入数据,即对视图的简单插入被转换为对基表稍微复杂一些的插入。

十、Chain Of Responsibility(职责链)

此模式目标:为避免通过多个对象提供机会处理请求,合并请求的发送者与接收者。为实现该模式,必须串联接收对象并沿此链传送请求,直到某个对象处理它。

前面的Conveyor模式已接到责任链模式,这里再作深入讨论。在T-SQL中最接近此模式所描述行为的是嵌套触发器(触发器的执行导致其他触发器激活并实现串联行为的操作)。

CREATE TABLE employee (id int identity ,name varchar(10))

GO

CREATE TABLE laborage (id int ,salary int)

GO

CREATE TABLE laborage2(id int,number int)

GO

INSERT employee (name) values('zs')

INSERT laborage values(101,101)

INSERT laborage2 values(101,102)

GO

CREATE TRIGGER up_employee ON Employee

FOR UPDATE

AS

UPDATE laborage Set Salary = Salary + 100 WHERE id =101

GO

CREATE TRIGGER up_laborage ON laborage

FOR UPDATE

AS

UPDATE laborage2 SET number = number + 200 WHERE id = 101

GO

UPDATE Employee SET Name = 'Zxm' WHERE id = 1

GO

注:SP_CONFIGURE 'NESTED TRIGGER',0 可以禁止触发器嵌套,同时触发器最多嵌套32次。从功能角度考虑,在触发器未设定激活顺序情况下,插入请求从一个触发器传送给另一个。在任何情况下,如果其中的任一触发器拒绝插入并回滚事务,则整个操作都将被取消。

十一、COMMAND(命令)

这种模式目标:将请求一个对象来封装,允许你参数化包含不同请求、队列或日志请求的客户端,并支持可撤消操作。在T-SQL中与此模式对应的是事务。

CREATE PROC prClearLS

@intLsID int

AS

BEGIN TRAN

UPDATE Inventory SET Lease = 0 WHERE LsID = @intLsID

IF @@ERROR <> 0

GOTO PROBLEM

UPDATE LeaseSchedule

SET PeriodTotalAmount = 0

WHERE ScheduleID = @intLsID

IF @@ERROR <> 0

GOTO PROBLEM

COMMIT TRAN

RETURN 0

PROBLEM:

PRINT 'Unable to eliminate lease amounts from the database.'

ROLLBACK TRAN

RETURN 1

这篇关于带你深入了解T-SQL的十一种设计模式的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/875174

相关文章

Java Stream流使用案例深入详解

《JavaStream流使用案例深入详解》:本文主要介绍JavaStream流使用案例详解,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录前言1. Lambda1.1 语法1.2 没参数只有一条语句或者多条语句1.3 一个参数只有一条语句或者多

MySQL 中的 JSON 查询案例详解

《MySQL中的JSON查询案例详解》:本文主要介绍MySQL的JSON查询的相关知识,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录mysql 的 jsON 路径格式基本结构路径组件详解特殊语法元素实际示例简单路径复杂路径简写操作符注意MySQL 的 J

Windows 上如果忘记了 MySQL 密码 重置密码的两种方法

《Windows上如果忘记了MySQL密码重置密码的两种方法》:本文主要介绍Windows上如果忘记了MySQL密码重置密码的两种方法,本文通过两种方法结合实例代码给大家介绍的非常详细,感... 目录方法 1:以跳过权限验证模式启动 mysql 并重置密码方法 2:使用 my.ini 文件的临时配置在 Wi

一文详解Java异常处理你都了解哪些知识

《一文详解Java异常处理你都了解哪些知识》:本文主要介绍Java异常处理的相关资料,包括异常的分类、捕获和处理异常的语法、常见的异常类型以及自定义异常的实现,文中通过代码介绍的非常详细,需要的朋... 目录前言一、什么是异常二、异常的分类2.1 受检异常2.2 非受检异常三、异常处理的语法3.1 try-

MySQL重复数据处理的七种高效方法

《MySQL重复数据处理的七种高效方法》你是不是也曾遇到过这样的烦恼:明明系统测试时一切正常,上线后却频频出现重复数据,大批量导数据时,总有那么几条不听话的记录导致整个事务莫名回滚,今天,我就跟大家分... 目录1. 重复数据插入问题分析1.1 问题本质1.2 常见场景图2. 基础解决方案:使用异常捕获3.

SQL中redo log 刷⼊磁盘的常见方法

《SQL中redolog刷⼊磁盘的常见方法》本文主要介绍了SQL中redolog刷⼊磁盘的常见方法,将redolog刷入磁盘的方法确保了数据的持久性和一致性,下面就来具体介绍一下,感兴趣的可以了解... 目录Redo Log 刷入磁盘的方法Redo Log 刷入磁盘的过程代码示例(伪代码)在数据库系统中,r

mysql中的group by高级用法

《mysql中的groupby高级用法》MySQL中的GROUPBY是数据聚合分析的核心功能,主要用于将结果集按指定列分组,并结合聚合函数进行统计计算,下面给大家介绍mysql中的groupby用法... 目录一、基本语法与核心功能二、基础用法示例1. 单列分组统计2. 多列组合分组3. 与WHERE结合使

Mysql用户授权(GRANT)语法及示例解读

《Mysql用户授权(GRANT)语法及示例解读》:本文主要介绍Mysql用户授权(GRANT)语法及示例,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录mysql用户授权(GRANT)语法授予用户权限语法GRANT语句中的<权限类型>的使用WITH GRANT

Mysql如何解决死锁问题

《Mysql如何解决死锁问题》:本文主要介绍Mysql如何解决死锁问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录【一】mysql中锁分类和加锁情况【1】按锁的粒度分类全局锁表级锁行级锁【2】按锁的模式分类【二】加锁方式的影响因素【三】Mysql的死锁情况【1

SQL BETWEEN 的常见用法小结

《SQLBETWEEN的常见用法小结》BETWEEN操作符是SQL中非常有用的工具,它允许你快速选取某个范围内的值,本文给大家介绍SQLBETWEEN的常见用法,感兴趣的朋友一起看看吧... 在SQL中,BETWEEN是一个操作符,用于选取介于两个值之间的数据。它包含这两个边界值。BETWEEN操作符常用