SQLSERVER SQL性能优化

news/2024/5/13 7:13:20/文章来源:https://blog.csdn.net/yangyifan0/article/details/137593637

1.选择最有效率的表名顺序(只在基于规则的优化器中有效)    
    SQLSERVER的解析器按照从右到左的顺序处理FROM子句中的表名,因此FROM子句中写在最后的表(基础表driving table)将被最先处理,在FROM子句中包含多个表的情况下,必须选择记录条数最少的表作为基础表,当 SQLSERVER处理多个表时,会运用排序及合并的方式连接它们, 

   首先,扫描第一个表(FROM子句中最后的那个表)并对记录进行排序;然后扫描第二个表(FROM子句中最后第二个表);最后将所有从第二个表中检索出的记录与第一个表中合适记录进行合并 
 

例如: 表 TAB1 16,384 条记录表 TAB2 5 条记录,选择TAB2作为基础表 (最好的方法) select count(*) from tab1,tab2 执行时间0.96秒,选择TAB2作为基础表 (不佳的方法) select count(*) from tab2,tab1 执行时间26.09秒;

如果有3个以上的表连接查询,那就需要选择交叉表(intersection table)作为基础表,交叉表是指那个被其他表所引用的表    

 例如: EMP表描述了LOCATION表和CATEGORY表的交集 SELECT * FROM LOCATION L, CATEGORY C, EMP E WHERE E.EMP_NO BETWEEN 1000 AND 2000 AND E.CAT_NO = C.CAT_NO AND E.LOCN = L.LOCN 将比下列 SQL更有效率 SELECT * FROM EMP E , LOCATION L , CATEGORY C WHERE E.CAT_NO = C.CAT_NO AND E.LOCN = L.LOCN AND E.EMP_NO BETWEEN 1000 AND 2000

 2.WHERE子句中的连接顺序    
    SQLSERVER采用自下而上的顺序解析WHERE子句,根据这个原理,表之间的连接必须写在其他WHERE条件之前,那些可以过滤掉最大数量记录的条件必须写在WHERE子句的末尾    

例如: (低效,执行时间156.3秒) SELECT * FROM EMP E WHERE SAL > 50000 AND JOB = ’MANAGER’ AND 25 < (SELECT COUNT(*) FROM EMP WHERE MGR=E.EMPNO);    (高效,执行时间10.6秒) SELECT * FROM EMP E WHERE 25 < (SELECT COUNT(*) FROM EMP WHERE MGR=E.EMPNO) AND SAL > 50000 AND JOB = ’MANAGER’; 

3.SELECT子句中避免使用’*’。当你想在SELECT子句中列出所有的COLUMN时,使用动态 SQL列引用’*’是一个方便的方法,不幸的是,这是一个非常低效的方法。实际上, SQLSERVER在解析的过程中,会将’*’依次转换成所有的列名,这个工作是通过查询数据字典完成的,这意味着将耗费更多的时间 

4.减少访问数据库的次数。当执行每条 SQL语句时, SQLSERVER在内部执行了许多工作:解析 SQL语句,估算索引的利用率,绑定变量,读数据块等等 ,由此可见,减少访问数据库的次数,就能实际上减少 SQLSERVER的工作量。

例如: 以下有三种方法可以检索出雇员号等于0342或0291的职员    方法1 (最低效) SELECT EMP_NAME, SALARY, GRADE FROM EMP WHERE EMP_NO = 342;  SELECT EMP_NAME, SALARY, GRADE FROM EMP WHERE EMP_NO = 291; 方法2 (次低效) DECLARE CURSOR C1 (E_NO NUMBER) IS SELECT EMP_NAME,SALARY,GRADE FROM EMP WHERE EMP_NO = E_NO; BEGIN OPEN C1(342); FETCH C1 INTO …,…,…; … OPEN C1(291); FETCH C1 INTO …,…,…; … CLOSE C1; END; 方法2 (高效) SELECT A.EMP_NAME, A.SALARY, A.GRADE, B.EMP_NAME, B.SALARY, B.GRADE FROM EMP A, EMP B WHERE A.EMP_NO = 342 AND B.EMP_NO = 291; 

   5.使用DECODE函数来减少处理时间 
 

