数据库知识点梳理

数据库面试常考题

 

一、 left join, right join, inner join区别?

  • left join(左联接) 返回包括左表中的所有记录和右表中联结字段相等的记录
  • right join(右联接) 返回包括右表中的所有记录和左表中联结字段相等的记录
  • inner join(等值连接) 只返回两个表中联结字段相等的行

注意:在某些数据库中left join等同于left outer join



举个例子: 
表A记录如下

aID aNum
1 a20050111
2 a20050112
3 a20050113
4 a20050114
5 a20050115


表B记录如下

bID bName
1 2006032401
2 2006032402
3 2006032403
4 2006032404
8 2006032408


1、sql语句如下:

<span style="color:#000000"><code><span style="color:#000088">select</span> * <span style="color:#000088">from</span> A
<span style="color:#000088">left</span> <span style="color:#000088">join</span> B
<span style="color:#000088">on</span> A.aID = B.bID</code></span>
  •  


结果如下:

aID aNum bID bName
1 a20050111 1 2006032401
2 a20050112 2 2006032402
3 a20050113 3 2006032403
4 a20050114 4 2006032404
5 a20050115 NULL NULL



(所影响的行数为5行) 
结果说明: 
left join是以A表的记录为基础的,A可以看成左表,B可以看成右表,left join是以左表为准的。换句话说,左表(A)的记录将会全部表示出来,而右表(B)只会显示符合搜索条件的记录(例子中为:A.aID = B.bID)。B表记录不足的地方均为NULL。


2、sql语句如下:

<span style="color:#000000"><code><span style="color:#000088">select</span> * <span style="color:#000088">from</span> A
<span style="color:#000088">right</span> <span style="color:#000088">join</span> B
<span style="color:#000088">on</span> A.aID =B.bID</code></span>
  •  


结果如下:

aID aNum bID bName
1 a20050111 1 2006032401
2 a20050112 2 2006032402
3 a20050113 3 2006032403
4 a20050114 4 2006032404
NULL NULL 8 2006032408


(所影响的行数为5行) 
结果说明: 
仔细观察一下,就会发现,和left join的结果刚好相反,这次是以右表(B)为基础的,A表不足的地方用NULL填充。 

3、sql语句如下:

<span style="color:#000000"><code><span style="color:#000088">select</span> * <span style="color:#000088">from</span> A
<span style="color:#000088">inner</span> <span style="color:#000088">join</span> B
<span style="color:#000088">on</span> A.aID = B.bID</code></span>
  •  


结果如下:

aID aNum bID bName
1 a20050111 1 2006032401
2 a20050112 2 2006032402
3 a20050113 3 2006032403
4 a20050114 4 2006032404


结果说明: 
很明显,这里只显示出了A.aID = B.bID的记录,这说明inner join并不以谁为基础,它只显示符合条件的记录。

二、 存储引擎MyIsam和Innodb区别?

(1)MyIsam类型不支持事务处理等高级处理,而Innodb类型支持 
事务处理是指原子性操作。例如,支持事务处理的Innodb表中,你发了一个帖子执行insert语句,来插入帖子内容,插入后要执行一个update语句来增加你的积分。假设一种特殊情况突然发生,insert成功了,update操作却没有被执行。也就是说你发了帖子却没有增加相应的积分。这就会造成用户不满。如果使用了事务处理,insert和update都放入到事务中去执行,这个时候,只有当insert和update两条语句都执行生成的时候才会将数据更新、写入到表中。如果其中任何一条语句失败,那么就会回滚为初始状态,不执行写入。这样就保证了insert和update肯定是一同执行的。

(2)MyIsam表不支持外键。innodb支持外键

(3)在执行数据写入的操作(insert,update,delete)的时候,MyIsam表会锁表,而innodb表会锁行。 
通俗的讲,就是你执行一个update语句,那么MyIsam表会将整个表都锁住,其他的insert和delete、update都会被拒之门外,等到这个update语句执行完成后才会被依次执行。而锁行,就是说,你执行update语句时,只会将这一条记录进行锁定,只有针对这条记录的其他写入、更新操作会被阻塞并等待这条update语句执行完毕后再执行,针对其他记录的写入操作不会有影响。但是innodb表的行锁也不是绝对的,如果在执行一个sql语句时,mysql不能确定要扫描的范围,innodb表同样会锁全表,例如update table set num =1 where name like “%aaa%”

(4)表的具体行数 
select count(*) from table , MyIsam只要简单的读出保存好的行数,注意的是,当count(*)语句包含where条件时,两种表的操作是一样的。 
innodb中不保存表的具体行数,也就是说,执行select count(*) from table时,innodb要扫描一遍整个表来计算有多少行。 

 

总结: 
因此,当你的数据库有大量的写入、更新操作而查询比较少或者数据完整性要求比较高的时候就选择innodb表。当你的数据库主要以查询为主,相比较而言更新和写入比较少,并且业务方面数据完整性要求不那么严格,就选择myisam表。因为MyIsam表的查询操作效率和速度都比innodb要快。

