1. 程式人生 > >關於SQL效率優化的幾個方法

關於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子句的末尾。

  1.  例如:  

  2. (低效,執行時間156.3秒)  

  3. SELECT …  

  4. FROM EMP E  

  5. WHERE SAL > 50000 AND JOB = ‘MANAGER’AND 25 < (SELECT COUNT(*) FROM EMP WHERE MGR=E.EMPNO);  

  1. (高效,執行時間10.6秒)  

  2. SELECT …  

  3. FROM EMP E  

  4. WHERE 25 < (SELECT COUNT(*) FROM EMP WHERE MGR=E.EMPNO) AND SAL > 50000 AND JOB = ‘MANAGER’;