高效SQL语句(SQL Server)

以下的SQL语句以Northwind为例

1、不要再where子句中"="的左侧使用函数和表达式,因为系统无法应用函数或表达式中的索引

SELECT * FROM Customers WHERE Cast(CustomerID AS VARCHAR(20))='1' --Clustered Index Scan 全表扫描
SELECT * FROM Customers WHERE CustomerID ='1' --Clustered Index Seek 索引扫描

2、只返回必要的行或列
2.1 减少I/O次数
2.2 减少加载到内存的数据量

3、关于组合索引(待定)
组合索引中的顺序很重要,当查询语句中的列只能与组合索引中的第1列相匹配时,才能在查询中应用此索引

4、Distinct语句的使用原则
尽量少用,因为数据库引擎需要花费大量的时间对所有字段进行比较,过滤掉重复的记录,因此影响了查询的效率
字段较少时,可适当采用;较多时,不宜采用

5、Union语句
Union必须满足以下要求:
1、所有select语句的列数必须相同
2、所有select语句中对应列的数据类型必须兼容

执行包含Union的查询语句的过程如下:
1、依次执行所有select语句
2、将所有select语句的结果集合并为一个结果集
3、对结果集进行排序,并过滤掉重复的记录(由于需要第3步的操作,导致联合查询效率很低)
可使用union all,不用排序和过滤重复记录,效率高

6、使用存储过程
数据库引擎可以在创建存储过程时对其进行分析和优化,并可在首次执行该过程后使用该过程的内存中版本。
相对来说,每次运行sql语句时,都要从客户端重复发送,并且在sqlserver每次执行这些语句时,对其进行编译和优化。

7、如果需要多次对一个数据量非常大的表中一部分数据进行查询操作,可以将这部分数据放在临时表中,然后对临时表进行操作。

8、模糊匹配符%和索引的关系
假定有一个表Good,主键为聚集索引,类型为nvarchar(50)
SELECT * FROM Goods AS g WHERE g.GoodsName LIKE '%商品1' --Clustered Index Scan 全表扫描
SELECT * FROM Goods AS g WHERE g.GoodsName LIKE '商%品1' --Clustered Index Seek 索引扫描
SELECT * FROM Goods AS g WHERE g.GoodsName LIKE '商品1%' --Clustered Index Seek 索引扫描
结论:通配符%放在中间和后面会走索引,放在前面不会

9、应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:
SELECT * FROM Employees AS e WHERE e.EmployeeID =1 --走聚集索引
SELECT * FROM Employees AS e WHERE e.EmployeeID IS NULL --Constanct Scan

10、尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连 接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。

11、在所有的存储过程和触发器的开始处设置 SET NOCOUNT ON ,在结束时设置 SET NOCOUNT OFF 。无需在执行存储过程和触发器的每个语句后向客户端发送 DONE_IN_PROC 消息。

12、不用SELECT *查询

     1、SELECT * 会产生额外的IO,消耗额外的带宽资源。当数据库有大量这类SQL,就会产生量变到质变。慢慢影响整个数据库的性能。 

     2、SELECT *会返回大量的数据,并占用内存空间,影响操作速度

     3、SELECT *可能还会导致不走索引(同样的sql语句,一个select *,一个select具体字段)

DROP TABLE TEST;
SELECT * INTO TEST FROM sys.objects

SELECT CREATE_DATE, TYPE FROM TEST 
WHERE CREATE_DATE >='2013-07-09 00:00' 
  AND CREATE_DATE <='2014-04-30 00:00' 
  AND TYPE='S'
  
SELECT * FROM TEST 
WHERE CREATE_DATE >='2013-07-09 00:00' 
  AND CREATE_DATE <='2014-04-30 00:00' 
    AND TYPE='S'
 

13、索引的列数对扫描的影响

DROP TABLE TEST;
SELECT * INTO TEST FROM sys.objects   --初始化表数据

DROP INDEX IDX_TEST_N1 ON TEST
 
--只对默写字段建索引,全表扫描比索引扫描开销小,查询不走索引,走全表扫描(Table Scan)
CREATE INDEX IDX_TEST_N1 ON TEST(CREATE_DATE, TYPE)
 
--建所有列的索引时,下面的查询会走索引(Index Seek)
CREATE NONCLUSTERED INDEX IDX_TEST_N1
ON [dbo].[TEST] ([type],[create_date])
INCLUDE ([name],[object_id],[principal_id],[schema_id],[parent_object_id],[type_desc],[modify_date],[is_ms_shipped],[is_published],[is_schema_published])
GO  
  
  
SET SHOWPLAN_ALL  ON
GO
SELECT * FROM TEST 
WHERE CREATE_DATE >='2013-04-09 00:00' 
  AND CREATE_DATE <='2014-04-30 00:00' 
  AND TYPE='S'
GO
SET SHOWPLAN_ALL  OFF;
GO
 

14、为什么不走索引(查询条件中LastName列为非聚集索引)

 --批执行计划(Ctrl+L),可以查看各条语句的【查询开销】
 --上条语句不走LastName索引,而是走聚集扫描,是因为走索引I/O开销较大,因为数据量较小;如果数据量增加到较多时,如1k以上,则可能走索引
 SELECT * FROM Employees AS e WHERE e.LastName='Davolio'  --
 
 SELECT * FROM Employees AS e WITH(INDEX=LastName) WHERE e.LastName='Davolio'
 --批执行计划(Ctrl+L)

走不走索引的影响因素

--1、查询的数据大小
--2、数据的分布
--3、I/O开销

原文地址:https://www.cnblogs.com/gossip/p/3843344.html