InnoDB的设计目标是处理大容量数据库系统,它的CPU利用率是其它基于磁盘的关系数据库引擎所不能比的。

我觉得使用InnoDB可以应对更为复杂的情况,特别是对并发的处理要比MyISAM高效。同时结合memcache也可以缓存SELECT来减少SELECT查询,从而提高整体性能。

使用以下mysql sql语句,可以给表设定数据库引擎:

<span style="color:#000000"><code><span style="color:#000088">ALTER</span> <span style="color:#000088">TABLE</span> <span style="color:#009900">`wp_posts`</span> ENGINE = MyISAM;</code></span>
  •  


查看当前数据库的引擎,可看出默认是InnoDB类型。 

三、 mysql的优化手段有哪些?

MYSQL数据库优化的八种方式: 
(1)选取最适用的字段属性 
MYSQL可以很好的支持大数据量的存取,但是一般来说,数据库中的表越小,在它上面执行的查询也就会越快。因此,在创建表的时候,为了获得更好的性能,我们可以将表中字段的宽度设得尽可能小。 
例如,在定义邮政编码这个字段时,如果将其设置为CHAR(255),显然给数据库增加了不必要的空间,甚至使用VARCHAR这种类型也是多余的,因为CHAR(6)就可以很好的完成任务了。同样的,如果可以的话,我们应该使用MEDIUMINT而不是BIGINT来定义整型字段。 
另外一个提高效率的方法是在可能的情况下,应该尽量把字段设置为NOT NULL,这样在将来执行查询的时候,数据库不用去比较NULL值。 
对于某些文本字段,例如“省份”或者“性别”,我们可以将它们定义为ENUM类型。因为在MYSQL中,ENUM类型被当做数值型数据来处理,而数值型数据被处理起来的速度要比文本类型快得多。这样,我们又可以提高数据库的性能。

(2)使用连接(JOIN)来代替子查询(Sub-Queries) 
例如:我们要将客户基本信息表中没有任何订单的客户删除掉,就可以利用子查询先从销售信息表中将所有发出订单的客户ID取出来,然后将结果传递给主查询,如下所示:

<span style="color:#000000"><code><span style="color:#000088">SELECT</span>* <span style="color:#000088">FROM</span> customerinfo
<span style="color:#000088">WHERE</span> customerID <span style="color:#000088">NOT</span> <span style="color:#000088">in</span> (<span style="color:#000088">SELECT</span> customerid <span style="color:#000088">FROM</span> salesinfo)</code></span>
  •  

如果使用连接(JOIN)来完成这个查询工作,速度将会快很多。尤其是当salesinfo表中对CustomerID建有索引的话,性能将会更好。查询如下: 
SELECT * FROM customerinfo 
LEFT JOIN salesinfo ON customerinfo.customerid = salesinfo.customerif 
Where salesinfo.customerid is NULL

连接(JOIN)之所以更有效率一些,是因为MYSQL不需要在内存中创建临时表来完成这个逻辑上的需要两个步骤的查询工作。

(3)使用联合(UNION)来代替手动创建的临时表 
UNION查询可以把需要使用临时表的两条或更多的select查询合并在一个查询中。在客户端的查询会话结束的时候,临时表会被自动删除,从而保证数据库整齐、高效。使用union来创建查询的时候,我们只需要用union作为关键字把多个select语句连接起来就可以了,要注意的是所有select语句中的字段数目要相同。 
例子:

<span style="color:#000000"><code><span style="color:#000088">Select</span> name, phone <span style="color:#000088">from</span> client <span style="color:#000088">union</span>
<span style="color:#000088">Select</span> name, birthdate <span style="color:#000088">from</span> author <span style="color:#000088">union</span>
<span style="color:#000088">Select</span> name, supplier <span style="color:#000088">from</span> product</code></span>
  •  

(4)事务 
尽管我们可以使用子查询、连接(join)和联合(union)来创建各种各样的查询,但不是所有的数据库操作都可以只用一条或少数几条sql语句就可以完成的。更多的时候是需要用到一系列的语句来完成某种工作。但是如果这个语句块中的某一条语句运行出错的时候,整个语句块的操作就会变得不确定起来。因此要尽量使用事务,它的作用:要么语句块中每条语句都操作成功,要么都失败。换句话说,就是可以保持数据库中数据的一致性和完整性。事务以BEGIN关键字开始,COMMIT关键字结束。在这之间的一条SQL操作失败,那么,rollback命令就可以把数据库恢复到BEGIN开始之前的状态。 
例子:

<span style="color:#000000"><code>BEGIN:
    <span style="color:#000088">INSERT</span> <span style="color:#000088">INTO</span>  salesinfo <span style="color:#000088">SET</span> customerid = <span style="color:#006666">14</span>;
    <span style="color:#000088">UPDATE</span> inventory <span style="color:#000088">SET</span> quantity = <span style="color:#006666">11</span> <span style="color:#000088">WHERE</span> item=‘book’;
