SQL 视图 临时表 存储过程 索引 事务
视图:
视图是按照你的sql语句生成的一个虚拟的东西,本身并不占数据库的空间
创建视图 create view view_1 as select id from table_1
当你表里的数据增加或者删除的时候,你视图里的内容也随之变化
总之你不能对视图进行update或者insert into操作
说白了,就是视图的变化随着表的变化而变化
除非重新create or replace view_1才能把这个视图中的东西改掉
应用场景:有一个查询语句非常复杂,大概有100行这么多,有时还想把这个巨大无比的select语句和其他表关联起来得到结果,写太多很麻烦,可以用一个视图来代替这100行的select语句,充当一个变量角色
临时表:
临时表只在当前连接可见,当关闭连接时,Mysql会自动删除表并释放所有空间。意思就是临时表就储存在内存中,临时创建的表,你把sql语句跑完,关掉,它就没了。所以,我们经常只是在存储过程或者频繁操作语句的时候能用到临时表。
对于临时表有如下几个特点:
- 本地临时表就是用户在创建表的时候添加了“#”前缀的表,其特点是根据数据库连接独立。只有创建本地临时表的数据库连接有表的访问权限,其它连接不能访问该表;
- 不同的数据库连接中,创建的本地临时表虽然“名字”相同,但是这些表之间相互并不存在任何关系;在SQLSERVER中,通过特别的命名机制保证本地临时表在数据库连接上的独立性。
- 真正的临时表利用了数据库临时表空间,由数据库系统自动进行维护,因此节省了表空间。并且由于临时表空间一般利用虚拟内存,大大减少了硬盘的I/O次数,因此也提高了系统效率。
- 临时表在事务完毕或会话完毕数据自动清空,不必记得用完后删除数据。
本地临时表
CREATE TABLE #Temp ( id int, customer_name nvarchar(50), age int )
本地临时表的名称以单个数字符号 (#) 打头;它们仅对当前的用户连接(也就是创建本地临时表的connection)是可见的;当用户从 SQL Server 实例断开连接时被删除。
全局临时表
全局临时表的名称以两个数字符号 (##) 打头,创建后对任何数据库连接都是可见的,当所有引用该表的数据库连接从 SQL Server 断开时被删除。
CREATE TABLE ##Temp ( id int, customer_name nvarchar(50), age int )
INSERT INTO ##Temp VALUES(1,'老王',20),(2,'老张',30),(3,'老李',25)
应用场景:你在写存储过程时,有很多的连接,比如你需要连接A,B,C,D,E,F,G,H那么多张表,才能得到你的结果表,同时做连接的消耗太大,你可以先A,B,C连接的结果,放在临时表,接着再把这张临时表,跟D,E,F连接,作为新的结果放在临时表,接着再把临时表与G,H连接,最后得到临时表数据,一次插入到结果表(永久表)
查询数据并写入临时表:
select * into #tab from table;
删除临时表:
drop table #tab;
存储过程
- 对于复杂的业务逻辑,因为在存储过程创建的时候,数据库已经对其进行了一次解析和优化。存储过程一旦执行,在内存中就会保留一份这个存储过程,这样下次再执行同样的存储过程时,可以从内存中直接调用,所以执行速度会比普通sql快。
- 减少网络流量 直接在数据库里面跑 可维护性可扩展 增强安全性
- 缺点 执行比较繁琐,维护也很麻烦,可移植性 开发调试复杂
use MyUser -- 切换数据库 go--检查是否存在存储过程 if exists(select * from sysobjects where name='V_elfareSalary') drop procedure V_elfareSalary go --创建存储过程 create procedure V_elfareSalary as begin select * from User1 end -- 调用存储过程 go exec V_elfareSalary go ------------------------------------------- ---创建带参存储过程 go if exists(select * from sysobjects where name='V_elfareSalary_C') drop procedure V_elfareSalary_C go --创建存储过程 create procedure V_elfareSalary_C @Name nvarchar(50)--- 参数 as begin select * from User1 where Name = @Name end go -- 调用存储过程 exec V_elfareSalary_C '王国栋'
事务
保持逻辑数据一致性与可恢复性,必不可少的利器。
死锁
什么是死锁,为什么会产生死锁。我用 “事务把死锁给整出来啦” 标题下的两个事务产生的死锁来解释应该会更加生动形象点。
例子是这样的:
第一个事务(称为A):先更新lives表 --->>停顿5秒---->>更新earth表
第二个事务(称为B):先更新earth表--->>停顿5秒---->>更新lives表
先执行事务A----5秒之内---执行事务B,出现死锁现象。
过程是这样子的:
1、A更新lives表,请求lives的排他锁,成功。
2、B更新earth表,请求earth的排他锁,成功。
3、5秒过后
4、A更新earth,请求earth的排它锁,由于B占用着earth的排它锁,等待。
5、B更新lives,请求lives的排它锁,由于A占用着lives的排它锁,等待。
这样相互等待对方释放资源,造成资源读写拥挤堵塞的情况,就被称为死锁现象,也叫做阻塞。而为什么会产生,上例就列举出来啦。
然而数据库并没有出现无限等待的情况,是因为数据库搜索引擎会定期检测这种状况,一旦发现有情况,立马选择一个事务作为牺牲品。牺牲的事务,将会回滚数据。有点像两个人在过独木桥,两个无脑的人都走在啦独木桥中间,如果不落水,必定要有一个人给退回来。这种相互等待的过程,是一种耗时耗资源的现象,所以能避则避。
哪个人会被退回来,作为牺牲品,这个我们是可以控制的。控制语法:
1
|
set deadlock_priority <级别> |
死锁处理的优先级别为 low<normal<high,不指定的情况下默认为normal,牺牲品为随机。如果指定,牺牲品为级别低的。
还可以使用数字来处理标识级别:-10到-5为low,-5为normal,-5到10为high。
减少死锁的发生,提高数据库性能
死锁耗时耗资源,然而在大型数据库中,高并发带来的死锁是不可避免的,所以我们只能让其变的更少。
1、按照同一顺序访问数据库资源,上述例子就不会发生死锁啦
2、保持是事务的简短,尽量不要让一个事务处理过于复杂的读写操作。事务过于复杂,占用资源会增多,处理时间增长,容易与其它事务冲突,提升死锁概率。
3、尽量不要在事务中要求用户响应,比如修改新增数据之后在完成整个事务的提交,这样延长事务占用资源的时间,也会提升死锁概率。
4、尽量减少数据库的并发量。
5、尽可能使用分区表,分区视图,把数据放置在不同的磁盘和文件组中,分散访问保存在不同分区的数据,减少因为表中放置锁而造成的其它事务长时间等待。
6、避免占用时间很长并且关系表复杂的数据操作。
7、使用较低的隔离级别,使用较低的隔离级别比使用较高的隔离级别持有共享锁的时间更短。这样就减少了锁争用。
常用语句就四个。
- Begin Transaction:标记事务开始。
- Commit Transaction:事务已经成功执行,数据已经处理妥当。
- Rollback Transaction:数据处理过程中出错,回滚到没有处理之前的数据状态,或回滚到事务内部的保存点。
- Save Transaction:事务内部设置的保存点,就是事务可以不全部回滚,只回滚到这里,保证事务内部不出错的前提下。
---开启事务 begin tran --错误捕捉机制,看好啦,这里也有的。并且可以嵌套。 begin try --语句正确 insert into lives (Eat,Play,Numb) values ('猪肉','足球',1) --Numb为int类型,出错 insert into lives (Eat,Play,Numb) values ('猪肉','足球','abc') --语句正确 insert into lives (Eat,Play,Numb) values ('狗肉','篮球',2) end try begin catch select Error_number() as ErrorNumber, --错误代码 Error_severity() as ErrorSeverity, --错误严重级别,级别小于10 try catch 捕获不到 Error_state() as ErrorState , --错误状态码 Error_Procedure() as ErrorProcedure , --出现错误的存储过程或触发器的名称。 Error_line() as ErrorLine, --发生错误的行号 Error_message() as ErrorMessage --错误的具体信息 if(@@trancount>0) --全局变量@@trancount,事务开启此值+1,他用来判断是有开启事务 rollback tran ---由于出错,这里回滚到开始,第一条语句也没有插入成功。 end catch if(@@trancount>0) commit tran --如果成功Lives表中,将会有3条数据。 --表本身为空表,ID ,Numb为int 类型,其它为nvarchar类型 select * from lives
索引
什么是索引?
SQL索引有两种,聚集索引和非聚集索引,索引主要目的是提高了SQL Server系统的性能,加快数据的查询速度与减少系统的响应时间
下面举两个简单的例子:
图书馆的例子:一个图书馆那么多书,怎么管理呢?建立一个字母开头的目录,例如:a开头的书,在第一排,b开头的在第二排,这样在找什么书就好说了,这个就是一个聚集索引,可是很多人借书找某某作者的,不知道书名怎么办?图书管理员在写一个目录,某某作者的书分别在第几排,第几排,这就是一个非聚集索引
字典的例子:字典前面的目录,可以按照拼音和部首去查询,我们想查询一个字,只需要根据拼音或者部首去查询,就可以快速的定位到这个汉字了,这个就是索引的好处,拼音查询法就是聚集索引,部首查询就是一个非聚集索引.
看了上面的例子,下面的一句话大家就很容易理解了:聚集索引存储记录是物理上连续存在,而非聚集索引是逻辑上的连续,物理存储并不连续。就像字段,聚集索引是连续的,a后面肯定是b,非聚集索引就不连续了,就像图书馆的某个作者的书,有可能在第1个货架上和第10个货架上。还有一个小知识点就是:聚集索引一个表只能有一个,而非聚集索引一个表可以存在多个。
1.2 索引的存储机制
首先,无索引的表,查询时,是按照顺序存续的方法扫描每个记录来查找符合条件的记录,这样效率十分低下,举个例子,如果我们将字典的汉字随即打乱,没有前面的按照拼音或者部首查询,那么我们想找一个字,按照顺序的方式去一页页的找,这样效率有多底,大家可以想象。
聚集索引和非聚集索引的根本区别是表记录的排列顺序和与索引的排列顺序是否一致,其实理解起来非常简单,还是举字典的例子:如果按照拼音查询,那么都是从a-z的,是具有连续性的,a后面就是b,b后面就是c, 聚集索引就是这样的,他是和表的物理排列顺序是一样的,例如有id为聚集索引,那么1后面肯定是2,2后面肯定是3,所以说这样的搜索顺序的就是聚集索引。非聚集索引就和按照部首查询是一样是,可能按照偏房查询的时候,根据偏旁‘弓’字旁,索引出两个汉字,张和弘,但是这两个其实一个在100页,一个在1000页,(这里只是举个例子),他们的索引顺序和数据库表的排列顺序是不一样的,这个样的就是非聚集索引。
建立索引的原则:
1) 定义主键的数据列一定要建立索引。
2) 定义有外键的数据列一定要建立索引。
3) 对于经常查询的数据列最好建立索引。
4) 对于需要在指定范围内的快速或频繁查询的数据列;
5) 经常用在WHERE子句中的数据列。
6) 经常出现在关键字order by、group by、distinct后面的字段,建立索引。如果建立的是复合索引,索引的字段顺序要和这些关键字后面的字段顺序一致,否则索引不会被使用。
7) 对于那些查询中很少涉及的列,重复值比较多的列不要建立索引。
8) 对于定义为text、image和bit的数据类型的列不要建立索引。
9) 对于经常存取的列避免建立索引
9) 限制表上的索引数目。对一个存在大量更新操作的表,所建索引的数目一般不要超过3个,最多不要超过5个。索引虽说提高了访问速度,但太多索引会影响数据的更新操作。
10) 对复合索引,按照字段在查询条件中出现的频度建立索引。在复合索引中,记录首先按照第一个字段排序。对于在第一个字段上取值相同的记录,系统再按照第二个字段的取值排序,以此类推。因此只有复合索引的第一个字段出现在查询条件中,该索引才可能被使用,因此将应用频度高的字段,放置在复合索引的前面,会使系统最大可能地使用此索引,发挥索引的作用。
创建索引
- alter table tbl_name add primary key (column_list):
- 该语句添加一个主键,这意味着索引值必须是唯一的,且不能为 null。
- alter table tbl_name add unique index_name (column_list):
- 这条语句创建索引的值必须是唯一的(除了 null 外,null 可能会出现多次)。
- alter table tbl_name add index index_name (column_list):
- 添加普通索引,索引值可出现多次。
- alter table tbl_name add fulltext index_name (column_list):
- 该语句指定了索引为 fulltext ,用于全文索引。
删除索引
drop index [indexname] on mytable;
修改
alter mytable add index [indexname] on(username(length))
查询
使用 show index 命令来列出表中的相关的索引信息。可以通过添加 \g 来格式化输出信息。
show index from table_name \g
聚集(clustered)索引,也叫聚簇索引
定义:数据行的物理顺序与列值(一般是主键的那一列)的逻辑顺序相同,一个表中只能拥有一个聚集索引。
非聚集(unclustered)索引
定义:该索引中索引的逻辑顺序与磁盘上行的物理存储顺序不同,一个表中可以拥有多个非聚集索引。
其实按照定义,除了聚集索引以外的索引都是非聚集索引,只是人们想细分一下非聚集索引,分成普通索引,唯一索引,全文索引。如果非要把非聚集索引类比成现实生活中的东西,那么非聚集索引就像新华字典的偏旁字典,他结构顺序与实际存放顺序不一定一致。
非聚集索引的二次查询问题
非聚集索引叶节点仍然是索引节点,只是有一个指针指向对应的数据块,此如果使用非聚集索引查询,而查询列中包含了其他该索引没有覆盖的列,那么他还要进行第二次的查询,查询节点上对应的数据行的数据。
我们需要搞清楚以下几个问题:
第一:聚集索引的约束是唯一性,是否要求字段也是唯一的呢? ** 不要求唯一!**
分析:如果认为是的朋友,可能是受系统默认设置的影响,一般我们指定一个表的主键,如果这个表之前没有聚集索引,同时建立主键时候没有强制指定使用非聚集索引,SQL会默认在此字段上创建一个聚集索引,而主键都是唯一的,所以理所当然的认为创建聚集索引的字段也需要唯一。
结论:聚集索引可以创建在任何一列你想创建的字段上,这是从理论上讲,实际情况并不能随便指定,否则在性能上会是恶梦。
第二:为什么聚集索引可以创建在任何一列上,如果此表没有主键约束,即有可能存在重复行数据呢?
粗一看,这还真是和聚集索引的约束相背,但实际情况真可以创建聚集索引。
分析其原因是:如果未使用 UNIQUE 属性创建聚集索引,数据库引擎将向表自动添加一个四字节 uniqueifier 列。必要时,数据库引擎 将向行自动添加一个 uniqueifier 值,使每个键唯一。此列和列值供内部使用,用户不能查看或访问。
第三:是不是聚集索引就一定要比非聚集索引性能优呢?
如果想查询学分在60-90之间的学生的学分以及姓名,在学分上创建聚集索引是否是最优的呢?
答:否。既然只输出两列,我们可以在学分以及学生姓名上创建联合非聚集索引,此时的索引就形成了覆盖索引,即索引所存储的内容就是最终输出的数据,这种索引在比以学分为聚集索引做查询性能更好。
第四:在数据库中通过什么描述聚集索引与非聚集索引的?
索引是通过二叉树的形式进行描述的,我们可以这样区分聚集与非聚集索引的区别:聚集索引的叶节点就是最终的数据节点,而非聚集索引的叶节仍然是索引节点,但它有一个指向最终数据的指针。
第五:在主键是创建聚集索引的表在数据插入上为什么比主键上创建非聚集索引表速度要慢?
有了上面第四点的认识,我们分析这个问题就有把握了,在有主键的表中插入数据行,由于有主键唯一性的约束,所以需要保证插入的数据没有重复。我们来比较下主键为聚集索引和非聚集索引的查找情况:聚集索引由于索引叶节点就是数据页,所以如果想检查主键的唯一性,需要遍历所有数据节点才行,但非聚集索引不同,由于非聚集索引上已经包含了主键值,所以查找主键唯一性,只需要遍历所有的索引页就行(索引的存储空间比实际数据要少),这比遍历所有数据行减少了不少IO消耗。这就是为什么主键上创建非聚集索引比主键上创建聚集索引在插入数据时要快的真正原因。
坏处
- 索引需要占用数据表以外的物理存储空间
- 创建索引和维护索引要花费一定的时间
- 当对表进行更新操作时,索引需要被重建,这样降低了数据的维护速度。
简要说明:
1、存储空间,每个索引都要空间存储
2、如果非聚集索引很多,一旦聚集索引改变,那么所有非聚集索引都会跟着变。
3、过多索引会导致优化器优化过程需要评估的组合增多。
4、每个索引都有统计信息,索引越多统计信息越多。
5、更新开销,一旦一个数据改变,并且改变的列比较多,可能会引起好几个索引跟着改变
是索引也有另一面,就是索引是有开销的,每次update、delete、insert表的时候,都会维护相应的索引,把值维护到索引中去。
所以,一旦你的索引很多,比如你一个表建了10个索引,那么在你进行update操作时,可能就得同时维护10个索引,那么update语句肯定就会变得慢,相应的delete、insert都会慢。
聚集索引(Clustered Index)特点
- 聚集索引的叶节点就是实际的数据页
- 聚集索引中的排序顺序仅仅表示数据页链在逻辑上是有序的。而不是按照顺序物理的存储在磁盘上
- 行的物理位置和行在索引中的位置是相同的
- 每个表只能有一个聚集索引
- 聚集索引的平均大小大约为表大小的5%左右
非聚集索引 (Unclustered Index) 特点
- 非聚集索引的页,不是数据,而是指向数据页的页。
- 若未指定索引类型,则默认为非聚集索引。
- 叶节点页的次序和表的物理存储次序不同
- 每个表最多可以有249个非聚集索引
- 在非聚集索引创建之前创建聚集索引(否则会引发索引重建)
聚集索引更能提高性能当它满足以下条件时:
独特, 狭窄, 持续增长的,最好是只向上增加