MySQL面试题

目录

一、连接查询

二、聚合函数

三、SQL注入

四、Select语句完整的执行顺序

五、存储引擎

六、索引

七、数据库三范式

八、数据库事务

九、存储过程

十、触发器

十一、数据库并发策略

十二、数据库锁

十三、基于 Redis 分布式锁

十四、分库分表

十五、读写分离

十六、常用30种SQL查询语句优化方法


一、连接查询

  1. 外连接

1)左连接(左外连接)以左表为基准进行查询,左表数据会全部显示出来,右表如果和左表匹配则显示相应字段数据,如果不匹配,则显示为NULL;

SELECT column_name(s)
FROM table1
LEFT JOIN table2
ON table1.column_name = table2.column_name;

2)右连接相反

SELECT column_name(s)
FROM table1
RIGHT JOIN table2
ON table1.column_name = table2.column_name;

3)全连接就是先以左表进行左外连接,然后以右表进行右外连接。

SELECT column_name(s)
FROM table1
FULL OUTER JOIN table2
ON table1.column_name = table2.column_name;
  1. 内连接:

显示表之间有连接匹配的所有行。

SELECT column_name(s)
FROM table1
INNER JOIN table2
ON table1.column_name = table2.column_name;

二、聚合函数

聚合函数是对一组值执行计算并返回单一的值的函数,它经常与 SELECT语句的 GROUP BY 子句一同使用。

1 )AVG() 返回数值列的平均值,空值被忽略。

SELECT AVG(column_name) FROM table_name;

2 )COUNT ()返回表中的行数(包括NULL和非NULL行)或特定列的行数。

SELECT COUNT(*) FROM table_name; -- 返回表中的总行数
SELECT COUNT(column_name) FROM table_name; -- 返回指定列的非NULL行数

3 )MIN()返回数值列的最小值或字符列的最小字符串;MAX()相反;SUM()返回数值列的总和。

SELECT MIN(column_name) FROM table_name;
SELECT MAX(column_name) FROM table_name;
SELECT SUM(column_name) FROM table_name;

4)使用 group by 子句对数据进行分组;对 group by 子句形成的组运行聚集函数计算每一组的值,最后用 having 子句去掉不符合条件的组,having 子句中的每一个元素也必须出现在 select 列表中。有些数据库例外,如 oracle.

-- 假设的 sales 表结构
-- region | product | revenue
-- ---------------------------
-- East   | A       | 100
-- West   | A       | 150
-- East   | B       | 200
-- West   | B       | 250
-- East   | C       | 300
-- West   | C       | 350
现在,我们想要找出每个地区的总收入,并且只显示总收入超过1000的地区。

SELECT region, SUM(revenue) AS total_revenue
FROM sales
GROUP BY region
HAVING SUM(revenue) > 1000;

三、SQL注入

SQL注入(SQL Injection)是一种常见的网络攻击技术,它允许攻击者通过在Web应用程序的输入字段中插入恶意SQL代码来控制数据库。这种攻击可以读取、修改、删除数据库中的数据,甚至在某些情况下,可以完全控制数据库服务器。

SELECT * FROM users WHERE username = '' OR '1'='1' --' 
AND password = '[用户输入的密码]'

防止 SQL 注入,使用预编译语句是预防 SQL 注入的最佳方式,如select admin from user where username= ? And password=? 使用预编译的 SQL 语句语义不会发生改变,在 SQL 语句中,变量用问号?表示。像上面例子中, username 变量传递的 'admin' or 'a'='a' 参数也只会当作 username 字符串来解释查询,从根本上杜绝了 SQL 注入攻击的发生。注意:使用 mybaits 时 mapper 中#方式能够很大程度防止 SQL 注入,$方式无法防止 SQL 注入。

四、Select语句完整的执行顺序

查询中用到的关键词主要包含六个,并且他们的顺序依次为select--from--where--group by--having--order by其中 select 和 from 是必须的,其他关键词是可选的,这六个关键词的执行顺序如下

select :查看结果集中的哪个列,或列的计算结果

from :需要从哪个数据表检索数据

where 过滤表中数据的条件

group by :如何将上面过滤出的数据分组

having :对上面已经分组的数据进行过滤的条件

order by :按照什么样的顺序来查看返回的数据

五、存储引擎

  1. InnoDB

InnoDB 底层存储结构为 B+ 树, B 树的每个节点对应 innodb 的一个 page,page 大小是固定的,一般设为 16k 。其中非叶子节点只有键值,叶子节点包含完整数据。