使用DECODE函数可以避免重复扫描相同记录或重复连接相同的表 

例如: SELECT COUNT(*), SUM(SAL) FROM EMP WHERE DEPT_NO = ’0020’ AND ENAME LIKE ’SMITH%’; SELECT COUNT(*), SUM(SAL) FROM EMP WHERE DEPT_NO = ’0030’ AND ENAME LIKE ’SMITH%’; 你可以用DECODE函数高效地得到相同结果 SELECT COUNT(DECODE(DEPT_NO, ’0020’, ’X’, NULL)) D0020_COUNT, COUNT(DECODE(DEPT_NO, ’0030’, ’X’, NULL)) D0030_COUNT, SUM(DECODE(DEPT_NO, ’0020’, SAL, NULL)) D0020_SAL, SUM(DECODE(DEPT_NO, 0030, SAL, NULL)) D0030_SAL FROM EMP WHERE ENAME LIKE ’SMITH%’; ’X’表示任何一个字段 类似的,DECODE函数也可以运用于GROUP BY和ORDER BY子句中 

  6.用Where子句替换HAVING子句 

避免使用HAVING子句,HAVING只会在检索出所有记录之后才对结果集进行过滤,这个处理需要排序、统计等操作, 如果能通过WHERE子句限制记录的数目,那就能减少这方面的开销 

例如: 低效 SELECT REGION, AVG(LOG_SIZE) FROM LOCATION GROUP BY REGION HAVING REGION REGION != ’SYDNEY’ AND REGION != ’PERTH’ 高效 SELECT REGION, AVG(LOG_SIZE) FROM LOCATION WHERE REGION REGION != ’SYDNEY’ AND REGION != ’PERTH’ GROUP BY REGION 

 7.减少对表的查询 

在含有子查询的 SQL语句中,要特别注意减少对表的查询

例如:    低效 SELECT TAB_NAME FROM TABLES WHERE TAB_NAME = (SELECT TAB_NAME FROM TAB_COLUMNS WHERE VERSION = 604) AND DB_VER = (SELECT DB_VER FROM TAB_COLUMNS WHERE VERSION = 604) 高效 SELECT TAB_NAME FROM TABLES WHERE (TAB_NAME, DB_VER) = (SELECT TAB_NAME, DB_VER FROM TAB_COLUMNS WHERE VERSION = 604) Update多个Column例子: 低效 UPDATE EMP SET EMP_CAT = (SELECT MAX(CATEGORY) FROM EMP_CATEGORIES), SAL_RANGE = (SELECT MAX(SAL_RANGE) FROM EMP_CATEGORIES) WHERE EMP_DEPT = 0020; 高效 UPDATE EMP SET (EMP_CAT, SAL_RANGE) = (SELECT MAX(CATEGORY), MAX(SAL_RANGE) FROM EMP_CATEGORIES) WHERE EMP_DEPT = 0020; 

  8.使用表的别名(Alias),当在 SQL语句中连接多个表时,请使用表的别名并把别名前缀于每个Column上,这样可以减少解析的时间并减少那些由Column歧义引起的语法错误 
    
9.用EXISTS替代IN 
    
在许多基于基础表的查询中,为了满足一个条件,往往需要对另一个表进行联接 ,在这种情况下,使用EXISTS(或NOT EXISTS)通常将提高查询的效率 

