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字符集还有一些坑,但在实际应用场景中遇到的字符集问题,其实不是特别的多,所以就此打住。