适用场景:

1 )经常更新的表,适合处理多重并发的更新请求。

2 )支持事务。

3 )可以从灾难中恢复(通过 bin-log 日志等

4 )外键约束。只有他支持外键。

5 )支持自动增加列属性 auto-incremento

  1. MyIASM

MyIASM 是 MySQL 默认的引擎,但是它没有提供对数据库事务的支持,也不支持行级锁和外键,因此当 INSERT( 插入)或 UPDATE (更新)数据时即写操作需要锁定整个表,效率便会低一些。

ISAM 执行读取操作的速度很快,而且不占用大量的内存和存储资源。在设计之初就预想数据组织成有固定长度的记录,按顺序存储的。 ---ISAM 是一种静态索引结构。缺点是它不支持事务处理。

  1. Memory

Memory (也叫 HEAP )堆内存:使用存在内存中的内容来创建表。每个MEMORY 表只实际对应一个磁盘文件。 MEMORY 类型的表访问非常得快,因为它的数据是放在内存中的,并且默认使用 HASH 索引。但是一旦服务关闭,表中的数据就会丢失掉。 Memory 同时支持散列索引和 B 树索引, B 树索引可以使用部分查询和通配查询,也可以使用和>=等操作符方便数据挖掘,散列索引相等的比较快但是对于范围的比较慢很多。

六、索引

  1. 加快查询,减缓更新
  2. 主键索引,唯一索引,单值索引,复合索引,全文索引。
  3. 常见索引原则有

选择唯一性索引:唯一性索引的值是唯一的,可以更快速的通过该索引来确定某条记录。

为经常需要排序.分组和联合操作的字段建立索引.

为常作为查询条件的字段建立索引。

限制索引的数目:越多的索引,会使更新表变得很浪费时间。

尽量使用数据量少的索引:如果索引的值很长,那么查询的速度会受到影响。

尽量使用前缀来索引:如果索引字段的值很长,最好使用值的前缀来索引。

删除不再使用或者很少使用的索引。

最左前缀匹配原则,非常重要的原则。

尽量选择区分度高的列作为索引:区分度的公式是表示字段不重复的比例

索引列不能参与计算,保持列"干净':带函数的查询不参与索引。

尽量的扩展索引,不要新建索引。

七、数据库三范式

范式是具有最小冗余的表结构。

  1. 第一范式(同一列中不能有多个值)

第一范式的目标是确保每列的原子性:如果每列都是不可再分的最小数据单元(也称为最小的原子单元 ), 则满足第一范式( INF ),第一范式( INF )要求数据库表的每一列都是不可分割的基本数据项,同一列中不能有多个值。

若某一列有多个值,可以将该列单独拆分成一个实体,新实体和原实体间是一对多的关系。在任何一个关系数据库中,第一范式( INF )是对关系模式的基本要求,不满足第一范式( INF )的数据库就不是关系数据库。第一范式是最基本的范式。如果数据库表中的所有字段值都是不可分解的原子值,就说明该数据库表满足了第一范式。

第一范式的合理遵循需要根据系统的实际需求来定。比如某些数据库系统中需要用到"地址"这个属性,本来直接将"地址"属性设计成一个数据库表的字段就行。但是如果系统经常会访问"地址"属性中的"城市"部分,那么就非要将"地址"这个属性重新拆分为省份.城市.详细地址等多个部分进行存储,这样在对地址中某一部分操作的时候将非常方便。这样设计才算满足了数据库的第一范式

  1. 第二范式(不能把多种数据保存在同一张表中)

首先满足第一范式,并且表中非主键列不存在对主键的部分依赖。第二范式要求每个表只描述一件事情。

满足第二范式必须先满足第一范式。

第二范式要求实体中没有任何一行的所有非主属性都必须完全依赖于主键,即:非主属性必须完全依赖于主键。

完全依赖:主键可能由多个属性构成,完全依赖要求不允许存在非主属性依赖于主键中的某一部分属性。若存在哪个非主属性依赖于主键中的一部分属性,那么要将发生部分依赖的这一组属性单独新建一个实体,并且在旧实体中用外键与新实体关联,并且新实体与旧实体间是一对多的关系。

第二范式在第一范式的基础之上更进一层。第二范式需要确保数据库表中的每一列都和主键相关,而不能只与主键的某一部分相关(主要针对联合主键而言)也就是说在一个数据库表中,一个表中只能保存一种数据,不可以把多种数据保存在同一张数据库表中。

  1. 第三范式(不能冗余)