低效 SELECT * FROM EMP (基础表) WHERE EMPNO > 0 AND DEPTNO IN (SELECT DEPTNO FROM DEPT WHERE LOC = ’MELB’) 高效 SELECT * FROM EMP (基础表) WHERE EMPNO > 0 AND EXISTS (SELECT ’X’ FROM DEPT WHERE DEPT.DEPTNO = EMP.DEPTNO AND LOC = ’MELB’) 

 10.用NOT EXISTS替代NOT IN 

 在子查询中,NOT IN子句将执行一个内部的排序和合并,无论在哪种情况下,NOT IN都是最低效的,因为它对子查询中的表执行了一个全表遍历 ,为了避免使用NOT IN,我们可以把它改写成外连接(Outer Joins)或NOT EXISTS 

例如: SELECT … FROM EMP WHERE DEPT_NO NOT IN (SELECT DEPT_NO FROM DEPT WHERE DEPT_CAT = ’A’); 为了提高效率改写为 高效 SELECT … FROM EMP A, DEPT B WHERE A.DEPT_NO = B.DEPT(+) AND B.DEPT_NO IS NULL AND B.DEPT_CAT(+) = ’A’ 最高效 SELECT … FROM EMP E WHERE NOT EXISTS (SELECT ’X’ FROM DEPT D WHERE D.DEPT_NO = E.DEPT_NO AND DEPT_CAT = ’A’); 

 11.用表连接替换EXISTS 

 通常来说,采用表连接的方式比EXISTS更有效率 

例如: SELECT ENAME FROM EMP E WHERE EXISTS (SELECT ’X’ FROM DEPT WHERE DEPT_NO = E.DEPT_NO AND DEPT_CAT = ’A’); 更高效 SELECT ENAME FROM DEPT D, EMP E WHERE E.DEPT_NO = D.DEPT_NO AND DEPT_CAT = ’A’; 

  12.用EXISTS替换DISTINCT 

当提交一个包含多表信息(比如部门表和雇员表)的查询时,避免在SELECT子句中使用DISTINCT,一般可以考虑用EXIST替换,EXISTS使查询更为迅速,因为RDBMS核心模块将在子查询的条件一旦满足后,立刻返回结果

例如: 低效 SELECT DISTINCT DEPT_NO, DEPT_NAME FROM DEPT D, EMP E WHERE D.DEPT_NO = E.DEPT_NO 高效 SELECT DEPT_NO, DEPT_NAME FROM DEPT D WHERE EXISTS (SELECT ’X’ FROM EMP E WHERE E.DEPT_NO = D.DEPT_NO); 

 13.用索引提高效率 

 索引是表的一个概念部分,用来提高检索数据的效率。实际上, SQLSERVER使用了一个复杂的自平衡B-tree结构 
    
   通常,通过索引查询数据比全表扫描要快。当 SQLSERVER找出执行查询和Update语句的最佳路径时, SQLSERVER优化器将使用索引 
    
   同样,在联结多个表时使用索引也可以提高效率。另一个使用索引的好处是,它提供了主键(primary key)的唯一性验证 
    
   除了那些LONG或LONG RAW数据类型,你可以索引几乎所有的列 
    
   通常在大型表中使用索引特别有效,当然,在扫描小表时,使用索引同样能提高效率 
    
   虽然使用索引能得到查询效率的提高,但是我们也必须注意到它的代价 
    
   索引需要空间来存储,也需要定期维护,每当有记录在表中增减或索引列被修改时,索引本身也会被修改 
    
   这意味着每条记录的INSERT、DELETE、UPDATE将为此多付出4、5次的磁盘I/O 
    
   因为索引需要额外的存储空间和处理,那些不必要的索引反而会使查询反应时间变慢 

 SQLSERVER对索引有两种访问模式: 
    
   1).索引唯一扫描(INDEX UNIQUE SCAN) 
    
   大多数情况下, 优化器通过WHERE子句访问INDEX  

例如: 表LODGING有两个索引:建立在LODGING列上的唯一性索引LODGING_PK和建立在MANAGER列上的非唯一性索引LODGING$MANAGER SELECT * FROM LODGING WHERE LODGING = ’ROSE HILL’; 

