SQL語句為什麼使用select * 會降低查詢速度?
從SQL語句被解析的過程中,SELECT的優先順序是比較低的,僅僅高於ORDER BY,也就是說在FROM和WHERE執行之後,就已經生成了一個關於全欄位的笛卡爾乘積的結果集集合,這個時候 再使用SELECT a,b...之類的 不是反而會降低SQL語句的查詢速度嗎?還是說這個需要考慮到從磁碟中轉到緩存中,對結果集不進行一定的裁減,會因為從ROM讀取並傳輸到RAM數據量過大而影響速度?實際上為了縮小磁碟到緩存的時間(畢竟ROM上讀取的速度是RAM的幾百分之一),SQL語句的執行速度可以忽略?
樓上大部分回答都是自己的主觀看法。select *最大的問題是可能會多出一些不用的列,這些列可能正好不在索引的範圍之內,或是針對*建索引成本過於高昂,導致查詢的成本幾何級增高。反而是對於網路傳輸來說,oltp環境多幾列(不包括blob數據類型)影響很小。
如果是mysql的話(我只回答自己熟悉的領域),select *杜絕了索引覆蓋的可能性,而索引覆蓋又是速度極快,效率極高,業界極為推薦的查詢方式。
例如,有一個表為t(a,b,c,d,e,f),其中,b列有索引,那麼,在磁碟上有兩棵b+樹,即聚集索引和輔助索引,分別保存(a,b,c,d,e,f)和(b,a),如果查詢條件中where條件可以通過b列的索引過濾掉一部分記錄,查詢就會先走輔助索引,如果用戶只需要a列和b列的數據,直接通過輔助索引就可以知道用戶查詢的數據,如果用戶select *,獲取了不需要的數據,則首先通過輔助索引過濾數據,然後通過聚集索引獲取所有的列,這就多了一次b+樹查詢,速度必然會慢很多。
由於輔助索引的數據比聚集索引少很多,很多情況下,通過輔助索引進行索引覆蓋(通過索引就能獲取用戶需要的所有列),都不需要讀磁碟,而聚集索引很可能數據在外存中(取決於buffer pool的大小和命中率),這種情況下,一個是內存讀,一個是磁碟讀,速度差異就很顯著了,幾乎是數量級的差異。
此外,作為有著良好編程習慣的程序員,也不應該獲取自己不需要的東西,你現在執行select *,以後表結構修改了,如增加了一列或刪除了一列,對你的代碼影響也很大,如果只是恰好只獲取自己需要的那幾列,表結構的修改對你的代碼影響就會比較小。
select*影響的是sqlserver最終把數據發給你的時候的總時間,並不影響查詢的計算過程。而且你不寫名字的話,到時候table的schema一變,數據就不對了,你的客戶端就傻逼了。
*相比窮舉所有欄位,Oracle只是會多一個查數據字典的開銷(資料庫需要知道*等於什麼),常數級複雜度,oracle的風格一般來說不推薦加schema前綴,這樣你才可以通過同義詞動態切換schema.
此外,如果是*就不可能走索引直接訪問的優化了,不過考慮到你的需求是查所有欄位,能走該優化的情況很少。
此外,如果程序寫得好,如果增加欄位也不會影響結果。因為欄位有順序,默認新增的是排序在最後。如果你的程序是獲取meta Data的,是可以處理這種變化的情況的。
*和全表掃描沒有任何關係,全表還是索引只由where謂語決定。說一下mysql innodb上的理解。1,不需要的欄位會增加數據傳輸的時間,即使mysql伺服器和客戶端是在同一台機器上,使用的協議還是tcp,通信也是需要額外的時間。
2,要取的欄位、索引的類型,和這兩個也是有關係的。舉個例子,對於user表,有name和phone的聯合索引,select name from user where phone=12345678912 和 select * from user where phone=12345678912,前者要比後者的速度快,因為name可以在索引上直接拿到,不再需要讀取這條記錄了。3,大欄位,例如很長的varchar,blob,text。準確來說,長度超過728位元組的時候,會把超出的數據放到另外一個地方,因此讀取這條記錄會增加一次io操作。手機碼字好累啊……
sql優化有很重要的一項叫做列裁剪(column pruning)。如果不考慮索引,sql的執行演算法大概分為sort-base和hash-base,不論是哪種,多出來的列都會帶來很多無用的計算。
如果select * 與你想查詢的欄位完全一致,單單就查詢來說,select * 並沒有降低查詢效率。
但時實際使用中這種情況極少。Oracle 11g 中影響查詢速度原因主要有:- 會查詢出不必要的欄位,CPU cost,IO cost,Cost,寬頻消耗都會增加。
- 影響資料庫自動重寫優化SQL。
Oracle 解析SQL的同時會重寫SQL,類似Java中編譯class時的編譯器自動優化。如圖所示,SQL執行過程中完全無視left join 這一段。如果使用 select * ,就不會有這個優化了。
- 解析欄位。資料庫需要根據數據字典生成一個語法樹,然後根據語法生成執行計劃。如果使用select * 資料庫需要解析更多的 對象,欄位,許可權,屬性相關,在SQL語句複雜,硬解析較多的情況下,會對資料庫造成沉重的負擔。(SQL語句軟解析時不會有影響)
簡單的說這是個工程上的建議。
其實可以看看&<&<高性能MySQL&>&>這本書, 裡面有很多提升性能的建議和原理.
話說老老實實的看一本好書, 比百度那搜索出的坑爹的內容好多了~~~終於有我能答的題了(??????) ?
拿MS SQL SERVER來說,
除開上面提及到的網路開銷,因為可能不是所有列都是需要被返回的,以及提到的其實一種編碼規範和性能關係不大還算著邊的回答,真正會在下面這種情況影響性能。
SQL SERVER有一種索引叫」覆蓋」索引,和一般非聚集索引的區別是可以把指定的非鍵列也和鍵列儲存在索引頁,比如一個表有十列,項目有個需求需要頻繁的據列a,查找列b和列C. 此時可以給列a建非聚集索引並」覆蓋」bc,那麼這樣一個通過select b,c和select*會有很大的不同,前者可以通過INDEX seek(因索引覆蓋此處無需rid或key lookup),而後者則是通過INDEX seek以及根據索引頁rid或key去表數據頁lookup,而lookup是有IO消耗的,在索引碎片較大的情況下,性能甚至會低於INDEX scan. 以上
利益相關:SQL SERVER 開發支持,歡迎大家都來用SQL SERVER啊,比O記M記不差啊(?????)っ,這樣我才能找更好的工作啊
你也說了只是解析階段 ,最後執行階段肯定是和select有關。
----------------------------sql的執行過程是(不考慮查詢緩存 )解析器 -----》 預處理器 -----》 優化器 -----》 查詢執行引擎 -----》 存儲引擎1)select * 會讓優化器無法完成上面童鞋的 覆蓋索引掃描這類優化。
2)網路開銷,如果db和應用程序不在同一台機器,這種開銷非常明顯3)額外的io,內存和cpu的消耗,因為多取了不必要的列。影響 ,當列比較多,尤其列裡面有長的文本欄位,影響越明顯。
------------------------------------------------解析 階段 並沒有取數據。
我個人認為:select * 不會降低查詢速度,但是極端情況下會增加mysql伺服器與web伺服器之間(如果不是同一個伺服器的話)的數據傳輸的時間。
極端情況指的是:如果欄位比較多或者非定長表有text之類的大欄位。
以上都是我猜的,我也不知道對不對。其實只要不是癥結,感覺都無所謂
寫點補充吧,我覺得樓主想問的不僅僅是select * 與 select 某些(部分)欄位的區別。因為這種區別很明顯啊,樓上的許多人也都解釋了。高贊答案也說明了原因。
還有一部分意思應該問的是 select * 與 select 全欄位的區別。那麼,我想說,至今2017年7月,mysql和oracle兩種資料庫,這兩種方式在效率上幾乎沒有區別,區別小到完全可以忽略程度!
有些開發就是sql寫的太差,非要寫select*,搞出來的程序效率低下。
最好別寫select *
①與SELECT特定列名相比,SELECT*被跨越網路傳遞不需要的數據拖住,佔用資源多。②用SELECT*需謹慎,因為一旦列的個數或順序更改,就有可能程序執行失敗。
----------------我是分割線---------------select c.*,
(select d.number from c_now_data d where d.code=c.code and d.commodity=c.commodity and d.export=c.export and d.type=c.type and d.period=concat(LEFT(c.period,4)-1,RIGHT(c.period,3))) as tq_number,-- 同期數量
(select d.amount from c_now_data d where d.code=c.code and d.commodity=c.commodity and d.export=c.export and d.type=c.type and d.period=concat(LEFT(c.period,4)-1,RIGHT(c.period,3))) as tq_amount,-- 同期金額(select d.total_number from c_now_data d where d.code=c.code and d.commodity=c.commodity and d.export=c.export and d.type=c.type and d.period=concat(LEFT(c.period,4)-1,RIGHT(c.period,3))) as tq_total_number,-- 同期累計數量(select d.total_amount from c_now_data d where d.code=c.code and d.commodity=c.commodity and d.export=c.export and d.type=c.type and d.period=concat(LEFT(c.period,4)-1,RIGHT(c.period,3))) as tq_total_amount -- 同期累計金額from c_now_data c where c.commodity ="26030000" and c.period ="2015-09"
----------------我是分割線---------------
這個語句的執行速度非常慢,求大神給小弟指點一二,方便的話給個優化方案,在此謝過了。分情況,如果返回數據多的話無所謂,如果返回數據少,select *不適合建索引,因為要回表,影響效率。
一般來說,網路開銷是系統延遲裡面最重頭的部分。什麼內存、磁碟、cpu的開銷,不過是微妙級;網路IO則是毫秒甚至是秒級的。所以過濾無用欄位的傳輸,比對其他優化帶來的系統響應速度提升更明顯。
肯定會影響查詢速度。如果select的欄位在索引沒內會取索引中的值,而無需對從block內重取。通過查詢計劃看得到這個分別。
count的時候用1,其他時間關係不大select count(*) 用select count(1)
推薦閱讀:
※單機 MySQL 資料庫可以支撐多大數據量?
※關於資料庫,非空列有什麼好處嗎,和索引有什麼關係,為什麼盡量設置成非空呢?
※如何寫一個輕量級分散式資料庫?
※資料庫主從複製,讀寫分離,負載均衡,分庫分表分別表達的什麼概念?
※NoSQL 能終結關係資料庫嗎?