第三范式定义是,满足第二范式,并且表中的列不存在对非主键列的传递依赖。除了主键订单编号外,顾客姓名依赖于非主键顾客编号。

满足第三范式必须先满足第二范式。

第三范式要求:实体中的属性不能是其他实体中的非主属性。因为这样会出现冗余。即属性不依赖于其他非主属性。

如果一个实体中出现其他实体的非主属性,可以将这两个实体用外键关联,而不是将另一张表的非主属性直接写在当前表中。第三范式需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关。

八、数据库事务

  1. 事务 (TRANSACTION )是作为单个逻辑工作单元执行的一系列操作,这些操作作为一个整体一起向系统提交,要么都执行要么都不执行。事务是一个不可分割的工作逻辑单元,事务必须具备以下四个属性,简称 ACID 属性

A 原子性( Atomicity ):事务是一个完整的操作。事务的各步操作是不可分的(原子的);要么都执行,要么都不执行。

B 一致性( Consistency ):当事务完成时,数据必须处于一致状态。

C 隔离性( lsolation ):对数据进行修改的所有并发事务是彼此隔离的,这表明事务必须是独立的,它不应以任何方式依赖于或影响其他事务。

D 永久性( Durability ):事务完成后,它对数据库的修改被永久保持,事务日志能够保持事务的永久性。

  1. 事务控制语句

BEGIN 或 START TRANSACTION 显式地开启一个事务;

COMMIT 也可以使用 COMMIT WORK ,不过二者是等价的。COMMIT 会提交事务,并使已对数据库进行的所有修改成为永久性的;

ROLLBACK 也可以使用 ROLLBACK WORK ,不过二者是等价的。回滚会结束用户的事务,并撤销正在进行的所有未提交的修改;

SAVEPOINT identifier , SAVEPOINT 允许在事务中创建一个保存点,一个事务中可以有多个 SAVEPOINT ;

RELEASE SAVEPOINT identifier 删除一个事务的保存点,当没有指定的保存点时,执行该语句会抛出一个异常;

ROLLBACK TO identifier 把事务回滚到标记点;

SET TRANSACTION 用来设置事务的隔离级别。 InnoDB 存储引擎提供事务的隔离级别有

  1. READ UNCOMMITTED(读取未提交):这是最低的隔离级别,它允许读取尚未提交的数据变更,可能会导致脏读、幻读和不可重复读。
  2. READ COMMITTED(读取已提交):它允许事务读取已经被其他事务提交的数据变更。它可以防止脏读,但幻读和不可重复读仍然可能发生。
  3. REPEATABLE READ(可重复读):这是InnoDB存储引擎的默认隔离级别。它确保在同一个事务中多次读取同样的记录结果是一致的,可以避免脏读和不可重复读,但幻读仍然可能发生。
  4. SERIALIZABLE(可串行化):这是最高的隔离级别,它通过强制事务串行化,避免了脏读、不可重复读和幻读的问题。但这也可能导致大量的超时和锁竞争问题,因此实际应用中很少使用这个隔离级别。

  1. MySQL事务处理主要有两种方法

1 )用 BEGIN, ROLLBACK, COMMIT 来实现

2 )直接用 SET 来改变 MySQL 的自动提交模式:

a) SET AUTOCOMMIT=0 禁止自动提交

b) SET AUTOCOMMIT=1 开启自动提交

九、存储过程

一组为了完成特定功能的 SQL 语句集,存储在数据库中,经过第一次编译后再次调用不需要再次编译,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。存储过程是数据库中的一个重要对象。存储过程优化思路:

  1. 尽量利用一些 SQL 语句来替代一些小循环,例如聚合函数,求平均函数等。
  2. 中间结果存放于临时表,加索引。
  3. 少使用游标。 SQL 是个集合语言,对于集合运算具有较高性能。而cursors 是过程运算。比如对一个 100 万行的数据进行查询。游标需要读表100 万次,而不使用游标则只需要少量几次读取。
  4. 事务越短越好。 SQLserver 支持并发操作。如果事务过多过长,或者隔离级别过高,都会造成并发操作的阻塞,死锁。导致查询极慢, cpu 占用率极低。
  5. 使用 try-catch 处理错误异常。
  6. 查找语句尽量不要放在循环内。

十、触发器