在内部,上述 SQL将被分成两步执行: 
    
   首先,LODGING_PK索引将通过索引唯一扫描的方式被访问,获得相对应的ROWID;然后通过ROWID访问表的方式执行下一步检索 
    
   如果被检索返回的列包括在INDEX列中, SQLSERVER将不执行第二步的处理(通过ROWID访问表) 
    
   因为检索数据保存在索引中,单单访问索引就可以完全满足查询结果 
    
   2).索引范围查询(INDEX RANGE SCAN) 
    
   适用于两种情况: 
    
   1>.基于唯一性索引的一个范围的检索 
    
   2>.基于非唯一性索引的检索 

例1 SELECT LODGING FROM LODGING WHERE LODGING LIKE ’M%’; 

WHERE子句条件包括一系列值, SQLSERVER将通过索引范围查询的方式查询LODGING_PK ,由于索引范围查询将返回一组值,它的效率就要比索引唯一扫描低一些  

例2 SELECT LODGING FROM LODGING WHERE MANAGER = ’BILL GATES’;

这个 SQL的执行分两步,LODGING$MANAGER的索引范围查询(得到所有符合条件记录的ROWID),通过ROWID访问表得到LODGING列的值 
    
   由于LODGING$MANAGER是一个非唯一性的索引,数据库不能对它执行索引唯一扫描 
    
   WHERE子句中,如果索引列所对应的值的第一个字符由通配符(WILDCARD)开始,索引将不被采用  

SELECT LODGING FROM LODGING WHERE MANAGER LIKE ’%HANMAN’; 

在这种情况下, SQLSERVER将使用全表扫描 

 14.避免在索引列上使用计算  

WHERE子句中,如果索引列是函数的一部分,优化器将不使用索引而使用全表扫描  

 例如: 低效 SELECT … FROM DEPT WHERE SAL * 12 > 25000; 高效 SELECT … FROM DEPT WHERE SAL > 25000/12; 

  请务必注意,检索中不要对索引列进行处理,如:TRIM,TO_DATE,类型转换等操作,破坏索引,使用全表扫描,影响 SQL执行效率 

15.避免在索引列上使用IS NULL和IS NOT NULL  

避免在索引中使用任何可以为空的列, SQLSERVER将无法使用该索引 
    
   对于单列索引,如果列包含空值,索引中将不存在此记录; 
    
   对于复合索引,如果每个列都为空,索引中同样不存在此记录。如果至少有一个列不为空,则记录存在于索引中 
    
   如果唯一性索引建立在表的A列和B列上,并且表中存在一条记录的A,B值为(123,null),  SQLSERVER将不接受下一条具有相同A,B值(123,null)的记录插入 
    
   如果所有的索引列都为空, SQLSERVER将认为整个键值为空,而空不可能等于空,因此你可以插入1000条具有相同键值的记录,当然它们都是空! 
    
   因为空值不存在于索引列中,所以WHERE子句中对索引列进行空值比较将使 SQLSERVER停用该索引 

低效(索引失效) SELECT … FROM DEPARTMENT WHERE DEPT_CODE IS NOT NULL 

16.使用UNION-ALL和UNION 
    
   当 SQL语句需要UNION两个查询结果集合时,这两个结果集合会以UNION-ALL的方式被合并,然后在输出最终结果前进行排序 
    
   如果用UNION ALL替代UNION,这样排序就不是必要了,效率就会因此得到提高 
    
   需要注意的是,UNION ALL将重复输出两个结果集合中相同记录,因此还是要从业务需求分析使用UNION ALL的可行性 
    
   关于索引下列经验请参考: 
    
   1).如果检索数据量超过30%的表中记录数,使用索引将没有显著的效率提高 
    
   2).在特定情况下,使用索引也许会比全表扫描慢,但这是同一个数量级上的差距;而通常情况下,使用索引比全表扫描要快几倍乃至几千倍!  

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_1045838.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

怎样关闭浏览器文件下载安全病毒中检测功能

