2019.12.22_SQL SERVER学习记录

1.昨天的总结:
nvarchar、nchar这两个数据类型以及查询时字符串前加上N(比如 where name=N’abc’)均表示这些字符串中一个字符占2个字节,无论中英文。
CASE的使用,用于查询或者用于WHERE后的筛选条件,相当于.NET中的SWITCH CASE,比如:
SELECT productname,
(CASE
WHEN categoryid=1 THEN ‘类型1’
WHEN categoryid=2 THEN ‘类型2’
WHEN categoryid=3 THEN ‘类型3’
WHEN categoryid=4 THEN ‘类型4’
ELSE ‘其他类型’
END) categoryname FROM Production.Products;
视图、函数:
创建视图:
GO
CREATE VIEW [视图名]
AS
[SELECT语句]
GO
创建函数:
GO
CREATE FUNCTION [函数名]
(定义传入的参数,比如@num as int) RETURNS TABLE
AS
RETURN
[包含已定义参数的SELECT语句]
GO
视图与函数作为可以重用的表表达式,相比派生表和公用表达式CTE来说更常用,而且不会在使用一次后就失效,可以多次使用,直到使用删除语句删除。
表表达式总结:
可以简化SQL查询逻辑,如果需要重用就使用视图和函数,需要传入参数就使用函数,如果只是在一条语句中临时使用可以使用派生表或者公用表达式CTE。一般实际企业级项目开发中使用视图和函数较多,需要熟练掌握。

2.事务

事务指的是数据库操作中一个工作单元内执行的全部操作。事务是数据库并发控制的基本单位,一个事务也就是一条或者一组语句要么全部执行成功,对数据库中的内容进行修改;要么全部失败,把所有执行过的操作还原到语句执行前的状态。
如何定义事务:
以下为显示定义

-- 开始一个事务
BEGIN TRAN;
--中间执行对数据库的操作
--提交事务
COMMIT TRAN;
--回滚
ROLLBACK TRAN;

同时,每个单独执行的语句都是包含隐式定义的事务。
事务的4大特征:
**原子性:**也是事务的最大特征,整个事务中的数据库操作看成一个原子,要么一起成功,要么一起失败。
**一致性:**即数据的完整性,事务的提交要保证数据库中数据的完整性,比如不能违背一些数据库中指定的数据约束,否则SQL SERVER会通过回滚事务保证这些数据的一致性。比如如果本来的操作是往A表的金额字段增加100,再往B表的金额字段减100,但是修改后B表只减少了10,则不满足一致性。
**隔离性:**不同的用户使用事务对数据库的修改是隔离的,多个用户可以同时对同一个数据进行修改而互不干扰,也不会影响数据的正确性和完整性。要跟事务锁一起理解,如果前一个事务还在对当前事务访问的数据表进行修改,则会锁定该表,当前事务无法修改、读取这个表的数据直到前一个事务执行完成。
**持久性:**事务对数据库的修改是持久的,事务提交成功后始终会持久化到数据库中。
**事务锁:**分为排它锁、共享锁,当事务对一个数据表进行修改时,数据表会被授予排它锁,此时其他事务无法再对这个表进行读写操作。共享锁则是事务对表进行读取操作时使用的,其他事务还是可以正常读写。需要注意的是如果已经分配共享锁的数据表,待读取操作完成前,无法授予排它锁,但是还是可以授予其他事务共享锁。也就是说读取操作可以同时执行很多个,但是如果一个表一直处于读取状态则一直无法写入、修改。
**阻塞:**某个事务在操作数据表时被授予了事务锁,而此时另一个事务也需要操作这张数据表,而且是无法同时授予的锁类型,此时就发生了阻塞。一般来说阻塞发生是正常现象,只要在合理的时间内满足请求就可以了。而如果长时间阻塞则需要人工干预排除问题。
如何解除阻塞?设置锁超时时间:SET LOCK_TIMEOUT 5000;。或者杀掉阻塞的进程。
隔离级别:
用于控制事务并发时用户读写的操作。一般情况下读操作需要共享锁,写操作需要排它锁。隔离级别可以控制读操作的行为方式,从而也对写操作产生一定的影响。

