SELECT語句 - 語法順序: 1. SELECT 2. DISTINCT <select_list> 3. FROM <left_table> 4. <join_type> JOIN <right_table> 5. ON <join_condition> 6. WHERE <where_condition> 7. GROUP BY <group_by_list> 8. HAVING <having_condition> 9. ORDER BY <order_by_condition> 10.LIMIT <limit_number> SELECT語句 - 執行順序: FROM <表名> # 選取表,將多個表數據通過笛卡爾積變成一個表。 ON <篩選條件> # 對笛卡爾積的虛表進行篩選 JOIN <join, left join, right join...> <join表> # 指定join,用于添加數據到on之后的虛表中,例如left join會將左表的剩余數據添加到虛表中 WHERE <where條件> # 對上述虛表進行篩選 GROUP BY <分組條件> # 分組 <SUM()等聚合函數> # 用于having子句進行判斷,在書寫上這類聚合函數是寫在having判斷里面的 HAVING <分組篩選> # 對分組后的結果進行聚合篩選 SELECT <返回數據列表> # 返回的單列必須在group by子句中,聚合函數除外 DISTINCT # 數據除重 ORDER BY <排序條件> # 排序 LIMIT <行數限制>
SELECT * FROM t WHERE id IN (2,3) 優化方式:如果是連續數值,可以用between代替。如下:
SELECT * FROM t WHERE id BETWEEN 2 AND 3 如果是子查詢,可以用exists代替。詳情見《MySql中如何用exists代替in》如下:
-- 不走索引 select * from A where A.id in (select id from B); -- 走索引 select * from A where exists (select * from B where B.id = A.id); 3. 盡量避免使用 or,會導致數據庫引擎放棄索引進行全表掃描。如下:
SELECT * FROM t WHERE id = 1 OR id = 3 優化方式:可以用union代替or。如下:
SELECT * FROM t WHERE id = 1 UNION SELECT * FROM t WHERE id = 3 4. 盡量避免進行null值的判斷,會導致數據庫引擎放棄索引進行全表掃描。如下:
SELECT * FROM t WHERE score IS NULL 優化方式:可以給字段添加默認值0,對0值進行判斷。如下:
SELECT * FROM t WHERE score = 0 5.盡量避免在where條件中等號的左側進行表達式、函數操作,會導致數據庫引擎放棄索引進行全表掃描。
可以將表達式、函數操作移動到等號右側。如下:
-- 全表掃描 SELECT * FROM T WHERE score/10 = 9 -- 走索引 SELECT * FROM T WHERE score = 10*9 6. 當數據量大時,避免使用where 1=1的條件。通常為了方便拼裝查詢條件,我們會默認使用該條件,數據庫引擎會放棄索引進行全表掃描。如下:
SELECT username, age, sex FROM T WHERE 1=1 優化方式:用代碼拼裝sql時進行判斷,沒 where 條件就去掉 where,有where條件就加 and。
select col1 from table where col_varchar=123; 10. order by 條件要與where中條件一致,否則order by不會利用索引進行排序
-- 不走age索引 SELECT * FROM t order by age;
-- 走age索引 SELECT * FROM t where age > 0 order by age; 對于上面的語句,數據庫的處理順序是:
第一步:根據where條件和統計信息生成執行計劃,得到數據。 第二步:將得到的數據排序。當執行處理數據(order by)時,數據庫會先查看第一步的執行計劃,看order by 的字段是否在執行計劃中利用了索引。如果是,則可以利用索引順序而直接取得已經排好序的數據。如果不是,則重新進行排序操作。 第三步:返回排序后的數據。 當order by 中的字段出現在where條件中時,才會利用索引而不再二次排序,更準確的說,order by 中的字段在執行計劃中利用了索引時,不用排序操作。
這個結論不僅對order by有效,對其他需要排序的操作也有效。比如group by 、union 、distinct等。
USE INDEX 在你查詢語句中表名的后面,添加 USE INDEX 來提供希望 MySQL 去參考的索引列表,就可以讓 MySQL 不再考慮其他可用的索引。例子: SELECT col1 FROM table USE INDEX (mod_time, name)... IGNORE INDEX 如果只是單純的想讓 MySQL 忽略一個或者多個索引,可以使用 IGNORE INDEX 作為 Hint。例子: SELECT col1 FROM table IGNORE INDEX (priority) ... FORCE INDEX 為強制 MySQL 使用一個特定的索引,可在查詢中使用FORCE INDEX 作為Hint。例子: SELECT col1 FROM table FORCE INDEX (mod_time) ... 在查詢的時候,數據庫系統會自動分析查詢語句,并選擇一個最合適的索引。但是很多時候,數據庫系統的查詢優化器并不一定總是能使用最優索引。如果我們知道如何選擇索引,可以使用FORCE INDEX強制查詢使用指定的索引?!禡ySQL中特別實用的幾種SQL語句送給大家》博文建議閱讀,干貨
例如:
SELECT * FROM students FORCE INDEX (idx_class_id) WHERE class_id = 1 ORDER BY id DESC; 二、SELECT語句其他優化 1. 避免出現select *
默認情況下,MySQL 會對GROUP BY分組的所有值進行排序,如 “GROUP BY col1,col2,....;” 查詢的方法如同在查詢中指定 “ORDER BY col1,col2,...;” 如果顯式包括一個包含相同的列的 ORDER BY子句,MySQL 可以毫不減速地對它進行優化,盡管仍然進行排序。
因此,如果查詢包括 GROUP BY 但你并不想對分組的值進行排序,你可以指定 ORDER BY NULL禁止排序。例如:
SELECT col1, col2, COUNT(*) FROM table GROUP BY col1, col2 ORDER BY NULL ; 3. 優化join語句
SELECT col1 FROM customerinfo WHERE CustomerID NOT in (SELECT CustomerID FROM salesinfo ) 如果使用連接(JOIN).. 來完成這個查詢工作,速度將會有所提升。尤其是當 salesinfo表中對 CustomerID 建有索引的話,性能將會更好,查詢如下:
SELECT col1 FROM customerinfo LEFT JOIN salesinfoON customerinfo.CustomerID=salesinfo.CustomerID WHERE salesinfo.CustomerID IS NULL 連接(JOIN).. 之所以更有效率一些,是因為 MySQL 不需要在內存中創建臨時表來完成這個邏輯上的需要兩個步驟的查詢工作。
select * from t where thread_id = 10000 and deleted = 0 order by gmt_create asc limit 0, 15; 上述例子通過一次性根據過濾條件取出所有字段進行排序返回。數據訪問開銷=索引IO+索引全部記錄結果對應的表數據IO。因此,該種寫法越翻到后面執行效率越差,時間越長,尤其表數據量很大的時候。
select t.* from (select id from t where thread_id = 10000 and deleted = 0 order by gmt_create asc limit 0, 15) a, t where a.id = t.id; 上述例子必須滿足t表主鍵是id列,且有覆蓋索引secondary key:(thread_id, deleted, gmt_create)。通過先根據過濾條件利用覆蓋索引取出主鍵id進行排序,再進行join操作取出其他字段。數據訪問開銷=索引IO+索引分頁后結果(例子中是15行)對應的表數據IO。因此,該寫法每次翻頁消耗的資源和時間都基本相同,就像翻第一頁一樣。
SELECT * FROM (SELECT ROW_NUMBER() OVER(ORDER BY ID ASC) AS rowid,* FROM infoTab)t WHERE t.rowid > 100000 AND t.rowid <= 100050 4. 用varchar/nvarchar 代替 char/nchar