<span style="color:#000088">COMMIT</span>;</code></span>
  •  

事务的另一个重要作用是当多个用户同时使用相同的数据源时,它可以利用锁定数据库的方法来为用户提供一种安全的访问方式,这样可以保证用户的操作不被其他的用户所干扰。

(5)锁定表 
通过锁定表来防止其他的访问对我们正在操作的表进行插入、更新或者删除的操作。 
例子:

<span style="color:#000000"><code><span style="color:#000088">LOCK</span> <span style="color:#000088">TABLE</span> inventory <span style="color:#000088">WRITE</span> <span style="color:#000088">SELECT</span> quantity <span style="color:#000088">FROM</span> inventory <span style="color:#000088">WHERE</span> item=‘book’;
…
<span style="color:#000088">UPDATE</span> inventory <span style="color:#000088">SET</span> Quantity=<span style="color:#006666">11</span> <span style="color:#000088">WHERE</span> Item=‘book’; UNLOCKTABLES</code></span>
  •  

这里,我们用一个select语句取出初始数据,通过一些计算,用update语句将新值更新到表中。包含有WRITE关键字的LOCKTABLE语句可以保证在UNLOCKTABLES命令被执行之前,不会有其他的访问来对inventory进行插入、更新或者删除的操作。

(6)使用外键 
锁定表的方法可以维护数据的完整性,但是它却不能保证数据的关联性。这个时候我们就可以使用外键。

(7)使用索引 
索引是提高数据库性能的常用方法,它可以令数据库服务器以比没有索引快得多的速度检索特定的行,尤其是在查询语句当中包含有MAX(),MIN()和ORDERBY这些命令的时候,性能提高更为明显。 
对哪些字段建立索引? 
索引应建立在那些将用于JOIN,WHERE判断和ORDERBY排序的字段上。 
全文索引在MYSQL中是一个FULLTEXT类型索引,但仅能用于MYISAM类型的表。对于一个大的数据库,将数据装载到一个没有FULLTEXT索引的表中,然后再使用ALTERTABLE或CREATEINDEX创建索引,将是非常快的。但如果将数据装载到一个已经有FULLTEXT索引的表中,执行过程将会非常慢。

(8)优化查询语句 
1、最好在相同类型的字段间进行比较的操作 
2、在建有索引的字段上尽量不要使用函数进行操作。 
例如,在一个DATE类型的字段上使用YEAR()函数时,将会使索引不能发挥应有的作用。所以下面的两个查询虽然返回的结果一样,但后者要比前者快的多。 
3、在搜索字符型字段时,我们有时会使用LIKE关键字和通配符,这种做法虽然简单,但却也是以牺牲系统性能为代价的。 
例如下面的查询将会比较表中的每一条记录

<span style="color:#000000"><code><span style="color:#000088">SELECT</span> * <span style="color:#000088">FROM</span> books <span style="color:#000088">WHERE</span> name <span style="color:#000088">like</span> “MYSQL%”</code></span>
  •  

但是如果换用下面的查询,返回的结果一样,但速度就要快上很多

<span style="color:#000000"><code><span style="color:#000088">SELECT</span> * <span style="color:#000088">FROM</span> books WEHRE name>=“MYSQL” <span style="color:#000088">and</span> name < “MYSQM”</code></span>
  •  

最后,应该注意避免在查询中让MYSQL进行自动类型转换,因为转换过程也会使索引变得不起作用。

四、 如何查看Mysql执行计划?

mysql的查看执行计划的语句:explain+你要执行的sql语句 
例如:



id是一组数字,表示查询中执行select子句或操作表的顺序。id如果相同,则可以认为是一组,从上往下顺序执行,所有组中,id越高,优先级越高,越容易执行。 
select_type有simple,primary,subquery,derived(衍生),union,unionresult. 
simple表示查询中不包含子查询或者union。 
当查询中包含任何复杂的子部分,最外层的查询被标记成primary。在select或where列表中包含了子查询,则子查询被标记成subquery。在from的列表中包含的子查询被标记成derived。….省略 
参考网址: 
http://www.cnblogs.com/ggjucheng/archive/2012/11/11/2765237.html

mysql执行计划的局限 
1. explain不会告诉你关于触发器、存储过程的信息或用户自定义函数对查询的影响情况。 
2. explain不考虑各种cache 
3. explain不能显示mysql在执行查询时所做的优化工作 
4. 部分统计信息是估算的,并非精确值 
5. explain只能解释select操作,其他操作要重写为select后查看执行计划。

五、 索引是什么? 有什么用? 如何建立? 索引的底层实现是什么? 什么情况下适合建立索引, 什么情况下不适合建立索引?

(1)什么索引? 
数据库索引,是帮助数据库系统高效获取数据的数据结构,以协助快速查询、更新数据库表中数据。

