SQL Server (一) 对表的增删改查、事务、锁

1、数据库命名规则(老版本,2008之前版本)

  • 名称长度不能超过128个字符,本地临时表名称不能超过116个字符。
  • 名称的第一个字符尽量使用英文字母、中文(或其他语言的字母)、下划线、“@”或“#”符号。
  • 除第一个字符外的其他字符,还可以包括数字和“$”符号。
  • 名称中间不允许有空格或其他特殊字符。
  • 名称不能是保留字。

2、基本操作

/*创建表*/
CREATE TABLE Class(
 cId INT PRIMARY KEY,
 cNum INT NOT NULL,
 cMteacher VARCHAR(20)
)
CREATE TABLE Student(
sId INT  PRIMARY KEY,
sName VARCHAR(20),
sAge INT,
sTel INT,
cId INT NOT NULL ,FOREIGN KEY(cId) REFERENCES class(cId)
)
CREATE TABLE Teacher(
 tId int NOT NULL,
 tName VARCHAR(20) NOT NULL,
 tAge int,
 cId int
)

/*删除表*/

DROP TABLE Teacher;

/*删除表数据*/
TRUNCATE TABLE Class;

/*更改表名*/
SP_RENAME Student,Studentinfo

/*更改表结构 -添加字段*/
ALTER TABLE Student ADD sAddress VARCHAR(50);

/*更改表结构 -删除字段*/
ALTER TABLE Student DROP COLUMN sAddress ;

/*添加组合主键*/

ALTER TABLE [dbo].[Teacher]
ADD
CONSTRAINT PK_test PRIMARY KEY NONCLUSTERED (tId,tName )

/*添加外键约束*/
ALTER TABLE dbo.Teacher 
WITH CHECK 
ADD CONSTRAINT FK_Teacher FOREIGN KEY(cId)REFERENCES dbo.Class(cId)

/*添加check约束 */
ALTER TABLE dbo.Teacher
WITH NOCHECK 
ADD CONSTRAINT CK_Teacher CHECK(tAge > 0 AND tAge <70)

/* 插入数据*/
INSERT INTO Class VALUES(1,30,'王小红'),
                        (2,30,'张小明')

/*利用显示事务控制DML操作 */
BEGIN TRANSACTION tr_Teacher
INSERT INTO test.dbo.Teacher VALUES(01,'张小明',28,2)
GO
UPDATE test.dbo.Teacher
SET tAge = 29
WHERE tId = 01
GO
COMMIT TRANSACTION tr_Teacher 

/*打开隐式事务后,每次执行操作,都会开始一个新事务,需要用COMMIT或ROLLBACK结束*/
SET IMPLICIT_TRANSACTIONS ON 
INSERT INTO Teacher VALUES(02,'王小红',35,1)
GO
UPDATE Teacher
SET tAge = 34
WHERE tId = 02
GO
COMMIT 
SET IMPLICIT_TRANSACTIONS OFF

SELECT * FROM Teacher

/*设置事务保护点和事务回滚至保护点的语法*/
SAVE TRANSACTION savepoint_name
ROLLBACK TRANSACTION savepoint_name

--并发控制

当多个用户同时访问同一数据时,为了保证数据的准确性,将对事务进行并发控制。

(1)并发访问的问题:

丢失更新数据:

 解决:在A完成数据修改并提交事务之前,B客户端不允许访问相同的数据文件

脏读:

解决:事务A提交事务之前,不允许其他事务读取正在更改的数据

非重复读:

解决:在事务A完成最后一次读取数据前,不允许其他事务读取正在更改的数据

幻象读:

解决:采用并发技术,利用锁方法等来确保数据的准确性

--SQL server中的锁

锁定是数据库引擎为了避免数据出现异常,而限制多个用户在同一时间访问相同数据块的一种机制

                                                                             

                                                                   有关锁资源1

                                                                            锁模式应用 

 

/* 得到数据库中活跃的事务和锁的信息*/
BEGIN TRANSACTION  /*开始事务*/
USE test

DELETE FROM dbo.Teacher/*删除表中数据并使用锁*/
WITH (TABLOCKX)
WHERE tId=1

DBCC OPENTRAN('test')/*查询活动事务信息 */

COMMIT/*关闭事务*/

查询结果:

在SQL Server中提供了一个动态视图(sys.dm_tran_locks),它管理着有关当前活动的锁管理器资源的信息。有关他的常用返回列及其含义:

* 查询当前的锁管理器资源的信息*/
SELECT request_session_id,
resource_type AS type,
resource_database_id,
request_status
FROM sys.dm_tran_locks

查询结果:

--事务的阻塞

如果一个事务在数据操作过程中锁住了某个数据库资源,而此时,另一个事务想访问该资源,则必须等待该资源解锁,这样就会发生阻塞。

【例5.11】演示事务的阻塞。
要求事务A对表AtriTest中的数据进行修改,但要求事务不提交,然后利用事务B对表AtriTest中相同的数据进行更新,查看效果,操作步骤如下:
1 利用事务A对表AtriTest中的数据进行更新,脚本如下:

  --READ COMMITTED隔离级别
  SET TRANSACTION ISOLATION LEVEL READ COMMITTED
  USE test
  GO

  BEGIN TRANSACTION A                                 --事务A开始
 UPDATE Teacher SET tAge = 39 WHERE
  tId = 1


事务A对表Teacher中id是1的记录进行修改操作,此时事务已经开始,但始终没有提交。

2 利用事务B对表Teacher中id为1的数据进行修改,脚本如下:

  -READ COMMITTED隔离级别
  SET TRANSACTION ISOLATION LEVEL READ COMMITTED
  USE test
  GO

 BEGIN TRANSACTION B                                 --事务B开始
  UPDATE Teacher SET tAge = 47 WHERE
  tId = 1


事务B同样修改id为1的tAge列数据,此时,由于事务A没有提交事务,所以事务B将等待事务A完成,如果A一直没有提交,那么B将一直等待下去,这就形成了事务的阻塞。
为了快速地了解当前是否有事务进入了阻塞,可以使用动态管理视图sys.dm_os_waiting_tasks,它可以返回正在等待某些资源的任务的等待队列的有关信息。在下表给出了它常用的列及对应的含义。

该实例中一旦出现B事务无法执行下去的情况,可以利用以下的SQL语句查看当前的事务阻塞情况

SELECT blocking_session_id, wait_duration_ms, session_id
FROM sys.dm_os_waiting_tasks
WHERE blocking_session_id IS NOT NULL

这段脚本执行结果如下:



当中断事务B的操作后,事务阻塞取消,此时执行以上脚本则没有查询记录。根据上图所示,会话ID“52”阻塞了会话ID“54”。
需要说明的是sys.dm_os_waiting_tasks视图,取代了SQL Server 2000中的sp_who系统存储过程。
当利用sys.dm_os_waiting_tasks查询阻塞会话信息时,可以利用DBCC INPUTBUFFER语句来查看某会话ID对应的SQL信息,相关语法结构如下:

DBCC INPUTBUFFER ( session_id [ , request_id ] )
[WITH NO_INFOMSGS ]
【语法说明】
● DBCC INPUTBUFFER:语法关键词。
● session_id:会话ID。
● NO_INFOMSGS:取消严重级别从0到10的所有信息性消息。

查询指定会话的SQL语句。
要求利用DBCC INPUTBUFFER语句查询会话ID为52的SQL语句,相关脚本如下:


DBCC INPUTBUFFER(52)


在出现事务阻塞时,一旦查出阻塞的SQL语句,则可以通知对方将该语句撤销,这样事务阻塞的问题将得以解决,如果无法通知对方撤销SQL命令,则可以利用KILL命令来强行终止阻塞会话,以确保数据库的正常运行,KILL命令的语法结构如下:
KILL {spid|UOW} [WITH STATUSONLY]
【语法说明】
● KILL:关键词。
● spid:会话ID。
● UOW:标识分布式事务的工作单元(UOW) ID。
● WITH STATUSONLY:生成指定ID的回滚进度报告,该回滚因为以前的KILL操作引起。
● 使用该命令时需要慎重。
【例5.13】KILL命令的使用。
要求终止会话ID为“52”的事务,具体命令如下:

KILL 52

该命令一旦被执行成功,那么被阻塞的事务将马上得到执行,而被终止的事务将进行回滚操作。

