CREATE TABLE sales { order_date DATETIME NOT NULL -- other columns } ENGINE=InnoDB PARTITION BY RANGE(YEAR(order_date)) ( PARTITION p_2014 VALUES LESS THAN (2014), PARTITION p_2015 VALUES LESS THAN (2015) PARTITION p_2016 VALUES LESS THAN (2016) PARTITION p_2017 VALUES LESS THAN (2017) PARTITION p_catchall VALUES LESS THAN MAXVALUE ) 分區子句中可以使用各種函數,但表達式的返回值必須是一個確定的整數,且不能是一個常數。MySQL還支持一些其他分區,比如鍵值、哈希、列表分區,但在生產環境中很少見到。在MySQL5.5以后可以使用RANGE COLUMNS類型分區,這樣即使是基于時間分區,也無需再將其轉化成一個整數。
假設按照PARTITION BY RANGE YEAR(order_date)分區,那么所有order_date為NULL或者非法值時,記錄都會被存放到第一個分區。所以WHERE order_date BETWEEN '2017-05-01' AND ‘2017-05-31’,這個查詢會檢查兩個分區,而不是我們認為的2017年這個分區(會額外的檢查第一個分區),是因為YEAR()在接收非法值時會返回NULL。如果第一個分區的數據量非常大,而且使用全表掃描的策略時,代價會非常大。為了解決這個問題,我們可以創建一個無用的分區,比如:PARTITION p_null values less than (0)。如果插入的數據都是有效的話,第一個分區就是空的。
在MySQL5.5以后就不需要這個技巧了,因為可以直接使用列本身而不是基于列的函數進行分區:PARTITION BY RANGE COLUMNS(order_date)。直接使用這個語法可避免這個問題。
// 視圖的作用是查詢未支付訂單 CREATE VIEW unpay_order AS SELECT * FROM sales WHERE status = 'new' WITH CHECK OPTION; // 其作用下文會講 現要從未支付訂單中查詢購買者為csc的訂單,可以使用如下查詢:
// 查詢購買者為csc且未支付的訂單 SELECT order_id,order_amount,buyer FROM unpay_order WHERE buyer = 'csc'; 使用臨時表來模擬視圖:
CREATE TEMPORARY TABLE tmp_order_unpay AS SELECT * FROM sales WHERE status = 'new'; SELECT order_id,order_amount,buyer FROM tmp_order_unpay WHERE buyer = 'csc'; 使用合并算法將視圖定義的SQL合并進查詢SQL后的樣子:
SELECT order_id,order_amount,buyer FROM sales WHERE status = 'new' AND buyer = 'csc'; MySQL可以嵌套定義視圖,即在一個視圖上在定義另一個視圖,可以在EXPLAN EXTENDED之后使用SHOW WARNINGS來查看使用視圖的查詢重寫后的結果。如果采用臨時表算法實現的視圖,EXPLAIN中會顯示為派生表(DERIVED),注意EXPLAIN時需要實際執行并產生臨時表,所以有可能會很慢。
// 視圖的作用是統計每日支出金額,DATE('2017-06-15 12:00:23') = 2017-06-15 CREATE VIEW cost_per_day AS SELECT DATE(create_time) AS date,SUM(cost) AS cost FROM costs GROUP BY date; 現要統計每日的收入與支出,有類似于上面的收入表,可以使用如下SQL:
SELECT c.date,c.cost,s.amount FROM cost_per_day AS c JOIN sale_per_day AS s USING(date) WHERE date BETWEEN '2017-06-01' AND '2017-06-30' 這個查詢中,MySQL先執行視圖的SQL,生成臨時表,然后再將sale_per_day表和臨時表進行關聯。這里WHERE字句中的BETWEEN條件并不能下推到視圖中,因而視圖在創建時,會將所有的數據放到臨時表中,而不是一個月數據,并且這個臨時表也不會有索引。
當客戶端與服務器通信時,它們可以使用不同的字符集,這時候服務器將進行必要的轉換工作。當客戶端向服務器發送請求時,數據以character_set_client設置的字符集進行編碼;而當服務器收到客戶端的SQL或者數據時,會按照character_set_connection設置的字符集進行轉換;當服務器將要進行增刪改查等操作前會再次將數據轉換成character_set_database(數據庫采用的字符集,沒有單獨配置即使用默認配置,具體參考上文),最后當服務器返回數據或者錯誤信息時,則將數據按character_set_result設置的字符集進行編碼。服務器端可以使用SET CHARACTER SET來改變上面的配置,客戶端也可以根據對應的API來改變字符集配置。客戶端和服務器端都使用正確的字符集才能避免在通信中出現問題。
SELECT order_no,order_amount FROM sales ORDER BY buyer; 只有當SQL查詢中排序要求的字符集與服務器數據的字符集相同時,才能使用索引進行排序。你可能會說,這不是廢話嗎?其實不然,MySQL是可以單獨指定排序時使用的校對規則的,比如:
// 你說,這不是吃飽了撐的嗎?我覺得也是,也許會有其適用的場景吧 // 這時候就不能使用索引排序呢,只能使用文件排序 SELECT order_no,order_amount FROM sales ORDER BY buyer COLLATE utf8_bin; 當使用兩個字符集不同的列來關聯兩張表時,MySQL會嘗試轉換其中一個列的字符集。這和在數據列外面封裝一個函數一樣,會讓MySQL無法使用這個列上的索引。關于MySQL字符集還有一些坑,但在實際應用場景中遇到的字符集問題,其實不是特別的多,所以就此打住。