(2)建立索引的目的? 
在数据库系统中建立索引主要有以下作用: 
1. 大大加快数据的检索速度(最主要原因,) 
2. 保证数据记录的唯一性(通过建立唯一性索引,可以保证数据库中每一行数据的唯一性) 
3. 可以加速表和表之间的连接,实现表与表之间的参照完整性 
4. 在使用order by、group by子句(分组和排序子句)进行数据检索时,利用索引可以显著减少分组和排序的时间。

(3)缺点 
1. 索引需要占物理内存 
2. 当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,降低了数据的维护速度

(4)mysql是使用B+树实现其索引结构

(5)创建索引的办法: 
1. 直接创建索引 
例如使用create index语句或者使用创建索引向导 
2. 间接创建索引 
例如在表中定义主键约束或者唯一性键约束时,同时也创建了索引。

当在表中定义主键或者唯一性键约束时,如果表中已经有了使用create index语句创建的标准索引时,那么主键约束或者唯一性键约束创建的索引覆盖以前创建的标准索引。也就是说,主键约束或者唯一性键约束创建的索引的优先级高于使用create index语句创建的索引。

(6)几种索引类型的比较 
聚集索引:物理存储按照索引排序(表中行的物理顺序与键值的索引顺序相同),叶子结点即存储了真实的数据行。 
非聚集索引:物理存储位置不按照索引排序,叶子结点包含索引字段值及指向数据页数据行的逻辑指针。

一张表中只能创建一个聚集索引,但表中的每一列都可以有自己的非聚集索引。

唯一性索引:这一列数据不重复,只能一个为NULL 
主键索引:主键索引是唯一索引的特定类型。不重复,不允许为空。主键只能有一个。 
普通索引:create index等建立的索引,alter tablename add index …

(7) 外键约束的要求: 
1. 父表和子表必须使用相同的存储引擎,而且禁止使用临时表。 
2. 数据表的存储引擎只能为InnoDB。 
3. 外键列和参照列必须具有相似的数据类型。其中数字的长度或是否有符号位必须相同;而字符的长度则可以不同。 
4. 外键列和参照列必须创建索引。如果外键列不存在索引的话,MySQL将自动创建索引。

六、 什么是存储过程?有什么好处?

(1)什么是存储过程? 
存储过程是一个被定义并保存在数据库服务器中的sql语句集,是一种介于应用程序和数据库间的编程接口,也是封装重复性工作的一种有效方法,它支持用户变量、条件执行及其它的编程功能。

(2)存储过程的语法

<span style="color:#000000"><code><span style="color:#000088">CREATE</span> <span style="color:#000088">PROCEDURE</span> <span style="color:#009900">procedure_name</span><span style="color:#4f4f4f">([paramters[,...]])</span>[<span style="color:#009900">attributes</span>]
<span style="color:#009900">BEGIN</span>
    <span style="color:#009900">body_statement</span>
<span style="color:#009900">END</span>;</code></span>
  •  

注: 
procedure_name:存储过程的名字 
paramters:存储过程的过程参数,包含:IN、OUT及INOUT。IN代表输入或传入值,在存储过程中被修改,但不返回;OUT代表输出或传出值,在存储过程中被修改,并返回;INOUT代表输入输出,在存储过程中被修改,并返回;body_statement:存储过程体,这里可以放入sql集,也可以内嵌存储过程。

好处: 
相对于直接使用sql语句,在应用程序中直接调用存储过程有以下好处: 
(1)减少网络通信量。调用一个行数不多的存储过程与直接调用sql语句的网络通信量可能不会有很大的差别,可是如果存储过程包含上百行sql语句,那么其性能绝对比一条一条的调用sql语句要高的多。 
(2)执行速度更快。有两个原因:首先,在存储过程创建的时候,数据库已经对其进行了一次解析和优化。其次,存储过程一旦执行,在内存中就会保留一份这个存储过程,这样下次再执行同样的存储过程时,可以从内存中直接调用。 
(3)更强的适应性:由于存储过程对数据库的访问时通过存储过程来进行的,因此数据库开发人员可以在不改动存储过程接口的情况下对数据库进行任何改动,而这些改动不会对应用程序造成影响。 
(4)布式工作:应用程序和数据库的编码工作可以分别独立进行,而不会相互压制。

七、 乐观锁和悲观锁是什么?

乐观锁和悲观锁是两种并发控制机制。 
首先了解一下为什么需要锁(并发控制)? 
在多用户环境中,在同一时间可能会有多个用户更新相同的记录,这会产生冲突。这就是著名的并发性问题

典型的冲突有: 
(1)丢失更新:一个事务的更新覆盖了其他事务的更新结果,这就是所谓的更新丢失。例如:用户A把值从6改为2,用户B把值从2改为6,则用户A丢失了他的更新。 
(2)脏读:当一个事务读取其它完成一半事务的记录时,就会发生脏读取。例如:用户A,B看到的值都是6,用户B把值改为2,用户A读到的值仍为6. 
为了解决这些并发带来的问题,我们需要引入并发控制机制。