--死锁的产生
--利用脚本解释死锁的产生。
这里假设有事务A和事务B,在默认隔离级别下分别访问两个表TAB_A和TAB_B,首先事务A对表TAB_A进行更新操作,并等待15秒,而后对表TAB_B进行更新操作,并提交事务,相关操作步骤如下:
1 分别创建表TAB_A和TAB_B,相关脚本如下:
 

 USE [test]
  GO
  --创建表TAB_A
  CREATE TABLE [dbo].[TAB_A](
      [id] [int] NOT NULL,
      [name] [nvarchar](50) NULL
  ) ON [PRIMARY]
  --创建表TAB_B
  CREATE TABLE [dbo].[ TAB_B](
      [id] [int] NOT NULL,
    [name] [nvarchar](50) NULL
) ON [PRIMARY]
GO


2 事务A首先对表TAB_A进行更新操作。

  SET TRANSACTION ISOLATION LEVEL READ COMMITTED      --设置隔离级别
  USE Test
  GO
  --开始事务,修改TAB_A表
  BEGIN TRANSACTION A

       --开始事务A
  UPDATE TAB_A SET Name='ta_taba'
  --等待15秒
  WAITFOR  DELAY '00:00:15'
  --修改TAB_B表并提交事务A
  UPDATE TAB_B SET Name='ta_tabb'
  COMMIT TRANSACTION A


3 事务B首先对表TAB_B进行更新操作。

  SET TRANSACTION ISOLATION LEVEL READ COMMITTED  --设置隔离级别
  USE Test
  GO
  --开始事务,修改TAB_B表
  BEGIN TRANSACTION B
  UPDATE TAB_B SET Name='tb_tabb'                 --更新TAB_B
  --等待15秒
  WAITFOR  DELAY '00:00:15'
  --修改TAB_A表并提交事务
  UPDATE TAB_A SET Name='tb_taba'                 --更新TAB_A表
  COMMIT TRANSACTION B                            --提交事务B


观察这两段代码,事务A需要等待事务B锁定的资源,而事务B又等待事务A锁定的资源,这样就形成了一个死循环,进而出现了永远阻塞状态,这就是死锁。解决方法只能是中断其中一个事务的请求,放弃它自己所占的资源。
 

处理死锁
SQL Server中,为了避免出现死锁,可以通过锁监视器定期地对特定线程进行检测。如果检测到死锁,那么数据库引擎则会对其中一个线程进行杀死操作,这样就可以结束死锁。被杀死的线程将回滚事务,并释放该事务持有的所有锁,同时将1205错误返回到应用程序,这样死锁将得到解决,数据库继续运行。
分别运行上述中的脚本,执行效果分别如下所示。

                                                         事务A的操作



                                                                           事务B的操作
从事务B的操作结果可以看出,事务B被数据库引擎作为“牺牲品”,它所做的操作将被回滚然后结束,这样就可以解除死锁,而保证了事务A的正常操作。为了验证这一点,可以执行以下的查询语句,查看运行后的结果:

  --查询表TAB_A数据
  SELECT [id]
       ,[name]
    FROM [test].[dbo].[TAB_A]
  GO
  --查询表TAB_B数据
  SELECT [id]
       ,[name]
    FROM [test].[dbo].[TAB_B]
  GO


分别查询表TAB_A和TAB_B的数据,查询结果如下图所示。


从上图可以发现事务A得以正常执行,而事务B的操作没有作用在两张表上。
 

预防死锁
正常来说,死锁不可能完全避免,只能减少发生的次数,下面给出了部分SQL编写原则,用户遵循这些原则编写SQL脚本,就能有效减少死锁的发生:
● 尽量保证事务简短。事务简短并处于同一个批处理中可以缩短单一事务对资源的锁定时间,如果程序比较长,可以考虑将脚本放到几个事务中。
事务尽量减少与人工交互。如果事务经常需要与人进行交互,那么整个事务的工作周期就会加长,对资源的锁定时间将大大加长,这样就很容易出现阻塞现象。工作中,当用户直接对数据库数据进行操作时容易出现这种情况。
使用低隔离级别正常操作数据,使用系统的默认事务隔离级别就可以了。事务的隔离级别越高,其产生死锁的可能性就相对越大。
访问资源的顺序要统一。并发的事务如果按照相同的顺序访问资源,那么发生死锁的可能性会降低。这是因为按照统一顺序访问资源,不会出现死锁的产生条件,但效率会有一定的下降。
 

猜你喜欢

转载自blog.csdn.net/qq_37503890/article/details/88534571