触发器是一段能自动执行的程序,是一种特殊的存储过程,触发器和普通的存储过程的区别是:触发器是当对某一个表进行操作时触发。诸如update.insert.delete 这些操作的时候,系统会自动调用执行该表上对应的触发器。 SQL Server 2005 中触发器可以分为两类: DML 触发器和 DDL 触发器,其中 DDL 触发器它们会影响多种数据定义语言语句而激发,这些语句有create. alter.drop 语句。

十一、数据库并发策略

并发控制一般采用三种方法,分别是乐观锁和悲观锁以及时间戳。

乐观锁

乐观锁认为一个用户读数据的时候,别人不会去写自己所读的数据;悲观锁就刚好相反,觉得自己读数据库的时候,别人可能刚好在写自己刚读的数据,其实就是持一种比较保守的态度;时间戳就是不加锁,通过时间戳来控制并发出现的问题。

悲观锁

悲观锁就是在读取数据的时候,为了不让别人修改自己读取的数据,就会先对自己读取的数据加锁,只有自己把数据读完了,才允许别人修改那部分数据,或者反过来说,就是自己修改某条数据的时候,不允许别人读取该数据,只有等自己的整个事务提交了,才释放自己加上的锁,才允许其他用户访问那部分数据。

两种锁的使用场景

从上面对两种锁的介绍,我们知道两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下(多读场景),即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果是多写的情况,一般会经常产生冲突,这就会导致上层应用会不断的进行 retry ,这样反倒是降低了性能,所以一般多写的场景下用悲观锁就比较合适。

乐观锁常见的两种实现方式

  1. 版本号机制

一般是在数据表中加上一个数据版本号 version 字段,表示数据被修改的次数,当数据被修改时, version 值会加一。当线程 A 要更新数据值时,在读取数据的同时也会读取 version 值,在提交更新时若刚才读取到的 version 值为当前数据库中的 version 值相等时才更新,否则重试更新操作,直到更新成功。

  1. CAS 算法即 compare and swap (比较与交换),是一种有名的无锁算法。

无锁编程,即不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步( Non-blockingSynchronization )

CAS 算法涉及到三个操作数

需要读写的内存值 v

进行比较的值 A

拟写入的新值 B

当且仅当 v 的值等于 A 时, CAS 通过原子方式用新值 B 来更新 v 的值,否则不会执行任何操作(比较和替换是一个原子操作)。一般情况下是一个自旋操作,即不断的重试。

乐观锁的缺点

ABA 问题

如果一个变量 v 初次读取的时候是 A 值,并且在准备赋值的时候检查到它仍然是 A 值,那我们就能说明它的值没有被其他线程修改过了吗?很明显是不能的,因为在这段时间它的值可能被改为其他值,然后又改回 A ,那 CAS 操作就会误认为它从来没有被修改过。这个问题被称为 CAS 操作的 "ABA '问题。JDK 1.5 以后的 AtomicStampedReference 类就提供了此种能力其中的 compareAndSet 方法就是首先检查当前引用是否等于预期引用并且当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。

循环时间长开销大

自旋 CAS (也就是不成功就一直循环执行直至成功)如果长时间不成功会给 CPU 带来非常大的执行开销。如果 JVM 能支持处理器提供的 pause 指令那么效率会有一定的提升 ,pause 指令有两个作用,第一它可以延迟流水线执行指令( de-pipeline ),使 CPU 不会消耗过多的执行资源,延迟的时间取决于具体实现的版本,在一些处理器上延迟时间是零。第二它可以避免在退出循环的时候因内存顺序冲突( memory order violation )而弓|起 CPU 流水线被清空( CPUpipeline flush ),从而提高 CPU 的执行效率。

只能保证一个共享变量的原子操作

CAS 只对单个共享变量有效,当操作涉及跨多个共享变量时 CAS 无效。但是从 JDK 1.5 开始,提供了 AtomicReference 类来保证引用对象之间的原子性,你可以把多个变量放在一个对象里来进行 CAS 操作.所以我们可以使用锁或者利用 AtomicReference 类把多个共享变量合并成一个共享变量来操作

CAS 与 synchronized 的使用情景

简单的来说 CAS 适用于写比较少的情况下(多读场景,冲突一般较少),synchronized 适用于写比较多的情况下(多写场景,冲突一般较多)对于资源竞争较少(线程冲突较轻)的情况,使用 synchronized 同步锁进行线程阻塞和唤醒切换以及用户态内核态间的切换操作额外浪费消耗 cpu 资源;而 CAS 基于硬件实现,不需要进入内核,不需要切换线程,操作自旋几率较少,因此可以获得更高的性能。