--要设置整个会话级别的隔离级别,可以使用以下语句:
SET TRANSACTION ISOLATION LEVEL <isolation name>;
--也可以使用表提示来设置查询级别的隔离级别:
SELECT ... FROM <table> WITH <isolation name>;

READ UNCOMMITED 未提交读:
最低的一种隔离级别。设置这种隔离级别的用户,在读取数据时不会被授予共享锁,其他事务则可以同时对数据表进行写操作,因此可能会出现脏读(也就是别的事务还未提交的事务修改,会被当前的查询读取到,如果另外的那个事务回滚了这个修改,则这个数据就是错误数据)。
READ COMMITED 已提交读:
这个是SQL SERVER对每个事务的默认隔离级别,只能读取到已经提交的数据。在用户的两次读操作之间可能有别的事务对读取的数据进行了修改,导致两次读取的数据不一致,这种现象称为不可重复读。
REPEATABLE READ 可重复读:
这种隔离级别下的事务,共享锁会一直保持到事务结束后才被释放,换句话说就是在该事务完成前,其他事务无法对同一数据表进行修改。可以防止不可重复读,也可以防止丢失更新,但是可能导致死锁。因为在可重复读的隔离级别下,事务的共享锁锁定的时第一次查询出来的那些行,其他事务无法对这些行进行修改,但是可以新增数据,如果新增的数据符合第一次查询时的筛选逻辑,那么在第二次执行读取时也会被读出来,导致两次读取的出的数量不一致,称为幻读
SERIALIZEABLE 可序列化:
为了解决幻读现象,可序列化这种隔离级别在授予共享锁时,不仅锁定了满足查询条件的那些行,同时也锁定了之后可能满足查询条件的行,那么当别的事务想要增加一些满足查询条件的行时,会被阻塞。
SNAPSHOT 快照:
基于行版本的隔离级别,在设置为快照隔离级别的事务执行DELETE/UPDATE操作前,会先备份当前行数据到tempdb中,这个隔离级别保证了事务读取行数据是可用的最后提交的版本。快照清理线程每1分钟执行一次,当一个快照版本不再被任何事务需要时,会被清理。
另外,在SNAP快照级别下,可以通过检查的行版本,检测出更新冲突。它能判断出在快照事务的一次读操作和一次写操作之间是否有其他事务修改过数据。如果SQL Server检测到在读取和写入操作之间有另一个事务修改了数据,则会让当前事务失败并终止。
READ COMMITED SNAPSHOT 已经提交读隔离:
也是基于行版本的隔离级别,与快照隔离的区别是它是保存的语句执行前的行状态,而不是事务开始前的行状态。此外,该级别不会为进行冲突检查,所以这个隔离级别跟SQL SERVER的默认级别READ COMMITED 已提交读非常类似了,不过读取时不需要获取共享锁,因此当需要访问的数据表被其他事务用排它锁锁定时也不需要等待。
(事务隔离级别具体可以参考:https://www.cnblogs.com/edisonchou/p/6129717.html#ch1)
死锁:
两个事务进程之间互相阻塞的现象,一般SQL SERVER探测到出现死锁后会优先结束掉逻辑比较少的那个事务进程来解开死锁。死锁的现象一般发生的过程是,事务A在执行的过程中,通过UPDATE/DELETE操作对甲表请求了排它锁,此时事务B又对乙表请求了排它锁,然后这两个事务都还没提交的情况下,A对乙表执行SELECT,B对甲表执行SELECT,双向阻塞引发了死锁。不难看出,越复杂执行时间越长的事务,越容易发生死锁,所以简化事务中的逻辑,尽量把操作放在同一个工作单元中执行,可以一定程度上避免死锁
避免死锁的方式:
1、调整事务的执行顺序,例如以上例子中,A、B事务都先访问甲表,再访问乙表,则不会发生死锁。
2、良好的索引设计,比如事务A对一张几十万数据但是无索引的订单表来UPDATE某个记录中的某字段,然后事务B也通过这个字段对表记录进行筛选,两个事务都耗时非常长,因为没有索引需要全表搜索,大概率发生死锁现象。而如果我们对这两个事务所用到的筛选字段建立索引,则可以减少这种没有真正逻辑冲突的死锁现象。

你可能感兴趣的:(sqlserver,数据库)