问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

如何解决SQL查询速度太慢?

发布网友 发布时间:2022-04-22 05:37

我来回答

7个回答

懂视网 时间:2022-04-30 00:03

很多人不知道SQL语句在SQL SERVER中是如何执行的,他们担心自己所写的SQL语句会被SQL SERVER误解。比如:  

select * from table1 where name=‘zhangsan‘ and tID > 10000  

和执行:  

select * from table1 where tID > 10000 and name=‘zhangsan‘  

一些人不知道以上两条语句的执行效率是否一样,因为如果简单的从语句先后上看,这两个语句的确是不一样,如果tID是一个聚合索引,那么后一句仅仅从表的10000条以后的记录中查找就行了;而前一句则要先从全表中查找看有几个name=‘zhangsan‘的,而后再根据限制条件条件tID>10000来提出查询结果。  

事实上,这样的担心是不必要的。SQL SERVER中有一个“查询分析优化器”,它可以计算出where子句中的搜索条件并确定哪个索引能缩小表扫描的搜索空间,也就是说,它能实现自动优化。  

虽然查询优化器可以根据where子句自动的进行查询优化,但大家仍然有必要了解一下“查询优化器”的工作原理,如非这样,有时查询优化器就会不按照您的本意进行快速查询。  

在查询分析阶段,查询优化器查看查询的每个阶段并决定限制需要扫描的数据量是否有用。如果一个阶段可以被用作一个扫描参数(SARG),那么就称之为可优化的,并且可以利用索引快速获得所需数据。  

SARG的定义:用于限制搜索的一个操作,因为它通常是指一个特定的匹配,一个值得范围内的匹配或者两个以上条件的AND连接。形式如下:  

列名 操作符 <常数 或 变量>  

或  

<常数 或 变量> 操作符列名  

列名可以出现在操作符的一边,而常数或变量出现在操作符的另一边。如:  

Name=’张三’  

价格>5000  

5000<价格  

Name=’张三’ and 价格>5000  

如果一个表达式不能满足SARG的形式,那它就无法限制搜索的范围了,也就是SQL SERVER必须对每一行都判断它是否满足WHERE子句中的所有条件。所以一个索引对于不满足SARG形式的表达式来说是无用的。  

介绍完SARG后,我们来总结一下使用SARG以及在实践中遇到的和某些资料上结论不同的经验:  

1、Like语句是否属于SARG取决于所使用的通配符的类型 

如:name like ‘张%’ ,这就属于SARG  

而:name like ‘%张’ ,就不属于SARG。  

原因是通配符%在字符串的开通使得索引无法使用。  

2、or 会引起全表扫描 

Name=’张三’ and 价格>5000 符号SARG,而:Name=’张三’ or 价格>5000 则不符合SARG。使用or会引起全表扫描。  

3、非操作符、函数引起的不满足SARG形式的语句 

不满足SARG形式的语句最典型的情况就是包括非操作符的语句,如:NOT、!=、<>、!<、!>、NOT EXISTS、NOT IN、NOT LIKE等,另外还有函数。下面就是几个不满足SARG形式的例子:  

ABS(价格)<5000  

Name like ‘%三’  

有些表达式,如:  

WHERE 价格*2>5000  

SQL SERVER也会认为是SARG,SQL SERVER会将此式转化为:  

WHERE 价格>2500/2  

但我们不推荐这样使用,因为有时SQL SERVER不能保证这种转化与原始表达式是完全等价的。  

4、IN 的作用相当与OR 

语句:  

Select * from table1 where tid in (2,3)  

和  

Select * from table1 where tid=2 or tid=3  

是一样的,都会引起全表扫描,如果tid上有索引,其索引也会失效。  

5、尽量少用NOT 

6、exists 和 in 的执行效率是一样的 

