避免使用SELECT* 的九個理由及兩個例外場景
我們經(jīng)常會看到一些文章警告使用SELECT * 是一種錯誤的習慣,應該明確地指定查詢的列名,譬如在最新《阿里java開發(fā)手冊(泰山版)》中對此做了強制性的規(guī)定。而大家對其原因只是一知半解。本文將從兩個角度解釋避免使用SELECT *的9個理由,同時討論2個合理使用SELECT *的例外場景。
性能問題
從性能的角度,在應用中使用SELECT *可能會引起查詢的性能問題,主要表現(xiàn)在以下六個方面。
- 增加解析成本:用SELECT *數(shù)據(jù)庫需要解析更多的對象、權限、屬性等相關內容,這個影響可能較小,但是它確實會對數(shù)據(jù)庫的元數(shù)據(jù)查詢造成一定的壓力。
- 增加I/O操作,對于無用的大字段,如 VARCHAR、LOB、TEXT類型的字段,會增加 IO操作;數(shù)據(jù)庫一般會把超過一定長度的大字段,存放在單獨的表空間中,因此對這些字段的訪問會額外地增加一次I/O操作。
- 增加網(wǎng)絡消耗,帶上如LOB/TEXT之類的無用的大文本字段,傳輸數(shù)據(jù)量會成多倍地增漲,特別是如果數(shù)據(jù)庫和應用程序不在同一臺機器,這種開銷非常明顯。
- 增加內存消耗,不管您是否使用這些列,您的應用程序都需要把它們接收到內存,這可能會無謂的消耗大量的內存,影響程序的性能及健壯性,甚至造成內存溢出,應用崩潰。
- 影響索引選擇,對于使用SELECT * 的查詢語句,優(yōu)化器會放棄覆蓋索引策略優(yōu)化的可能性,導致需要回表或是全表掃描。
- 影響索引推薦,PawSQL的索引推薦引擎能夠分析SQL的結構,對符合條件的表上創(chuàng)建索引(快速定位、避免排序、避免回表)以提升查詢性能,具體請參考《創(chuàng)建高效索引的準則》。對于使用SELECT * 的查詢語句,PawSQL將不考慮進行覆蓋索引的推薦。
維護代價
從代碼維護的角度,在應用中使用SELECT *可能會導致維護變得困難,主要表現(xiàn)在以下三個方面,
代碼可讀性:使用SELECT * 會降低代碼的可讀性,這是因為使用SELECT * 會使查詢語句不容易理解,開發(fā)人員需要查看表定義來確定到底查詢的是什么數(shù)據(jù),同時也難以進行調試。
列名對齊:
- 在通過ORM框架開發(fā)應用時,增加或是刪減字段,容易與 resultMap 配置不一致;
- 當使用SELECT * 定義視圖時,增加或是刪減字段,都可能導致視圖失效,
- 您可能會經(jīng)常使用SELECT * into INSERT . . .之類的語句,以實現(xiàn)將某些數(shù)據(jù)從一張表復制到另一張表。如果在兩張表中,各個列的排列順序略有不同,那么就可能會出現(xiàn)將不正確的數(shù)據(jù)復制到錯誤列中的情況。
列名沖突:如果您在連接查詢中使用了SELECT * ,一旦在多個表中出現(xiàn)了具有相同名稱的列,就會導致列名沖突;從而導致數(shù)據(jù)的消費方使用錯誤。
兩個例外情形
SELECT * 并不是任何情況下都不適合使用,我們日常開發(fā)中經(jīng)常使用的合理場景有兩類:
即席查詢(Ad-hoc Query)
當我們進行數(shù)據(jù)探索或是問題定位時,我們需要即時的手工寫一些SQL來查看某些數(shù)據(jù)表,我們不知道表有哪些列,這個時候我們可以使用SELECT *來完成我們的查詢。我們不會,也不需要預先研究底層列名是什么,我們甚至是通過SELECT *來確定列名和樣例數(shù)據(jù)。特別是當表有大量的列時,SELECT *可以讓我們更方便快捷完成數(shù)據(jù)探索或是問題定位的目標。
當 * 表示一行,而不是代表所有列時
當 * 表示一行,而不是代表所有列時,* 的使用是合理的。
譬如在以下用例中,* 表示符合某個條件的行。如果您使用列名而不是 *,它將計算該列值不為NULL的行數(shù)。
類似的,在下面的這個查詢中,* 表示符合關聯(lián)條件條件的行。
有些人在 SELECT 列表中使用 表orders的主鍵o.c_custkey,或者使用數(shù)字1,但是這些約定基本上是沒有意義的。你查詢的是符合某個條件的所有行,這就是 * 的含義。對于數(shù)據(jù)庫優(yōu)化器來說,這兩個查詢語句實際上是相同的。
PawSQL中與SELECT*相關的優(yōu)化
PawSQL中與SELECT*相關的審查或優(yōu)化有三個。
SELECT*審查預警
PawSQL分析用戶輸入的SQL語句,并對其中出現(xiàn)的非上述例外情況的SELECT*進行提示預警。
- 對下面的SQL進行預警
- 對下面的SQL不預警,屬于例外情況
投影下推(Projection Pushdown)
PawSQL中的投影下推重寫優(yōu)化可以把子查詢中的SELECT進行重寫,刪除不必要的SELECT
- 優(yōu)化前SQL,子查詢中存在SELECT*
- 應用投影下推后
如果您希望在PawSQL中驗證投影下推對于SELECT *的處理邏輯,您需要先禁用查詢折疊(Query Folding)重寫優(yōu)化,因為查詢折疊會將子查詢重寫合并。
覆蓋索引推薦
如在性能問題章節(jié)里所述,對于使用SELECT * 的查詢語句,PawSQL索引推薦引擎將不考慮進行覆蓋索引的推薦。
關于PawSQL
PawSQL專注數(shù)據(jù)庫性能優(yōu)化的自動化和智能化,支持MySQL,PostgreSQL,Opengauss等,提供的SQL優(yōu)化產(chǎn)品包括。
- PawSQL Cloud,在線自動化SQL優(yōu)化工具,支持SQL審查,智能查詢重寫、基于代價的索引推薦,適用于數(shù)據(jù)庫管理員及數(shù)據(jù)應用開發(fā)人員。
- PawSQL Advisor,IntelliJ 插件, 適用于數(shù)據(jù)應用開發(fā)人員,可以IDEA/DataGrip應用市場通過名稱搜索“PawSQL Advisor”安裝。