or 和 in 效率对比
《mysql数据库开发的36条军规》里面提到了or和in的效率问题,文中提到or的效率为O(n),而in的效率为O(logn), 当n越大的时候效率相差越明显
如果ax=N(a>0,且a≠1),那么数x叫做以a为底N的对数,记作x=logaN,读作以a为底N的对数,其中a叫做对数的底数,N叫做真数
当a>0,a≠1时,aX=N X=logaN。(N>0)
在MySQL数据库中关闭query cache,数据库缓存不会对查询造成影响,数据库版本为5.1.63
测试代码:
1. #创建测试的test表
2. DROP TABLE IF EXISTS test;
3. CREATE TABLE test(
4. ID INT(10) NOT NULL,
5. `Name` VARCHAR(20) DEFAULT '' NOT NULL,
6. PRIMARY KEY( ID )
7. )ENGINE=INNODB DEFAULT CHARSET utf8;
8.
9. #创建生成测试数据的存储过程
10. DROP PROCEDURE IF EXISTS pre_test;
11. DELIMITER //
12. CREATE PROCEDURE pre_test()
13. BEGIN
14. DECLARE i INT DEFAULT 0;
15. SET autocommit = 0;
16. WHILE i<10000000 DO
17. INSERT INTO test ( ID,`Name` ) VALUES( i, CONCAT( 'Carl', i ) );
18. SET i = i+1;
19. IF i%2000 = 0 THEN
20. COMMIT;
21. END IF;
22. END WHILE;
23. END; //
24. DELIMITER ;
25.
26. #执行存储过程生成测试数据
27. CALL pre_test();
测试过程:
SELECT * FROM test WHERE id IN (1,23,48,...);
SELECT * FROM test WHERE id =1 OR id=23 OR id=48 or ... ;
测试结果:
|
所在列为主键 |
所在列有索引 |
所在列没有索引 |
|||
or |
in |
or |
in |
or |
in |
|
3条数据 |
0.002s |
0.002s |
0.002s |
0.002s |
5.016s |
5.071s |
150条数据 |
0.004s |
0.004s |
0.006s |
0.005s |
1min 02s |
5.018s |
300条数据 |
0.006s |
0.005s |
0.008s |
0.008s |
1min 55s |
5.018s |
1000条数据 |
0.018s |
0.014s |
0.021s |
0.020s |
6min 17s |
5.057s |
结论:
从上面的测试结果,可以看出如果in和or所在列有索引或者主键的话,or和in没啥差别,执行计划和执行时间都几乎一样。如果in和or所在列没有索引的话,性能差别就很大了。在没有索引的情况下,随着in或者or后面的数据量越多,in的效率不会有太大的下降,但是or会随着记录越多的话性能下降非常厉害,从第三中测试情况中可以很明显地看出了,基本上是指数级增长。
因此在给in和or的效率下定义的时候,应该再加上一个条件,就是所在的列是否有索引或者是否是主键。如果有索引或者主键性能没啥差别,如果没有索引,性能差别不是一点点
号外:
MySQL代价计算的方法, 一个计划的代价体现在硬件上就是I/O + CPU,I/O就是将所需的物理页载入内存的时间,CPU则是数据计算所消耗的时间, 有些语句是I/O密集的,有些语句是CPU运算密集的。MySQL在计算上面SQL语句的代价时,I/O代价的计算是由range的个数n_ranges和最终的结果集的行数total_rows得出来的
SQL Server中in和or效率一样
Select * from table1 where tid in (2,3)
和
Select * from table1 where tid=2 or tid=3
是一样的,都会引起全表扫描,如果tid上有索引,其索引也会失效。
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(主键)将会来的更直接些。
SQL Server 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 非聚集索引列”的查询速度是快得多的。
同时,按照某个字段进行排序的时候,无论是正序还是倒序,速度是基本相当的。
SQL Server海量数据如何提高查询效率几点建议
1.索引
2.当你想在SELECT子句中列出所有的COLUMN时,使用动态SQL列引用 ‘*’ 是一个方便的方法。不幸的是,这是一个非常低效的方法。 实际上,在解析的过程中会将‘*’ 依次转换成所有的列名, 这个工作是通过查询数据字典完成的,这意味着将耗费更多的时间。
3.增加内存、另外硬盘的读写速度如何?这都是影响查询效率因素。如果磁盘读写速度比较慢的话,对于磁盘的I/O操作会存在的瓶颈的。
4.数据量比较大建议做一下分区处理。把大的表分成几个表,这样的查询效率会大大提高的。
5.数据库采用自下而上的顺序解析WHERE子句,根据这个原理,表之间的连接必须写在其他WHERE条件之前, 那些可以过滤掉最大数量记录的条件必须写在WHERE子句的末尾。
[sql] view plain copy
例如:
(低效,执行时间156.3秒)
SELECT …
FROM EMP E
WHERE SAL > 50000 AND JOB = ‘MANAGER’AND 25 < (SELECT COUNT(*) FROM EMP WHERE MGR=E.EMPNO);
[sql] view plain copy
(高效,执行时间10.6秒)
SELECT …
FROM EMP E
WHERE 25 < (SELECT COUNT(*) FROM EMP WHERE MGR=E.EMPNO) AND SAL > 50000 AND JOB = ‘MANAGER’;