很多资料上都显示说,exists要比in的执行效率要高,同时应尽可能的用not exists来代替not in。但事实上,我试验了一下,发现二者无论是前面带不带not,二者之间的执行效率都是一样的。因为涉及子查询,我们试验这次用SQL SERVER自带的pubs数据库。运行前我们可以把SQL SERVER的statistics I/O状态打开。  

(1)select title,price from titles where title_id in (select title_id from sales where qty>30)  

该句的执行结果为:  

表 ‘sales‘。扫描计数 18,逻辑读 56 次,物理读 0 次,预读 0 次。  

表 ‘titles‘。扫描计数 1,逻辑读 2 次,物理读 0 次,预读 0 次。  

(2)select title,price from titles where exists (select * from sales where sales.title_id=titles.title_id and qty>30)  

第二句的执行结果为:  

表 ‘sales‘。扫描计数 18,逻辑读 56 次,物理读 0 次,预读 0 次。  

表 ‘titles‘。扫描计数 1,逻辑读 2 次,物理读 0 次,预读 0 次。  

我们从此可以看到用exists和用in的执行效率是一样的。  

7、用函数charindex()和前面加通配符%的LIKE执行效率一样 

前面,我们谈到,如果在LIKE前面加上通配符%,那么将会引起全表扫描,所以其执行效率是低下的。但有的资料介绍说,用函数charindex()来代替LIKE速度会有大的提升,经我试验,发现这种说明也是错误的:  

select gid,title,fariqi,reader from tgongwen where charindex(‘刑侦支队‘,reader)>0 and fariqi>‘2004-5-5‘  

用时:7秒,另外:扫描计数 4,逻辑读 7155 次,物理读 0 次,预读 0 次。  

select gid,title,fariqi,reader from tgongwen where reader like ‘%‘ + ‘刑侦支队‘ + ‘%‘ and fariqi>‘2004-5-5‘  

用时:7秒,另外:扫描计数 4,逻辑读 7155 次,物理读 0 次,预读 0 次。  

8、union并不绝对比or的执行效率高 

我们前面已经谈到了在where子句中使用or会引起全表扫描,一般的,我所见过的资料都是推荐这里用union来代替or。事实证明,这种说法对于大部分都是适用的。  

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi=‘2004-9-16‘ or gid>9990000  

用时:68秒。扫描计数 1,逻辑读 404008 次,物理读 283 次,预读 392163 次。  

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi=‘2004-9-16‘  

union  

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where gid>9990000  

用时:9秒。扫描计数 8,逻辑读 67489 次,物理读 216 次,预读 7499 次。  

看来,用union在通常情况下比用or的效率要高的多。  

但经过试验,笔者发现如果or两边的查询列是一样的话,那么用union则反倒和用or的执行速度差很多,虽然这里union扫描的是索引,而or扫描的是全表。  

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi=‘2004-9-16‘ or fariqi=‘2004-2-5‘  

用时:6423毫秒。扫描计数 2,逻辑读 14726 次,物理读 1 次,预读 7176 次。  

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi=‘2004-9-16‘  

union  

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi=‘2004-2-5‘  

用时:11640毫秒。扫描计数 8,逻辑读 14806 次,物理读 108 次,预读 1144 次。  

9、字段提取要按照“需多少、提多少”的原则,避免“select *” 

我们来做一个试验:  

select top 10000 gid,fariqi,reader,title from tgongwen order by gid desc  

用时:4673毫秒  

select top 10000 gid,fariqi,title from tgongwen order by gid desc  

用时:1376毫秒  

select top 10000 gid,fariqi from tgongwen order by gid desc  

用时:80毫秒  

由此看来,我们每少提取一个字段,数据的提取速度就会有相应的提升。提升的速度还要看您舍弃的字段的大小来判断。  

10、count(*)不比count(字段)慢 

某些资料上说:用*会统计所有列,显然要比一个世界的列名效率低。这种说法其实是没有根据的。我们来看:  

select count(*) from Tgongwen  

用时:1500毫秒  

select count(gid) from Tgongwen  