并发控制机制: 
最常用的处理多用户并发访问的方法是加锁。当一个用户锁住数据库中的某个对象时,其他用户就不能再访问该对象。加锁对并发访问的影响体现在锁的粒度上。比如,放在一个表上的锁限制对整个表的并发访问;放在数据页上的锁限制了对整个数据页的访问;放在行上的锁只限制对该行的并发访问。可见行锁粒度最小,并发访问最好,页锁粒度最大,表锁介于两者之间。

悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。 
悲观锁假定其他用户企图访问或者改变你正在访问、更改的对象的概率是很高的,因此在悲观锁的环境中,在你开始改变此对象之前就将该对象锁住,并且直到你提交了所作的更改之后才释放锁。悲观的缺陷是不论是页锁还是行锁,加锁的时间可能会很长,这样可能会长时间的限制其他用户的访问,也就是说悲观锁的并发访问性不好。

乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。 
乐观锁不能解决脏读的问题。乐观锁则认为其他用户企图改变你正在更改的对象的概率是很小的,因此乐观锁直到你准备提交所作的更改时才将对象锁住,当你读取以及改变该对象时并不加锁。可见乐观锁加锁的时间要比悲观锁短,乐观锁可以用较大的锁粒度获得较好的并发访问性能。但是如果第二个用户恰好在第一个用户提交更改之前读取了该对象,那么当他完成了自己的更改进行提交时,数据库就会发现该对象已经变化了,这样,第二个用户不得不重新读取该对象并作出更改。这说明在乐观锁环境中,会增加并发用户读取对象的次数。

乐观锁的应用: 
(1)使用自增长的整数表示数据版本号。更新时检查版本号是否一致,比如数据库中数据版本为6,更新提交时version=6+1,使用该version值(=7)与数据库version+1(=7)作比较,如果相等,则可以更新,如果不等则有可能其他程序已更新该记录,所以返回错误。 
(2)使用时间戳来实现。

悲观锁应用: 
需要使用数据库的锁机制,比如SQL server的TABLOCKX(排它表锁)此选项被选中时,SQL server将在整个表上置排它锁直至该命令或事务结束。这将防止其他进程读取或修改表中的数据。

总结: 
在实际生产环境里边,如果并发量不大且不允许脏读,可以使用悲观锁解决并发问题;但如果系统的并发非常大的话,悲观锁定会带来非常大的性能问题,所以我们就要选择乐观锁定的方法。

八、 表锁、页锁、行锁的区别?

行级锁是mysql中锁定粒度最细的一种锁,表示只针对当前操作的行进行加锁。行级锁能大大减少数据库操作的冲突。其加锁粒度最小,但加锁的开销也最大。行级锁分为共享锁和排他锁。 
特点:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。

表级锁是mysql中锁定粒度最大的一种锁,表示对当前操作的整张表加锁,它实现简单,资源消耗较少,被大部分Mysql引擎支持。最常使用的myisam和innodb都支持表级锁定。表级锁定分为表共享读锁(共享锁)和表独占写锁(排他锁)。 
特点:开销小,加锁快;不会出现死锁;锁定粒度大,发出锁冲突的概率最高,并发度最低。

页级锁是mysql钟锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快,但冲突多,行级冲突少,但速度慢。所以取了折中的页级,一次锁定相邻的一组记录。BDB支持页级表。 
特点:开销和加锁时间介于表锁和行锁之间;会出现死锁;锁定粒度介于表锁和行锁之间,并发度一般。

上述三种锁的特性可大致归纳如下: 
1) 表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。 
2) 行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
3) 页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。

九、 having和where的区别?

where语句在group by语句之前,sql会在分组之前计算where语句。 
having语句在group by语句之后,sql会在分组之后计算having语句。

where是一个约束声明,使用where约束来自数据库的数据,where是在结果返回之前起作用的,where中不能使用聚合函数。having是一个过滤声明,是在查询返回结果集以后对查询结果进行的过滤操作,在having中可以使用聚合函数。 
在查询过程中聚合语句(sum,min,max,avg,count)要比having子句优先执行。而where子句在查询过程中执行优先级高于聚合语句。

例一:要查找平均工资大于3000的部门,则sql语句应为:

<span style="color:#000000"><code><span style="color:#000088">select</span> department, <span style="color:#009900">avg</span>(salary) <span style="color:#000088">as</span> average <span style="color:#000088">from</span> salary_info
<span style="color:#000088">group</span> <span style="color:#000088">by</span> department <span style="color:#000088">having</span> average><span style="color:#006666">3000</span></code></span>
  •  

此时只能使用having,而不能使用where。一来,我们要使用聚合语句avg;二来,我们要对聚合后的结果进行筛选(average>3000),因此使用where会被告知sql有误。

例二:要查询每个部门工资大于3000的员工个数

<span style="color:#000000"><code><span style="color:#000088">select</span> department, <span style="color:#009900">count</span>(*) <span style="color:#000088">as</span> c <span style="color:#000088">from</span> salary_info
<span style="color:#000088">where</span> salary><span style="color:#006666">3000</span> <span style="color:#000088">group</span> <span style="color:#000088">by</span> department.</code></span>
  •  

