MySQL常见语法类问题
1、为啥俺的update执行后没有生效呢?
场景如下:
mysql> select col1,col2 from test;
+------+------+
| col1 | col2 |
+------+------+
| 0 | hot |
+------+------+
开发童鞋想将col1列的值改为:1 , col2列的值改为:haha ,他的update语句如下:
update test set col1 = 1 and col2 = 'haha' WHERE updatetime > '2017-08-03';
他反馈如上SQL运行时没有报错,但SQL执行后没有生效。。
原因是SQL语法有误哟
正确的语法是set 后的两个字段之间应该用英文逗号分隔,上面用了 and 喔,and 引发的“灵异事件”。。。正确的SQL长这样:
update test set col1 = 1,col2 = 'haha' WHERE updatetime > '2017-08-03';
那为啥错误写法的SQL没有也没有报错呢?
因为被MySQL理解成了:
update test set col1 = 1 and col2 = 'haha' WHERE updatetime > '2017-08-03';
=>
update test set col1 = (1 and col2 = 'haha') WHERE updatetime > '2017-08-03';
=>
update test set col1 = (1 and 0) WHERE updatetime > '2017-08-03';
=>
update test set col1 = 0 WHERE updatetime > '2017-08-03';
所以,这种情况下,错误的update执行后相当于啥都没改( 假设col1是字符串类型,那么如上错误SQL就将col1列的字段内容改成0了呀,即业务数据改错了太恐怖了!!)
2、varchar(N)可以放多少个中文字符呢?
varchar(n) 表示n个字符,无论汉字和英文,MySql都能存入 n 个字符,仅实际字节长度有所区别。
MySQL5.0以上版本,utf8字符集, varchar(n) 可以存n个中文字符。
【Tips】:
varchar 字段是将实际内容单独存储在聚簇索引之外,内容开头用1到2个字节表示实际长度(长度超过255时需要2个字节),因此最大长度不能超过65535。
UTF-8:一个汉字 = 3个字节,英文是一个字节
GBK: 一个汉字 = 2个字节,英文是一个字节
在utf-8状态下,varchar(N),N的最大值为: (65535-2)/3=21844,所以,汉字最多可以存 21844个字符串, 英文也为 21844个字符串。
在 gbk 状态下,varchar(N),N的最大值为: (65535-2)/2=32766,此时,汉字最多可以存 32766个字符串,英文也为 32766个字符串。
3、带子查询的update被退单了,咋办呢?
带子查询的SQL:
update tablename1 set idCardNo = (select idcard_no from tablename2 where id = bankCardId) where createtime > '2016-01-01 00:00:00';
上面的SQL怎么去子查询呢?
第一种方法:
update tablename1,tablename2 set tablename1.idCardNo=tablename2.idcard_no where tablename1.bankCardId=tablename2.id and tablename1.createtime > '2016-01-01 00:00:00' ;
HOHO 是滴,可以酱紫写,还可以酱紫写喔:
第二种方法:
update tablename1 A inner join tablename2 B on A.bankCardId=B.id set A.idCardNo=B.idcard_no where A.createtime > '2016-01-01 00:00:00' ;
你木有看错,确实可以这么写,啦啦啦~( ̄▽ ̄~)(~ ̄▽ ̄)~
一般带子查询的SQL可以试试看用join的方式改写哟~
Tips:
1、为啥不让提交带子查询的更新呢?
因为带子查询的更新锁表时间可能会很长,不安全哟~
特别当表比较大的时候,高风险,基本无法操作。。
2、以上两种改写带子查询update的写法,为啥推荐第一种呢?
因为那样写我们可以用工具根据主键ID分段更新大表, 既安全又快速还方便^_^
4、为什么InnoDB表建议用无业务含义的自增列做主键?
如果InnoDB表的数据写入顺序能和B+树索引的叶子节点顺序一致的话,这时候存取效率是最高的,也就是当表中有自增 主键时,存取效率最高(减少B+树分裂次数)。无业务含义自增主键的好处:
1. 自增主键以利于插入性能的提高;
2. 自增主键设计(int,bigint)可以降低二级索引空间,提升二级索引的内存命中率;
3. 自增主键可以减小page的碎片,提升空间和内存的使用。
表中需要确保唯一性的字段加唯一索引UK即可。
5、索引怎么命名呀?
唯一索引:uk___······
一般索引:idx___······
(column_name 可与建表sql脚本中字段名保持一致,若字段名过长,可用字段名缩写)
Tips:
索引中注意字段顺序哟,分辨率高的字段放左侧。
6、同一个表的DDL操作肿么写在一个alter table中呀?
某表新增字段、新增索引、删除冗余索引:
alter table tablename
add column type tinyint(1) not null default '0' COMMENT '类型',
add index IX_col1_col2(col1,col2),
drop index IX_col1;
7、where条件后的字段顺序会影响索引使用吗?
不影响的哟^_^
索引优化Tips:
目标:利用最小的索引成本找到最需要的行记录。
原则:
* 最左前缀原则:MySQL会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a=1 and b=2 and c>3 and d=4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整
* 避免重复索引:idx_abc多列索引,相当于创建了(a)单列索引,(a,b)组合索引以及(a,b,c)组合索引。不在索引列使用函数 如 max(id)> 10 ,id+1>3 等
* 尽量选择区分度高的列作为前缀索引:区分度的公式是count(distinct col)/count(*),表示字段不重复的比例,比例越大我们扫描的记录数越少
8、int(11)和 int(4)有啥区别?
int(N),int代表整型,(N)代表字段的宽度,而不是存储在数据库中的具体的长度。他们之间除了在存储的时候稍微有区别外,在我们使用的时候是没有区别的。加上zerofill后, 可以看到两者区别,比如 int(4) zerofill,你插入到数据库里的是10,则实际插入为0010,即在前面补充加了00。如果int(4)和int(11)不加zerofill,则它们没有什么区别。
INT[(N)] [UNSIGNED] [ZEROFILL] N默认为11
带符号的范围是-2147483648到2147483647。无符号的范围是0到4294967295。
有图有真相(*^__^*) 哦:
mysql> create table test1(col1 int(4) zerofill);
Query OK, 0 rows affected (0.01 sec)
mysql> insert into test1(col1) values(10);
Query OK, 1 row affected (0.00 sec)
mysql> select * from test1;
+------+
| col1 |
+------+
| 0010 |
+------+
1 row in set (0.00 sec)
mysql> create table test2(col2 int(11) zerofill);
Query OK, 0 rows affected (0.01 sec)
mysql> insert into test2(col2) values(10);
Query OK, 1 row affected (0.00 sec)
mysql> select * from test2;
+-------------+
| col2 |
+-------------+
| 00000000010 |
+-------------+
1 row in set (0.00 sec)
mysql> create table test3(col3 int(4),col4 int(11),col5 int);
Query OK, 0 rows affected (0.02 sec)
mysql> insert into test3(col3,col4,col5) values(10,10,10);
Query OK, 1 row affected (0.00 sec)
mysql> select * from test3;
+------+------+------+
| col3 | col4 | col5 |
+------+------+------+
| 10 | 10 | 10 |
+------+------+------+
1 row in set (0.00 sec)
9、varchar(N)不是变长字符串类型吗,为啥建议N尽量小?
主要是出于性能考虑。MySQL InnoDB类型的表,在进行排序和创建临时表一类的内存操作时,会使用表定义中N的长度申请内存,不会看字段具体用了多少空间哦。假设一页可以放16K数据,那么N越小可以是不是可以放到内存页中的数据就更多了呢。查内存可比查磁盘快了N个数量级,酱紫还可以减少磁盘IO操作啦~
10、select * 为啥不建议用?
- 不需要的字段会增加数据传输的时间;
- 表中有大字段时,比如longtext这样的大字段读取到返回都是很慢的(当字段长度超过768字节,会把超出的数据放到溢出页,因此读取这条记录会增加一次io操作);
- select * 杜绝了索引覆盖的可能性;
- 比如:select a from tablename where b=123456789 和 select * from tbname where b=123456789,表中有索引IX_b_a。前者要比后者的查询速度快,因为所需的字段都可以在索引上直接拿到,不再需要读取这条记录了。
所以,从数据库里读出越多的数据,查询就会变得越慢。还是养成需要什么就取什么的好的习惯~\(≧▽≦)/~啦啦啦
11、为啥字段要尽量not null,不建议用NULL?
是时候祭出MySQL官方文档啦:
NULL columns require additional space in the row to record whether their values are NULL. For MyISAM tables, each NULL column takes one bit extra, rounded up to the nearest byte.
所以,为什么捏:
- 可空列需要更多的存储空间,需要一个额外字节作为判断是否为NULL的标志位;
- 还需要MySQL进行内部特殊处理。MySQL难以优化引用可空列查询,它会使索引、索引统计和值更加复杂;
- 还会导致MYISAM 中固定大小的索引变成可变大小的索引(虽然MyISAM引擎不推荐用了,哈哈);
所以,最好总是让字段保持not null哈。
Tips:
1. 比如int、bigint类型,默认值可以用0,字符串类型默认值可以用'';
2. 如果是索引字段,一定要定义为not null喔;
12、limit及分页查询如何优化?
limit offset,N 当offset非常大时,效率很低。
原因是MySQL并不是跳过offset行然后单取N行,而是取offset+N行,返回时抛弃 offset 行,返回N行,效率较低。当offset越大时,效率越低。
优化办法:
- 1)从业务上优化
办法:
0. 应避免在数据库中做分页;
1. 若无法避免,前端加缓存,减少落到库的查询操作;
2. 不允许翻过太多页,比如不允许翻过100页;
3. 一次预取多页,降低查询次数。比如之前是limit 1000000,10,更新为一次取200页: limit 1000000,2000;
- 2)不用offset,用条件查询
举个栗子:
优化前:
select id,name from test limit 1000000,10;
优化后:
select name from test where id>1000000 limit 10;
记录上次查询最新or最大的id值,向后追溯 M行记录。比如:条件中增加主键自增ID范围;
- 3)使用“延迟关联”优化
何谓"延迟关联" :通过使用覆盖索引查询返回需要的主键,再根据主键关联原表获得需要的数据;
栗子:
优化前:
SELECT id,col1,col2,col3,col4,col5 FROM test where updatetime >='2017-08-08 00:00:01' ORDER BY id asc LIMIT 10000000,20;
优化后:
select a.id,a.col1,a.col2,a.col3,a.col4,a.col5 from test ,(select id from test where updatetime >='2017-08-08 00:00:01' ORDER BY id asc LIMIT 10000000,20) tmp where test.id=tmp.id;
优化后,查询效率会比之前提高几倍。
总结一下优化思路:
避免数据量大时扫描过多的记录。
使用有索引的列或主键进行order by;
记录上次返回的主键,在下次查询时使用主键过滤。
- 4)使用Sphinx 搜索优化
13、Online DDL啥原理呀?
目前,我司是通过Percona工具pt-online-schema-change(简称为pt-osc)来做在线DDL的,通过改进原生ddl的方式,达到不锁表在线修改表结构。
其原理如下:
1. 创建新表_table_new
2. 修改新表_table_new add column/add index(此时新表是空表,DDL很快)
3. 创建insert/update/delete相关的3个触发器
4. copy已有的数据,通过触发器将增量数据同步到新表
5. rename table,同时rename 原表table和新表_table_new
6. 删除旧表table_old( 可以设置保留旧表)
7. 删除触发器
【使用限制】:
表中不能有触发器。因为一个表上不能同时有2个相同类型的触发器。
使用 pt-osc 实际alter table过程中还是会引起 data meta lock 问题,所以不是完全不锁表,可以理解为锁表的时间变短了。
(还有另一种“Online” DDL工具:gh—ost,它是github的dba开源一款使用go语言开发的MySQL在线改表工具,解决了目前采用pt-online-schema-change会遇到的一些比如死锁问题。抛弃了pt-online-schema-change使用trigger来同步增量数据的方法,通过假扮成slave获取row格式的binlog的方式来获取增量数据,思路很新颖。不过gh—ost在新增唯一索引时会删除重复数据,这点比较坑,所以我们不用。)
14、索引越多越好吗?
非也非也~
原因:
1、索引会使数据修改操作变慢,还会在硬盘上占用相当大空间。MySQL在运行 时也要消耗资源维护索引,因此索引并不是越多越好。要建立合适的索引哟~
2、有的索引建了也有可能根本没用不到,可以用explain去确认下哟~
15、哪种情况不建议建索引呀?
表记录较少,如2000行以下;
索引的选择性较低,一般需要高于0.1哟。Index Selectivity = count(distinct column)/count(*) ;
HOHO 是滴,如上情况可以不用建索引哈~
PS:请善待存在MySQL中的数据喔^_^ ~
参考文档:
MySQL 性能优化之延迟关联
update不生效