怎样关闭浏览器文件下载安全病毒中检测功能 有时候需要通过浏览下载一些特殊文件&#xff0c;浏览器会提示有病毒&#xff0c;终止下载并且自动删除文件。 以为是浏览器的问题&#xff0c;用 chrome、Edge、firefox 三种浏览器下载均失败。 尝试关闭了所有浏览器安全防护也不行…

防止邮箱发信泄露服务器IP教程

使用QQ邮箱,网易邮箱,189邮箱,新浪邮箱,139邮箱可能会泄露自己的服务器IP。 泄露原理&#xff1a;服务器通过请求登录SMTP邮箱服务器接口&#xff0c;对指定的收件人发送信息。 建议大家使用商业版的邮箱&#xff0c;比如阿里云邮箱发信等 防止邮件发信漏源主要关注的是确保邮件…

MySQL innoDB存储引擎多事务场景下的事务执行情况

一、背景 在日常开发中&#xff0c;对不同事务之间的隔离情况等理解如果不够清晰&#xff0c;很容易导致代码的效果和预期不符。因而在这对一些存在疑问的场景进行模拟。 下面的例子全部基于innoDB存储引擎。 二、场景&#xff1a; 2.1、两个事务修改同一行记录 正常来说&…

Linux JDK修改不生效

原JDK8&#xff0c;现需要切换为JDK11&#xff0c;环境变量已经修改为11&#xff0c;但java -version还是显示8。 ln -s -f /home/jdk-11.0.19/bin/java

稀碎从零算法笔记Day45-LeetCode:电话号码的字母组合

题型&#xff1a;映射、回溯算法、递归 链接&#xff1a;17. 电话号码的字母组合 - 力扣&#xff08;LeetCode&#xff09; 来源&#xff1a;LeetCode 题目描述 给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出…

AI大模型引领未来智慧科研暨ChatGPT自然科学高级应用

以ChatGPT、LLaMA、Gemini、DALLE、Midjourney、Stable Diffusion、星火大模型、文心一言、千问为代表AI大语言模型带来了新一波人工智能浪潮&#xff0c;可以面向科研选题、思维导图、数据清洗、统计分析、高级编程、代码调试、算法学习、论文检索、写作、翻译、润色、文献辅助…

基于Springboot中小企业设备管理系统设计与实现(论文+源码)_kaic

摘 要 随着信息技术和网络技术的飞速发展&#xff0c;人类已进入全新信息化时代&#xff0c;传统管理技术已无法高效&#xff0c;便捷地管理信息。为了迎合时代需求&#xff0c;优化管理效率&#xff0c;各种各样的管理系统应运而生&#xff0c;各行各业相继进入信息管理时代&a…

TensorFlow学习之:深度学习基础

神经网络基础 神经网络是深度学习的核心&#xff0c;它们受人脑的结构和功能启发&#xff0c;能够通过学习大量数据来识别模式和解决复杂问题。神经网络的基本工作原理包括前向传播和反向传播两个阶段。 前向传播&#xff08;Forward Propagation&#xff09; 前向传播是神经…

AI大模型之ChatGPT科普(深度好文)

目录 训练ChatGPT分几步&#xff1f; 如何炼成ChatGPT&#xff1f; 如何微调ChatGPT? 如何强化ChatGPT? 如何调教ChatGPT? AI思维链是什么&#xff1f; GPT背后的黑科技Transformer是什么&#xff1f; Transformer在计算机视觉上CV最佳作品&#xff1f; ChatGPT是人…

修复 Windows 上的 PyTorch 1.1 github 模型加载权限错误

问题: 在 Windows 计算机上执行示例 github 模型加载时,生成了 master.zip 文件的权限错误(请参阅下面的错误堆栈跟踪)。 错误堆栈跟踪: 在[4]中:en2de = torch.hub.load(pytorch/fairseq, transformer.wmt16.en-de, tokenizer=moses, bpe=subword_nmt) 下载:“https://…

【R基础】一组数据计算均值、方差与标准差方法及意义