此处的where不可用having进行替换,因为是直接对库中的数据进行筛选,而非对结果集进行筛选。

where和having的执行级别不同 
在查询过程中聚合语句(sum,min,max,avg,count)要比having子句优先执行.而where子句在查询过程中执行优先级别优先于聚合语句(sum,min,max,avg,count)。 
having就是来弥补where在分组数据判断时的不足。因为where执行优先级别要快于聚合语句。

十、 事务的隔离级别?

隔离级别 脏读 不可重复读 幻读
未提交读 可能 可能 可能
已提交读 不可能 可能 可能
可重复读 不可能 不可能 可能
可串行化 不可能 不可能 不可能


未提交读:允许脏读,也就是可能读取到其他会话中未提交事务修改的数据。 
已提交读:只能读取到已经提交的数据。Oracle等多数数据库默认是该级别(不重复读)。 
可重复读:在同一个事务内的查询都是事务开始时刻一致的,innodb默认级别。在sql标准中,该隔离级别消除了不可重复读,但是还存在幻象读。 
串行读:完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞。

四个级别逐渐增强,每个级别解决一个问题。事务级别越高,性能越差

脏读:是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。

不可重复读:是指在一个事务中,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。

可重复读:第一个事务两次读到的数据是一样的。

幻读:第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改了这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好像发生了幻觉一样。 
本事务中第一次读取出一行,做了一次更新后,另一个事务里提交的数据就出现了。也可以看做是一种幻读。

一道笔试题: 
小桔A在事务1中第一次读取年龄20岁的员工总数为1000人,之后小桔B在事务2中增加了100名年龄20岁的新员工,之后小桔A在事务1中再次读取年龄20岁的员工数发现总数变为1100人,属于()? 
A、脏读 
B、不可重复读 
C、幻读 
选C 
幻读的重点在于新增或者删除 
同样的条件,第一次和第二次读出来的记录数不一样 
例子: 
目前工资为1000的员工有10人 
事务1,读取所有工资为1000的员工,共读取10条记录。 
这时另一个事务向employee表插入了一条员工记录。 
事务1再次读取所有工资为1000的员工共读取到了11条记录,这就产生了幻像读

不可重复读的重点是修改 
同样的条件,你读取过的数据,再次读取出来发现值不一样了 
例如: 
在事务1中,Mary读取了自己的工资为1000,操作并没有完成。 
在事务2中,这时财务人员修改了Mary的工资为2000,并提交了事务。 
在事务1中,Mary再次读取自己的工资时,工资变味了2000。

脏读: 
脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。

十一、 事务的特点有哪些?

ACID含义: 
原子性(Atomicity):一个事务要么发生,要么不发生。 
例如故障发生在write(A)和read(B)之间,则将有可能造成账户A的余额已经减少50元钱,而账户B的余额却没有改变,凭空就少了50元钱。 
一致性(Consistency):数据库中数据的完整性,保证他们的正确性。 
隔离性(Isolation):多个事务并发(同时)执行,互相不影响 
持久性(Durability):每个事务成功执行后对数据库的修改是永久的。即使系统出现故障也不受影响。

十二、 触发器是什么?

触发器是SQL server提供给程序员和数据分析员来保证数据完整性的一种方法,它是与表事件相关的特殊存储过程,它的执行不是由程序调用,也不是手工启动,而是由事件触发。比如当对一个表进行操作(insert,delete,update)时就会激活它执行。触发器经常用于加强数据的完整性约束和业务规则等。

在mysql中,创建触发器语法如下:

<span style="color:#000000"><code><span style="color:#000088">Create</span> <span style="color:#000088">trigger</span> trigger_name trigger_time trigger_event <span style="color:#000088">on</span> tbl_name <span style="color:#000088">for</span> <span style="color:#000088">each</span> <span style="color:#000088">row</span> trigger_stmt</code></span>
  • 1

其中: 
Trigger_name:标识触发器名称,用户自行指定 
trigger_time:标识触发时机,取值为before或after 
Trigger_event:标识触发事件,取值为insert,update或delete 
Tbl_name:标识建立触发器的表名,即在哪张表上建立触发器 
Trigger_stmt:触发器程序体,可以是一句sql语句,或者用begin和end包含的多条语句。

由此可见,可以建立6种触发器,即:before insert、before update、before delete、after insert、after update、after delete。 
注意:不能同时在一个表上建立2个相同类型的触发器,因此在一个表上最多建立6个触发器。

SQL Server包括三种常规类型的触发器:DML触发器、DDL触发器和登录触发器

DML触发器 
当数据库中表中的数据发生变化时,包括insert,update,delete任意操作,如果我们对该表写了对应的DML触发器,那么该触发器自动执行。DML触发器的主要作用在于强制执行业务规则,以及扩展sql server约束,默认值等。因为我们知道约束只能约束同一个表中的数据,而触发器中则可以执行任意sql命令。