时间戳

时间戳就是在数据库表中单独加一列时间戳,比如 "TimeStamp" ,每次读出来的时候,把该字段也读出来,当写回去的时候,把该字段加 1 ,提交之前跟数据库的该字段比较一次,如果比数据库的值大的话,就允许保存,否则不允许保存,这种处理方法虽然不使用数据库系统提供的锁机制,但是这种方法可以大大提高数据库处理的并发量,以上悲观锁所说的加"锁",其实分为几种锁,分别是·排它锁(写锁)和共享锁(读锁)

十二、数据库锁

  1. 行级锁

行级锁是一种排他锁,防止其他事务修改此行;在使用以下语句时, Oracle会自动应用行级锁:

INSERT.UPDATE.DELETE.SELECT ... FOR UPDATE

SELECT … FOR UPDATE 语句允许用户一次锁定多条记录进行更新

使用 COMMIT 或 ROLLBACK 语句释放锁。

  1. 表级锁

表示对当前操作的整张表加锁,它实现简单,资源销耗较少,被大部分MySQL 引擎支持。最常使用的 MYISAM 与 INNODB 都支持表级锁定。表级锁定分为表共享读锁(共享锁)与表独占写锁(排他锁)。

  1. 页级锁

页级锁是 MySQL 中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快,但冲突多,行级冲突少,但速度慢。所以取了折中的页级,一次锁定相邻的一组记录。

十三、基于 Redis 分布式锁

  1. 获取锁的时候,使用 setnx ( ETNX key val:当且仅当 key 不存在时,set 一个 key 为 val 的字符串返回 1 ;若 key 存在,则什么都不做返回 0 )加锁,锁的 value 值为一个随机生成的 UUID ,在释放锁的时候进行判断。并使用 expire 命令为锁添加一个超时时间,超过该时间则自动释放锁。
  2. 获取锁的时候调用 setnx ,如果返回 0 ,则该锁正在被别人使用,返回 1则成功获取锁。还设置一个获取的超时时间,若超过这个时间则放弃获取锁。
  3. 释放锁的时候,通过 UUID 判断是不是该锁,若是该锁,则执行 delete 进行锁释放。

十四、分库分表

分库分表有垂直切分和水平切分两种。

  1. 垂直切分:将表按照功能模块.关系密切程度划分出来,部署到不同的库上。例如,我们会建立定义数据库 workDB. 商品数据库 payDB. 用户数据库userDB .日志数据库 logDB 等,分别用于存储项目数据定义表.商品定义表.用户数据表. 日志数据表等。
  2. 水平切分:当一个表中的数据量过大时,我们可以把该表的数据按照某种规则,例如 userID 散列,进行划分,然后存储到多个结构相同的表,和不同的库上。例如,我们的 userDB 中的用户数据表中,每一个表的数据量都很大就可以把 userDB 切分为结构相同的多个 userDB · part0DB.part1DB 等,再将 userDB 上的用户数据表 userTable ,切分为很多 userTableuserTable0.userTable1 等然后将这些表按照一定的规则存储到多个 userDB上。

十五、读写分离

在实际的应用中,绝大部分情况都是读远大于写。 MySQL 提供了读写分离的机制,所有的写操作都必须对应到 Master (主),读操作可以在 Master(主) 和 Slave(从)机器上进行, Slave 与 Master 的结构完全一样,一个 Master 可以有多个Slave, 甚至 Slave 下还可以挂 Slave ,通过此方式可以有效的提高 DB 集群的每秒查询率.所有的写操作都是先在 Master 上操作,然后同步更新到 Slave上,所以从 Master 同步到 Slave 机器有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重, Slave 机器数量的增加也会使这个问题更加严重。

此外,可以看出 Master 是集群的瓶颈,当写操作过多,会严重影响到Master 的稳定性,如果 Master 挂掉,整个集群都将不能正常工作。所以,1.当读压力很大的时候,可以考虑添加 Slave 机器的分式解决,但是当 Slave机器达到一定的数量就得考虑分库了。 2. 当写压力很大的时候,就必须得进行分库操作。