【R基础】一组数据计算均值、方差与标准差方法及意义 均值、方差与标准差是用来描述数据分布情况 均值&#xff1a;用来衡量一组数据整体情况。 数据离散程度度量标准&#xff1a; 方差&#xff08;均方&#xff0c;s^2&#xff0c;总体参数&#xff0c;离均差平方和&#…

实用工具推荐:如何使用MechanicalSoup进行网页交互

在当今数字化时代&#xff0c;网页交互已经成为日常生活和工作中不可或缺的一部分。无论是自动填写表单、抓取网页数据还是进行网站测试&#xff0c;都需要一种高效而可靠的工具来实现网页交互。而在众多的选择中&#xff0c;MechanicalSoup作为一种简单、易用且功能强大的Pyth…

GitLab教程(一):安装Git、配置SSH公钥

文章目录 序一、Git安装与基本配置&#xff08;Windows&#xff09;下载卸载安装基本配置 二、SSH密钥配置 序 为什么要使用代码版本管理工具&#xff1a; 最近笔者确实因为未使用代码版本管理工具遇到了一些愚蠢的问题&#xff0c;笔者因此认为代码版本管理工具对于提高团队…

基于FPGA的以太网相关文章导航

首先需要了解以太网的一些接口协议标准&#xff0c;常见的MII、GMII、RGMII时序&#xff0c;便于后续开发。 【必读】从MII到RGMII&#xff0c;一文了解以太网PHY芯片不同传输接口信号时序&#xff01; 介绍一款比较老的以太网PHY芯片88E1518&#xff0c;具有RGMII接口&#xf…

防止狗上沙发,写一个浏览器实时识别目标检测功能

家里有一条狗&#x1f436;&#xff0c;很喜欢乘人不备睡沙发&#x1f6cb;️&#xff0c;恰好最近刚搬家 狗迎来了掉毛期 不想让沙发上很多毛。所以希望能识别到狗&#xff0c;然后播放“gun 下去”的音频&#x1f4e3;。 需求分析 需要一个摄像头&#x1f4f7; 利用 chrome…

openHarmony 如何从API9升级到API10

最近用从官方下载的DevEco Studio3.1开发小app, 需要用到第三方库&#xff0c;加载第三方库&#xff0c;并添加代码&#xff0c;编译时如下错误&#xff1a; hvigor Finished :entry:defaultGenerateMetadata… after 3 ms hvigor ERROR: Failed :entry:defaultMergeProfile… …

微信小程序页面交互综合练习 (重点:解决“setData of undefined”报错问题)

一、写一个注册表单&#xff0c;点击“注册”按钮将用户输入的数据带到服务器&#xff0c;并且能在控制台显示参数。 &#xff08;1&#xff09;首先&#xff0c;我需要在vscode里面创建一个简易的node.js服务器 //第一步:引入http模块 var http require(http); //第二步:创建…

算法刷题Day30 | 332.重新安排行程、51. N皇后、37. 解数独

目录 0 引言1 重新安排行程1.1 我的解题1.2 更好的解法 2 N皇后2.1 我的解题 3 解数独3.1 我的解题3.2 &#x1f64b;‍♂️ 作者&#xff1a;海码007&#x1f4dc; 专栏&#xff1a;算法专栏&#x1f4a5; 标题&#xff1a;算法刷题Day30 | 332.重新安排行程、51. N皇后、37. …

【图论】详解链式前向星存图法+遍历法

细说链式前向星存图法 首先要明白&#xff0c;链式前向星的原理是利用存边来进行模拟图。 推荐左神的视频–建图、链式前向星、拓扑排序 比方说有这样一张图&#xff0c;我们用链式前向星来进行模拟时&#xff0c;可以将每一条边都进行编号&#xff0c;其中&#xff0c;红色的…

刷题DAY49 | LeetCode 121-买卖股票的最佳时机 122-买卖股票的最佳时机II

121 买卖股票的最佳时机&#xff08;easy&#xff09; 给定一个数组 prices &#xff0c;它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票&#xff0c;并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取…