DDL触发器 
主要用于审核与规范对数据库中表,触发器,视图等结构上的操作。比如在修改表,修改列,新增表,新增列等。它在数据库结构发生变化时执行,我们主要用它来记录数据库的修改过程,以及限制程序员对数据库的修改,比如不允许删除某些指定表等。

登录触发器 
登录触发器将为响应LOGIN事件而激发存储过程。与SQL server实例建立用户会话时将引发此事件。登陆触发器将在登录的身份验证阶段完成之后且用户会话实际建立之前激发。因此,来自触发器内部且通常将到达用户的所有消息会传送到SQL Server错误日志。如果身份验证失败,将不激发登录触发器。

触发器的作用: 
1、可在写入数据表前,强制检验或转换数据 
2、触发器发生错误时,异动的结果会被撤销 
3、部分数据库管理系统可以针对数据定义语言(DDL)使用触发器,称为DDL触发器 
4、可依照特定的情况,替换异动的指令(INSTEAD OF)

DML触发器又可分为After触发器和Instead Of触发器 
(1)After触发器:这类触发器是在记录已经改变完之后(after),才会被激活执行,它主要是用于记录变更后的处理或检查,一旦发现错误,也可以用Rollback Transaction语句来回滚本次的操作。 
(2)Instead of触发器:这类触发器一般是用来取代原本的操作,在记录变更之前发生的,它并不去执行原来SQL语句里的操作(insert、update、delete),而去执行触发器本身所定义的操作。 
在SQL Server里,每个DML触发器都分配有两个特殊的表,一个是Inserted表,一个是Deleted表。它们两个存在于数据库服务器的内存中,是由系统管理的逻辑表,是两个临时表,而不是真正存储在数据库中的物理表。用户对这两个表只有读取的权限,没有修改的权限。 
这两个表的结构(主外键、字段、数据类型等)与触发器所在数据表的结构是完全一致的,当触发器的工作完成之后,这两个表也将会从内存中删除。

Inserted和Deleted两个表的作用: 
Inserted:对于插入记录操作来说,插入表里存放的是要插入的数据;对于更新记录操作来说,插入表里存放的是要更新的记录。 
Deleted:对于更新记录操作来说,删除表里存放的是被更新记录;对于删除记录操作来说,删除表里存入的是被删除的旧记录。

 

激活触发器的操作 Inserted表 Deleted表
Insert 存放要插入到表中的数据
Update 存放要更新到表中的数据 存放被更新的记录
Delete 存放要删除的记录


网上找的一张图,可以帮助理解一下过程。 


注意事项: 
(1)只有表才可以支持触发器,视图和临时表都不支持触发器 
(2)每个表的每个事件只支持一个触发器,因此每个表最多支持6个触发器(触发器应该相应的行动insert,delete,update,触发器何时执行before,after,2*3=6种) 
(3)单一触发器不能与多个操作相关 
(4)触发器不能更新和覆盖,如果想更新一个触发器必须先删除,再创建。

十三、 为什么大部分数据库索引的实现使用B+树而不是哈希表或红黑树之类的?

前言 
动态查找树主要有:二叉查找树,平衡二叉查找树,红黑树,B- tree/B+ tree/B* tree。前三者是典型的二叉查找树结构,其查询的时间复杂度都与树的深度相关,那么降低树的深度自然会提高查找效率。

磁盘存储的知识 
计算机存储设备一般分为两种:内存储器(main memory)和外存储器(external memory)。内存存取速度快,但容量小,价格昂贵,而且不能长期保存数据(在不通电情况下数据会消失)

索引一般以文件形式存储在磁盘上,索引检索需要磁盘I/O操作。与主存不同,磁盘I/O存在机械运动耗费,因此磁盘I/O的时间消耗是巨大的。从网上找了一张磁盘构造图如下所示: 

除了最顶端和最底端的外侧面不存储数据之外,一共有4个面可以用来保存信息。当磁盘驱动器执行读/写功能时,盘片装在一个主轴上,并绕主轴高速旋转,当磁盘在读/写头(又叫磁头)下通过时,就可以进行数据的读/写了。

盘片被划分成一系列同心环,圆心是盘片中心,每个同心环叫做一个磁道,所有半径相同的磁道组成一个柱面。磁道被沿半径线划分成一个个小的段,每个段叫做一个扇区,每个扇区是磁盘的最小存储单元。

当需要从磁盘读取数据时,系统会将数据逻辑地址传给磁盘,磁盘的控制电路按照寻址逻辑地址翻译成物理地址,即确定要读的数据在哪个磁道,哪个扇区。为了读取这个扇区的数据,需要将磁头放到这个扇区上方,为了实现这一点,磁头需要移动对准相应磁道,这个过程叫做寻道,所耗费时间叫做寻道时间,然后磁盘旋转将目标扇区旋转到磁头下,这个过程耗费的时间叫做旋转时间。

读取数据顺序:先找到磁道(寻道时间)、然后旋转到目标扇区(旋转时间)、然后读取数据

