關於SQL效率優化的幾個方法
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 非聚集索引列”的查詢速度是快得多的。
同時,按照某個欄位進行排序的時候,無論是正序還是倒序,速度是基本相當的。
1.索引
2.當你想在SELECT子句中列出所有的COLUMN時,使用動態SQL列引用 ‘*’ 是一個方便的方法。不幸的是,這是一個非常低效的方法。 實際上,在解析的過程中會將‘*’ 依次轉換成所有的列名, 這個工作是通過查詢資料字典完成的,這意味著將耗費更多的時間。
3.增加記憶體、另外硬碟的讀寫速度如何?這都是影響查詢效率因素。如果磁碟讀寫速度比較慢的話,對於磁碟的I/O操作會存在的瓶頸的。
4.資料量比較大建議做一下分割槽處理。把大的表分成幾個表,這樣的查詢效率會大大提高的。
5.資料庫採用自下而上的順序解析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’;