用时:1483毫秒  

select count(fariqi) from Tgongwen  

用时:3140毫秒  

select count(title) from Tgongwen  

用时:52050毫秒  

从以上可以看出,如果用count(*)和用count(主键)的速度是相当的,而count(*)却比其他任何除主键以外的字段汇总速度要快,而且字段越长,汇总的速度就越慢。我想,如果用count(*), SQL SERVER可能会自动查找最小字段来汇总的。当然,如果您直接写count(主键)将会来的更直接些。  

11、order by按聚集索引列排序效率最高 

我们来看:(gid是主键,fariqi是聚合索引列)  

select top 10000 gid,fariqi,reader,title from tgongwen  

用时:196 毫秒。 扫描计数 1,逻辑读 289 次,物理读 1 次,预读 1527 次。  

select top 10000 gid,fariqi,reader,title from tgongwen order by gid asc  

用时:4720毫秒。 扫描计数 1,逻辑读 41956 次,物理读 0 次,预读 1287 次。  

select top 10000 gid,fariqi,reader,title from tgongwen order by gid desc  

用时:4736毫秒。 扫描计数 1,逻辑读 55350 次,物理读 10 次,预读 775 次。  

select top 10000 gid,fariqi,reader,title from tgongwen order by fariqi asc  

用时:173毫秒。 扫描计数 1,逻辑读 290 次,物理读 0 次,预读 0 次。  

select top 10000 gid,fariqi,reader,title from tgongwen order by fariqi desc  

用时:156毫秒。 扫描计数 1,逻辑读 289 次,物理读 0 次,预读 0 次。  

从以上我们可以看出,不排序的速度以及逻辑读次数都是和“order by 聚集索引列” 的速度是相当的,但这些都比“order by 非聚集索引列”的查询速度是快得多的。  

同时,按照某个字段进行排序的时候,无论是正序还是倒序,速度是基本相当的。  

12、高效的TOP 

事实上,在查询和提取超大容量的数据集时,影响数据库响应时间的最大因素不是数据查找,而是物理的I/0操作。如:  

select top 10 * from (  

select top 10000 gid,fariqi,title from tgongwen  

where neibuyonghu=‘办公室‘  

order by gid desc) as a  

order by gid asc  

这条语句,从理论上讲,整条语句的执行时间应该比子句的执行时间长,但事实相反。因为,子句执行后返回的是10000条记录,而整条语句仅返回10条语句,所以影响数据库响应时间最大的因素是物理I/O操作。而限制物理I/O操作此处的最有效方法之一就是使用TOP关键词了。TOP关键词是SQL SERVER中经过系统优化过的一个用来提取前几条或前几个百分比数据的词。经笔者在实践中的应用,发现TOP确实很好用,效率也很高。但这个词在另外一个大型数据库ORACLE中却没有,这不能说不是一个遗憾,虽然在ORACLE中可以用其他方法(如:rownumber)来解决。在以后的关于“实现千万级数据的分页显示存储过程”的讨论中,我们就将用到TOP这个关键词。 

sqlserver查询效率

标签:

热心网友 时间:2022-04-29 21:11

1. 执行计划中明明有使用到索引,为什么执行还是这么慢?