局部性原理与磁盘预读 
由于存储介质的特性,磁盘本身存取就比主存慢很多,再加上机械运动耗费,磁盘的存取速度往往是主存的几百分之一,因此为了提高效率,要尽量减少磁盘I/O。为了达到这个目的,磁盘往往不是严格按需读取,而是每次都会预读,即使只需要一个字节,磁盘也会从这个位置开始,顺序向后读取一定长度的数据放入内存。这样做的理论依据是计算机科学中著名的局部性原理。

所谓的局部性原理是指:当一个数据被用到时,其附近的数据也通常会马上被使用。程序运行期间所需要的数据通常比较集中。由于磁盘顺序读取的效率很高(不需要寻道时间,只需很少的旋转时间),因此对于具有局部性的程序来说,预读可以提高I/O效率。

预读的长度一般为页的整倍数。页式计算机管理存储器的逻辑块,硬件及操作系统往往将主存和磁盘存储区分割为连续的大小相等的块,每个存储块称为一页(在许多操作系统中,页的大小通常为4K),主存和磁盘以页为单位交换数据。当程序要读取的数据不在主存中时,会触发一个缺页异常,此时系统会向磁盘发出读盘信号,磁盘会找到数据的起始位置并向后连续读取一页或几页载入内存中,然后异常返回,程序继续运行。

为什么目前大部分数据库系统及文件系统都采用B树或B+树作为索引结构? 
1、红黑树 
红黑树的定义:一种自平衡二叉查找树,但是每个结点上增加一个存储位表示结点的颜色,通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出两倍,因而是接近平衡的。

红黑树的特性: 
首先,作为一棵二叉查找树,具备二叉查找树的特性: 
1、若任意结点的左子树不空,则左子树上所有结点的值均小于它的根节点的值。 
2、若任意结点的右子树不空,则右子树上所有结点的值均大于它的根节点的值。 
3、任意结点的左、右子树也分别为二叉查找树。 
4、没有键值相等的结点 
红黑树的另外5个特性,保证了一棵n个结点的红黑树的高度始终保持在logn,也就解释了“红黑树的查找、插入、删除的时间复杂度最坏是o(logn)” 
1、每个结点要么是红的要么是黑的 
2、根节点是黑色的 
3、每个叶节点(叶节点即树尾端NIL指针或NULL结点)都是黑的 
4、如果一个结点时红的,那么它的两个儿子都是黑的 
5、对于任意结点而言,其到叶节点树尾端NIL指针的每条路径都包含相同数目的黑结点。 

2、B树 
B树与红黑树最大的不同在于,B树的结点可以有许多子女,从几个到几千个。为什么又说B树与红黑树很相似呢?因为与红黑树一样,一棵含n个结点的B树的高度也为o(lgn),但可能比一棵红黑树的高度小许多,因为它的分支因子比较大。所以,B树可以在o(logn)时间内,实现各种如插入、删除等动态集合操作。 

这是一棵B树,一颗关键字为英语中辅音字母的B树,现在要从树中查找字母R,一个内结点x若含有n[x]个关键字,那么x将含有n[x]+1个子女,例如2个关键字D H的内结点有3个子女,而含有3个关键字Q T X的内结点有4个子女。所有的叶结点都处于相同的深度,背景为白色的结点为查找字母R时要检查的结点。

一棵m阶的B树的特性如下: 
1、每个结点至多有m棵子树 
2、除根节点外,其他每个分支结点至少有ceil(m/2)棵子树(ceil是一个向上取整的函数) 
3、根节点至少有两棵子树(除非B树只包含一个结点) 
4、所有叶结点在同一层上。B树的叶节点可以看成一种外部结点,不包含任何信息。 
5、有j个孩子的非叶结点恰好有j-1个关键码,关键码按递增次序排列。

3、B+树 

一棵m阶的B+树和m阶的B树的异同点在于: 
1、B+树:有n棵子树的结点含有n个关键字,每个关键字不保存数据,只用来索引,所有数据都保存在叶子结点 
B树:有n棵子树的结点还有n-1个关键字。 
2、B+树:所有的叶子结点中包含了全部关键字的信息,及指向含有这些关键字记录的指针,且叶子结点本身依关键字的大小自小而大的顺序链接。 
3、B+树:所有的非终端结点可以看成是索引部分,结点中仅含有其子树根节点中最大(或最小)的关键字。 
通常在B+树上有两个头指针,一个指向根节点,一个指向关键字最小的叶子结点

为什么说B+树比B树更适合实际应用中操作系统的文件索引和数据库索引? 
(1)B+树的磁盘读写代码更低 
B+树的内部结点并没有指向关键字具体信息的指针,因此其内部结点相比B树更小。如果把所有同一内部结点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多。一次性读入内存中的需要查找的关键字也就越多。相对来说IO读写次数也就降低了。 
(2)B+树的查询效率更加稳定 
由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根节点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。

版权声明:本文为博主原创文章,转载请标明原文地址,谢谢 ^_^ https://blog.csdn.net/xiaoquantouer/article/details/71036439

版权声明:本文为qq_34802511原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_34802511/article/details/81476005