十六、常用30种SQL查询语句优化方法

  1. 应尽量避免在 where 子句中使用 =或<>操作符,否则引擎将放弃使用索引而进行全表扫描。
  2. 对查询进行优化,应尽量避免全表扫描首先应考虑在 where 及 orderby 涉及的列上建立索引。
  3. 应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描。如:select id from t where num is null;可以在 num 上设置默认值 0 ,确保表中 num 列没有 null 值,然后这样查询.select id from t where num=0
  4. 尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如:select id from t where num=10 or num=20;可以这样查询select id from t where num=10 union all select id from t where num=20
  5. 下面的查询也将导致全表扫描:(不能前置百分号)select id from t where name like ‘%c%’;下面走索引:select id from t where name like ‘%c’ 若要提高效率,可以考虑全文检索。
  6. in 和 not in 也要慎用,否则会导致全表扫描,如:select id from t where num in (1,2,3)对于连续的数值能用 between 就不要用 in 了 :select id from t where num between 1 and 3
  7. 如果在 where 子句中使用参数,也会导致全表扫描。因为 SQL 只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。如下面语句将进行全表扫描select id from t where num=@num 可以改为强制查询使用索引:select id from t with(index(索引名) where num=@num
  8. 应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如·select id from t where num / 2 = 100 应改为select id from t where num = 100 * 2
  9. 应尽量避免在 where 子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:select id from t where substring(name,1,3)= 'abc' -name以abc开头的id;select id from t where datediff(day,createdate,'2005-11-30')=0 -'2005-11-30'生成的id;应改为:select id from t where name like 'abc%' select id from t where createdate >=2005-11-30 ' and createdate<'2005-12-1'
  10. 10 不要在 where 子句中的 "=" 左边进行函数·算术运算或其他表达式运算,否则系统将可能无法正确使用索引。
  11. 在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索弓|将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。
  12. 不要写一些没有意义的查询,如需要生成一个空表结构:select col1,col2 into #t from t where 1 = 0 这类代码不会返回任何结果集,但是会消耗系统资源的,应改成这样·create table #t(...)
  13. 很多时候用 exists 代替 in 是一个好的选择:select num from a where num in(select num fro m b) 用下面的语句替换:select num from a where exists(select 1 from b where num=a.num)
  14. 并不是所有索引对查询都有效 ,SQL 是根据表中数据来进行查询优化的,当索引列有大量数据重复时, SQL 查询可能不会去利用索引,如一表中有字段sex , male.female 几乎各一半,那么即使在 sex 上建了索引也对查询效率起不了作用。
  15. 索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑视具体情况而定。一个表的索引数较好不要超过 6 个,太多则应考虑一些不常使用到的列上建的索引是否有必要。
  16. 应尽可能的避免更新 clustered 索引数据列,因为 clustered 索引数据列的顺序就是表记录的物理存储顺序,一旦该列值改变将导致整个表记录的顺序的调整,会耗费相当大的资源。若应用系统需要频繁更新 clustered 索引数据列,那么需要考虑是否应将该索引建为 clustered 索引。
  17. 尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。
  18. 尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。
  19. 任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。
  20. 尽量使用表变量来代替临时表。如果表变量包含大量数据,请注意索引非常有限(只有主键索引)。
  21. 避免频繁创建和删除临时表,以减少系统表资源的消耗。
  22. 临时表并不是不可使用,适当地使用它们可以使某些例程更有效,例如,当需要重复引用大型表或常用表中的某个数据集时。但是,对于一次性事件,较好使用导出表。
  23. 在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果数据量不大,为了缓和系统表的资源,应先 create table ,然后 insert。
  24. 如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table 然后 drop table ,这样可以避免系统表的较长时间锁定。
  25. 尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过 1万行,那么就应该考虑改写。
  26. 使用基于游标的方法或临时表方法之前,应先寻找基于集的解决方案来解决问题,基于集的方法通常更有效。
  27. 与临时表一样,游标并不是不可使用。对小型数据集使用FAST-FORWARD 游标通常要优于其他逐行处理方法,尤其是在必须引用几个表才能获得所需的数据时。在结果集中包括"合计"的例程通常要比使用游标执行的速度快。如果开发时间允许,基于游标的方法和基于集的方法都可以尝试一下,看哪一种方法的效果更好。
  28. 在所有的存储过程和触发器的开始处设置 SET NOCOUNT ON ,在结束时设置 SET NOCOUNT OFF 。无需在执行存储过程和触发器的每个语句后向客户端发送 DONEINPROC 消息。
  29. 尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理
  30. 尽量避免大事务操作,提高系统并发能力。

  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值