2. 执行计划中显示扫描行数为 644,为什么 slow log 中显示 100 多万行?
a. 我们先看执行计划,选择的索引 “INDX_BIOM_ELOCK_TASK3(TASK_ID)”。结合 sql 来看,因为有 "ORDER BY TASK_ID DESC" 子句,排序通常很慢,如果使用了文件排序性能会更差,优化器选择这个索引避免了排序。
那为什么不选 possible_keys:INDX_BIOM_ELOCK_TASK 呢?原因也很简单,TASK_DATE 字段区分度太低了,走这个索引需要扫描的行数很大,而且还要进行额外的排序,优化器综合判断代价更大,所以就不选这个索引了。不过如果我们强制选择这个索引(用 force index 语法),会看到 SQL 执行速度更快少于 10s,那是因为优化器基于代价的原则并不等价于执行速度的快慢;
b. 再看执行计划中的 type:index,"index" 代表 “全索引扫描”,其实和全表扫描差不多,只是扫描的时候是按照索引次序进行而不是行,主要优点就是避免了排序,但是开销仍然非常大。
Extra:Using where 也意味着扫描完索引后还需要回表进行筛选。一般来说,得保证 type 至少达到 range 级别,最好能达到 ref。
在第 2 点中提到的“慢日志记录Rows_examined: 1161559,看起来是全表扫描”,这里更正为“全索引扫描”,扫描行数确实等于表的行数;
c. 关于执行计划中:“rows:644”,其实这个只是估算值,并不准确,我们分析慢 SQL 时判断准确的扫描行数应该以 slow log 中的 Rows_examined 为准。
4. 优化建议:添加组合索引 IDX_REL_DEVID_TASK_ID(REL_DEVID,TASK_ID)

优化过程:
TASK_DATE 字段存在索引,但是选择度很低,优化器不会走这个索引,建议后续可以删除这个索引:
select count(*),count(distinct TASK_DATE) from T_BIOMA_ELOCK_TASK;+------------+---------------------------+| count(*) | count(distinct TASK_DATE) |+------------+---------------------------+| 1161559 | 223 |+------------+---------------------------+

在这个 sql 中 REL_DEVID 字段从命名上看选择度较高,通过下面 sql 来检验确实如此:
select count(*),count(distinct REL_DEVID) from T_BIOMA_ELOCK_TASK;+----------+---------------------------+| count(*) | count(distinct REL_DEVID) |+----------+---------------------------+| 1161559 | 62235 |+----------+---------------------------+

由于有排序,所以得把 task_id 也加入到新建的索引中,REL_DEVID,task_id 组合选择度 100%:
select count(*),count(distinct REL_DEVID,task_id) from T_BIOMA_ELOCK_TASK;+----------+-----------------------------------+| count(*) | count(distinct REL_DEVID,task_id) |+----------+-----------------------------------+| 1161559 | 1161559 |+----------+-----------------------------------+

在测试环境添加 REL_DEVID,TASK_ID 组合索引,测试 sql 性能:alter table T_BIOMA_ELOCK_TASK add index idx_REL_DEVID_TASK_ID(REL_DEVID,TASK_ID);
添加索引后执行计划:
这里还要注意一点“隐式转换”:REL_DEVID 字段数据类型为 varchar,需要在 sql 中加引号:AND T.REL_DEVID = 000000025xxx >> AND T.REL_DEVID = '000000025xxx'

执行时间从 10s+ 降到 毫秒级别:
1 row in set (0.00 sec)
结论
一个典型的 order by 查询的优化,添加更合适的索引可以避免性能问题:执行计划使用索引并不意味着就能执行快。

热心网友 时间:2022-04-29 22:29

SQL Server查询速度慢的原因有很多,常见的有以下几种:

1、没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷)

2、I/O吞吐量小,形成了瓶颈效应。

3、没有创建计算列导致查询不优化。

4、内存不足

5、网络速度慢

6、查询出的数据量过大(可以采用多次查询,其他的方法降低数据量)

7、锁或者死锁(这也是查询慢最常见的问题,是程序设计的缺陷)

8、sp_lock,sp_who,活动的用户查看,原因是读写竞争资源。

9、返回了不必要的行和列

10、查询语句不好,没有优化

热心网友 时间:2022-04-30 00:03

对于数据可以参照下面几点
1、优化SQL语句,SQL语句对查询速度影响最大
2、对于经常查询的字段作索引。但是这样会增加修改时的压力
4、优化SQLServer,比如给其分配固定的内存,预先分配查询内存,调整CPU使用率等。
5、优化硬件资源,比如使用更高的服务器或者硬盘,独立安排数据库的数据文件和索引文件,将数据文件分布于不同的物理硬盘上等等
6、考虑使用分布数据库或者对大表进行拆分

另外,2G的数据库应该不算很大了,我处理过18G的数据库,8000万条记录,查询速度可以被接受

热心网友 时间:2022-04-30 01:55

把一个表分成几个表,可以按,ID分开,比如1-10000,10000-20000这样分成多个表,当然你可以用其它的方法分开,这样的SELECT的速度会快点,其实你看到的耗时54秒,主要是输出速度太慢了,不是查询慢

热心网友 时间:2022-04-30 04:03

建议不要使用select * 这样数据量太大,可以加上select top 1000 * from hr_worktime

最好把一起不用的数据转移到备份库,这里保留需要的最新数据即可。

热心网友 时间:2022-04-30 06:27

呵呵,这个问题很有趣不是吗?
上面的同志们只是给出一些建议,以我的经验来看(oracle),
如果数据量较大,索引的重复量尽量避免,最好的方式是建立非业务id(最好使用自增或是序列),把这个id建立索引。
你的最大的问题就是,建立了索引后,索引列必须出现在where中,否则索引就白白建立了,比如你的id是从1一直到383000,那么你的语句可以写成
select * from hr_worktime where id>-1
还有就是,where条件中避免出现!=,or,between,等东西,否则索引实效。
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
山东沃尔德集团集团所辖公司介绍 齐鲁银行无忧贷和市民贷哪个好 什么叫补按揭 后按揭贷款什么意思 买房者续按揭有什么危害 加按揭是什么意思 八月中国最凉快的地方 八月份哪里最凉快,去哪旅游好?美丽的地方 乱字同韵字是什么意思 华硕笔记本电脑触摸板怎么开笔记本电脑触摸板怎么开启和关闭_百度知 ... 电脑一直重复重启重装系统进不了BIOS 123456789相加减最终等20 电脑开机停留在智能主板的界面好久!而且进去不了B... SQL 语句执行感觉很慢,怎么回事? 电脑有几年没用过了,能开机进不了界面 索引用了!查询速度为什么还是那么慢?! 我的电脑不能进入BIOS界面了 SQL语句执行很慢,怎么回事? 电脑能开机不能进入bios,是不是主板坏了? 求助,sql语句无法用到索引,执行很慢 我的电脑开机一直卡在asus界面,也进不去bios界面... 电脑开机时一直停留在BIOS界面加载进度,始终进不... 我家电脑进不了bios啊,怎么办,技嘉的 单片机C语言数组,十六进制转十进制 我的电脑为什么进不了BIOS界面 62235英语怎么写? 电脑开机无法进入系统,不能进入bios,停留在主板... 五位数62a3b是45的倍数,这个数最大是多少? 电脑总是bios界面进不去 62A3B是45的倍数,这个五位数最大是多少? 普洱茶究竟有没有保质期的? 斯诺克各位大师的世界排名。要前二十的。 全国涂料培训班。谁知道? 普洱茶的保质期有多久,是像朋友说的越陈越香吗? 普洱茶生茶保质期几年? 普洱茶有保质期吗 冻水饺用冷水还是热水下锅? 冻饺子是冷水下锅还是热水下锅? 出了事故,只报警了没报保险,事后再走保险可以吗? 车撞石墩走了过后可以报保险吗? 汽车撞到人当场没报保险事后可以再报保险吗? 车撞了,没及时报警,事后可以走保险吗 车辆发生事故后离开了现场可否报保险呢? 我的车子前几天撞得现在还能报保险吗? 车子撞墙了可以走保险吗? 车子前段时间撞上护栏,现在还可以走保险吗? 别人车撞了但是私了了我还能走保险吗? 追尾了别人车辆,然后别人没事走了,可以报保险吗? 汽车碰撞以后第一时间没发现可以二次报险吗? 怎么开创业板