5-05 MySQL索引优化实战二
2022-12-13 16:48:04 0 举报
05 MySQL索引优化实战二
作者其他创作
大纲/内容
<b>实战应用示例表1</b><font color="#9e9e9e">(以下分页查询和count(*)查询优化示例使用此表)</font>
1. 创建示例表
2. 通过存储过程插入示例数据
<b>实战应用示例表2</b><font color="#9e9e9e">(以下Join关联查询优化示例使用此表)</font>
1. 创建示例表
2. 通过存储过程插入示例数据
<b><font color="#f57c00">分页查询优化</font></b>
<b><font color="#2196f3">实际分页功能实现问题</font></b>
如下分页SQL实现示例:<font color="#9e9e9e">(从表 employees 中取出从 10001 行开始的 10 行记录)</font>
示例中看似只查询了 10 条记录,实际这条 SQL :
1. 先读取 10010 条记录
2. 然后抛弃 10000 条记录
3. 再然后读取后面 10 条想要的数据
因此要<b><font color="#f57c00">查询一张大表</font></b>,且<b><font color="#f57c00">比较靠后的数据</font></b>,<b><font color="#e57373">执行效率非常低</font></b>
<b><font color="#2196f3">常见的分页场景优化技巧</font></b>
<b><font color="#f57c00">1. 根据自增且连续的主键排序的分页查询</font></b>
如下所示:<font color="#9e9e9e">(根据自增且连续主键排序的分页查询)</font>
示例中 SQL 表示:
查询<b>从第 90001 开始</b>的5行数据
没有添加单独 order by,<b>默认</b>通过<b>主键排序</b>
<b><font color="#4caf50">优化 SQL:</font></b>
因表 employees 的主键是自增且连续的,所以可以优化成按主键查询从第90001开始的5行数据
如下所示:
<b>优化前后 SQL 执行计划对比:</b>
<b>优化前:</b>
<b>优化后:</b>
<b><font color="#4caf50">优化后</font></b>的 <b><font color="#4caf50">SQL 走了索引</font></b>,而且<b><font color="#4caf50">扫描的行数大大减少</font></b>,<b><font color="#4caf50">执行效率更高</font></b>
但是,这种<b><font color="#e57373">优化后的 SQL 很多场景并不实用</font></b>:
<font color="#e57373">1. </font>因为表中可能<font color="#e57373">某些记录被删除后</font>,<font color="#e57373" style="">主键空缺,导致结果不一致</font>
如下所示:<font color="#9e9e9e">(先删一条前面的记录,再测试原 SQL 和 优化后的 SQL)</font>
因此,如果主键不连续,不能使用上面的优化方法
<font color="#e57373">2. </font>如果原 SQL 排序 <font color="#e57373">order by 字段为非主键字段</font>,使用上面<font color="#e57373">优化方法会导致两条SQL的结果不一致</font>
<b style=""><font color="#2196f3">这种优化方案,需要同时满足以下两个条件:</font></b>
1. 主键自增且连续
2. 结果是按照主键排序的
<b><font color="#f57c00">2. 根据非主键字段排序的分页查询</font></b>
如下所示:<font color="#9e9e9e">(根据非主键字段排序的分页查询,执行时间 0.094s)</font>
<b><font color="#4caf50">优化SQL:</font></b>
关键是让排序时返回的字段尽可能少,所以可以让排序和分页操作先查出主键,然后根据主键查到对应的记录
如下所示:<font color="#9e9e9e">(执行时间 0.026s)</font>
<b>优化前后 SQL 执行计划对比:</b>
查看原SQL执行计划:
发现并<font color="#e57373"><b>没有使用name字段的索引</b></font>(key字段对应的值为null),且<b><font color="#e57373">排序使用的filesort</font></b>
原因:<font color="#e57373"><b>扫描整个索引</b></font>并查找到没有索引的行(可能要遍历多个索引树)的<font color="#e57373"><b>成本比扫描全表成本更高</b></font>
查看优化后SQL执行计划:
优化后的<b><font color="#4caf50">查询结果与原 SQL 一致</font></b>,且<b><font color="#4caf50">执行时间减少了一半以上</font></b>
<font color="#e57373"><b>原 SQL</b> 使用的是<b> filesort 排序</b></font>,而<font color="#4caf50"><b>优化后的 SQL</b> 使用的是<b>索引排序</b></font>
<b><font color="#f57c00">Join 关联查询优化</font></b>
<b><font color="#2196f3">1. MySQL的表关联常见有两种算法:</font></b>
Nested-Loop Join (NLJ)算法
Block Nested-Loop Join(BNL) 算法
<b><font color="#2196f3">2. 嵌套循环连接 Nested-Loop Join(NLJ) 算法</font></b>
<b><font color="#f57c00">一次一行</font></b>循环地<b>从第一张表(称为驱动表)中<font color="#f57c00">读取</font>行</b>,在这行数据中<b>取到关联字段</b>,根据<b>关联字段在另一张表(被驱动表)里取出满足条件的行</b>,然后<b>取出两张表的结果合集</b>
如下所示:
从执行计划中可以看到:
<b>驱动表是 t2</b>,<b>被驱动表是 t1 </b>
<b>1)先执行</b>的就是<b>驱动表<font color="#9e9e9e"> </font></b><font color="#9e9e9e">(如果执行计划结果的id一样,则按从上到下顺序执行)</font>
<b>2)优化器</b>一般会优先选择<b>小表做驱动表,</b><font color="#2196f3">t2 表数据量小,作为驱动表</font>
<b>3)所以当使用</b> <b>inner join 时,</b><font color="#2196f3"></font>排在前面的表并不一定就是驱动表,小表做驱动表,大表作为被驱动表
<b>而当使用 left join 时</b>:<br>
<b><font color="#2196f3">左表</font>是<font color="#2196f3">驱动表</font></b>,<b><font color="#00bcd4">右表</font>是<font color="#00bcd4">被驱动表</font></b>
<b>而当使用 right join 时</b>:
<b><font color="#00bcd4">右表</font>是<font color="#2196f3">驱动表</font>,<font color="#2196f3">左表</font>是<font color="#00bcd4">被驱动表</font></b>
<b>以上查询使用了 <font color="#f57c00">NLJ算法</font>:</b>
<font color="#f57c00">一般 join 语句中</font>,<b>如果</b>执行计划<b> Extra 中<font color="#e57373">未出现 Using join buffer</font></b>,<b>则</b>表示使用的 join <b><font color="#2196f3">算法是 NLJ</font></b>
上面SQL的大致流程如下:
1)<font color="#f57c00">从表 t2 中读取一行数据</font><font color="#9e9e9e">(如果t2表有查询过滤条件的,会从过滤结果里取出一行数据)</font>;
2)从第 1 步的数据中,<font color="#f57c00">取出关联字段 a,到表 t1 中查找</font>;<b><font color="#f57c00">(t2每行数据找t1数据做比对)</font></b>
3)<font color="#f57c00">取出表 t1 中满足条件的行,跟 t2 中获取到的结果合并</font>,作为结果返回给客户端;<br>
4)重复上面 3 步。<br>
<font color="#f57c00">整个过程中扫描了多少行?</font>
1)整个过程会<font color="#2196f3">读取 t2 表的所有数据(扫描100行)</font> --> 100行
2)然后遍历这每行数据中字段 a 的值,<font color="#2196f3">根据 t2 表中 a 的值索引扫描 t1 表中的对应行</font><font color="#2196f3"> </font><font color="#000000">--> 100行</font>
<font color="#f44336">因此整个过程扫描了<b> 200 行</b></font>
如果<font color="#f57c00"><b>被驱动表</b>的<b>关联字段没索引</b></font>,使用<b><font color="#e57373">NLJ算法性能会比较低</font></b>,<font color="#2196f3"><b>MySQL会选择 Block Nested-Loop Join算法</b></font>
<b><font color="#2196f3">3. 基于块的嵌套循环连接 Block Nested-Loop Join(BNL)算法</font></b>
把<font color="#f57c00"><b>驱动表的数据</b>读入到</font> <b><font color="#f57c00">join_buffer </font></b>中,然后扫描被驱动表,<font color="#2196f3">把<b>被驱动表每一行</b>取出来<b>跟 join_buffer 中的数据</b>做对比</font><br>
如下所示:
从执行计划中可以看到这些信息:
<b>Extra 中 的 Using join buffer (Block Nested Loop) </b>说明该<b>关联查询使用的是 BNL 算法</b>
上面sql的大致流程如下:<br>
1. 把 <b>t2 的所有数据放入到 join_buffer 中</b>
2. 把表<b> t1 中每一行取出来,跟 join_buffer 中的数据做对比<font color="#f57c00">(t1每行数据找t2数据做比对)</font></b>
3. 返回满足 join 条件的数据
<font color="#f57c00">整个过程中扫描了多少行?</font>
整个过程<b>对表 t1 和 t2 都做了一次全表扫描</b><br>
因此扫描的总行数为10000(表 t1 的数据总量) + 100(表 t2 的数据总量) =<b><font color="#f44336">10100</font></b>
<font color="#f57c00">因为 join_buffer 里的数据是无序的</font>,因此<font color="#f57c00">对表 t1 中的每一行,都要做 100 次判断</font>,所以内存中的<font color="#f44336">判断次数是100 * 10000= 100 万次</font>。
<font color="#f57c00">如果表 t2 是一个大表,<b>join_buffer 放不下</b>怎么办呢?</font>
<b>join_buffer 的大小</b>是由参数 <b>join_buffer_size</b> 设定的,<b>默认值是 256k</b>。
如果<b>放不下</b>表 t2 的所有数据话,策略很简单,就是<b>分段放</b>
<font color="#f57c00"><b>被驱动表的关联字段没索引为什么要选择使用 BNL 算法</b>而不使用 Nested-Loop Join 呢?</font>
如果上面第二条SQL<b><font color="#e57373">使用 Nested-Loop Join</font></b>,那么<b>扫描行数为 100 * 10000 = 100万次</b>,<b><font color="#e57373">这个是磁盘扫描</font></b>。
很显然,用<b>BNL</b>磁盘扫描次数<font color="#9e9e9e">(10100行)</font>少很多,<font color="#4caf50">相比于磁盘扫描,BNL的内存计算会快得多</font>。
因此<b>MySQL对于被驱动表的关联字段没索引的关联查询</b>,<b>一般都会使用 BNL 算法</b>。<b>如果有索引</b>一般选择 NLJ 算法,有索引的情况下<b> NLJ 算法比 BNL算法性能更高</b>
<b><font color="#2196f3">4. 对于关联sql的优化</font></b>
<b>1)关联字段加索引,</b><font color="#2196f3" style="">让MySQL</font><font color="#2196f3">做 join 操作时</font><b><font color="#2196f3">尽量选择NLJ算法</font></b>
<b>2)小表驱动大表,</b>关联查询时,如果明确知道哪张表是小表可以<font color="#2196f3">用 <b>straight_join </b>写法</font><b><font color="#2196f3">固定连接驱动方式</font></b>,省去MySQL优化器自己判断的时间
<b>straight_join 解释:</b>
<b>straight_join </b>功能同 join 类似,但<b><font color="#f57c00">能让左边的表来驱动右边的表</font></b>,即<b><font color="#2196f3">修改</font></b>表优化器对于<b><font color="#2196f3">联表查询的执行顺序</font></b>
比如:[SQL] select * from t2 <b>straight_join </b>t1 on t2.a = t1.a; 代表指定 MySQL选 t2 表作为驱动表<br>
<font color="#f44336">straight_join 只适用于 inner join</font>,并不适用于 left join,right join<font color="#9e9e9e">(因为left join,right join已经代表指定了表的执行顺序)</font>
使用 straight_join一定要慎重,因为部分情况下人为指定的执行顺序并不一定会比优化引擎要靠谱。
尽可能让优化器去判断,因为大部分情况下mysql优化器是比人要聪明的。
<b>对于小表定义的明确:</b>
在决定哪个表做驱动表的时候,应该是<font color="#f57c00" style=""><b>两个表</b>按照各自的<b>条件过滤完成之后</b></font>,计算<font color="#f57c00"><b>参与 </b>join 的各个字段的<b>总数据量</b></font>,<font color="#2196f3"><b>数据量小</b>的那个表就是<b>“小表”</b></font>,应该作为<b><font color="#4caf50">驱动表</font></b>
<b><font color="#2196f3">5. in和exsits的优化</font></b>
<b>原则:</b>
<b>小表驱动大表</b>,即小的数据集驱动大的数据集
<b>in:</b><br>
当<b>B表的数据集<font color="#f57c00">小于</font>A表的数据集</b>时,<b><font color="#2196f3">in </font><font color="#f57c00">优于 </font><font color="#2196f3">exists</font></b>
将主查询B的数据,放到子查询A中做条件验证,根据验证结果(true或false)来决定主查询的数据是否保留
它的<b>执行逻辑</b>如下所示:
[SQL] select * from A where id in (select id from B);
等价于
for(select id from B){<br> select * from A where A.id = B.id<br>}
exists:
当<b>A表的数据集<font color="#f57c00">小于</font>B表的数据集</b>时,<b><font color="#2196f3">exists </font><font color="#f57c00">优于 </font><font color="#2196f3">in</font></b>
将主查询A的数据,放到子查询B中做条件验证,根据验证结果(true或false)来决定主查询的数据是否保留<br>
它的<b>执行逻辑</b>如下所示:
[SQL] select * from A where exists (select 1 from B where B.id = A.id);<br>
等价于
for(select * from A){<br> select * from B where B.id = A.id<br>}
<b>exists 补充:</b>
<b>1)EXISTS (subquery) 只返回 TRUE 或 FALSE </b>,因此<b>子查询中的</b> <b>SELECT * 也可以用 SELECT 1 替换</b><font color="#9e9e9e">(官方说法是实际执行时会忽略SELECT清单)</font>,因此没有区别<br>
<b>2)EXISTS子查询的实际执行过程可能经过了优化</b>而不是我们理解上的逐条对比
<b>3)EXISTS子查询往往也可以用 JOIN 来代替</b>,何种最优需要具体问题具体分析<br>
<b><font color="#f57c00">count(*) 查询优化</font></b>
<b><font color="#2196f3">1. 四种 count 查询情况对比</font></b>
如下所示:
<font color="#e57373"><b>注意:</b>以上4条sql只有<b>根据某个字段</b>count<b>不会统计字段为null值的</b>数据行</font>
查看执行计划:
四个SQL<b>从实际执行结果</b>可看出,<b><font color="#4caf50">执行计划一样</font></b>,说明这四个SQL<b><font color="#4caf50">执行效率应该差不多</font></b>
四个SQL<b>从理论上</b>比较执行效率:
<b>字段<font color="#00bcd4">有索引</font>时:</b>
<b>count(*)</b> <b><font color="#4dd0e1">≈</font></b> <b>count(1)</b> <b><font color="#4dd0e1">></font></b> <b>count(字段)<font color="#2196f3"> </font><font color="#4dd0e1">></font></b> <b>count(主键 id)</b>
<b><font color="#000000">count(字段) </font><font color="#f57c00">></font><font color="#00bcd4"> </font><font color="#000000">count(主键 id) :</font></b>
<font color="#00bcd4">count(字段) 统计走二级索引</font>
<font color="#f57c00">count(主键 id) 统计走主键索引</font>
<font color="#00bcd4" style="">二级索引存储数据</font><font color="#000000"> 比 </font><font color="#00bcd4" style="">主键索引</font><font color="#000000"> 少</font>
<b>字段<font color="#ba68c8">无索引</font>时:</b>
<b>count(*) <font color="#ba68c8">≈</font> count(1)<font color="#ba68c8"> ></font> count(主键 id) <font color="#ba68c8">></font> count(字段)</b>
<b><font color="#000000"> count(主键 id) </font><font color="#f57c00">> </font><font color="#000000">count(字段):</font></b>
<font color="#e57373">count(字段) 统计走不了索引</font><br>
<font color="#ba68c8" style="">count(主键 id) </font><font color="#ba68c8">还可以</font><font color="#ba68c8" style="">走主键索引</font>
<b>count(1)<font color="#f57c00"> ></font> count(字段):</b>
count(1) 跟 count(字段) 执行过程类似
<font color="#4caf50">count(1) 不需要取出字段统计</font>,<font color="#4caf50">就用常量1做统计</font>
<font color="#e57373" style="">count(字段) 还需要取出字段</font>
<b>count(*) <font color="#f57c00">≈</font> count(1):</b>
<font color="#f57c00">count(*) 是个例外</font>
mysql并<font color="#f57c00">不会把全部字段取出来</font>
而是<font color="#4caf50">专门做了优化</font>,<font color="#4caf50">不取值,按行累加,效率很高</font>
<font color="#e57373">不需要用 count(列名) 或 count(常量) 来替代 count(*)</font>
<font color="#f57c00">为什么<b>对于 count(id) </b>,MySQL<b>最终选择辅助索引而不是主键聚集索引?</b></font>
因为<b>二级索引相对主键索引存储数据更少</b>,<b><font color="#f57c00">检索性能应该更高</font></b>,<b><font color="#4caf50">MySQL内部做了点优化</font></b><font color="#9e9e9e">(应该是在5.7版本才优化)</font>。
<b><font color="#2196f3">2. 常见优化方法</font></b>
<b>1、查询mysql自己维护的总行数</b>
<b>1)</b>对于<b>MyISAM存储引擎</b>的表:<br>
如下所示:
<font color="#2196f3">不带where条件的count查询性能很高</font>
因为表的<font color="#f57c00">总行数会被MySQL存储在磁盘上</font>,<font color="#4caf50">查询不需要计算</font>
<b>2)</b>对于<b>Innodb存储引擎</b>的表:
<font color="#e57373">查询count需要实时计算</font>
<font color="#e57373">MySQL不会存储表的总记录行数</font><font color="#f57c00">(因为有MVCC机制)</font>
<b>2、使用 show table status</b>
如果只需要知道<font color="#f57c00">表总行数的<b>估计值</b></font>可以用,<font color="#2196f3">如下sql查询,性能很高</font>
如下所示:<font color="#9e9e9e">(该值不准确,只适用于特定场景)</font>
<b>3、将总数维护到Redis里</b>
<font color="#2196f3">插入或删除表数据行</font>的时候,<font color="#2196f3">同时维护到 Redis 里的表总行数key的计数值</font><font color="#9e9e9e">(用incr或decr命令)</font>,
但是<font color="#f57c00">这种方式可能不准</font>,<font color="#e57373">很难保证</font>表和 Redis <font color="#e57373">操作的事务一致性</font>
<b>4、增加数据库计数表</b>
<font color="#2196f3">插入或删除表数据行</font>的时候,<font color="#2196f3">同时维护计数表</font>,让他们<font color="#f57c00">在同一个事务里操作</font>
<b><font color="#f57c00">阿里MySQL规范推荐解读</font></b>
1. <font color="#f44336">单表行数超过 500 万行</font>或者<font color="#f44336">单表容量超过 2GB</font>,<font color="#2196f3">才推荐进行分库分表</font>
2. 业务上<font color="#f57c00">具有唯一特性的字段</font>,即使是组合字段,也<font color="#2196f3">必须建成唯一索引</font>
3. <font color="#f44336">超过三个表禁止 join</font>。需要<font color="#2196f3"> join 的字段数据类型保持绝对一致</font>;多表关联查询时,<font color="#2196f3">保证被关联的字段需要有索引</font>。<font color="#4caf50">关联逻辑可放在java中处理,而且扩展性比较好</font>。
4. 页面<font color="#f44336">搜索严禁左模糊或者全模糊</font>,<font color="#2196f3">如果需要请走搜索引擎来解决</font>
5. <font color="#f44336">不要使用count(列名),不会统计null值</font>,<font color="#2196f3">使用count(*)</font>
6. <font color="#f44336">禁止使用存储过程</font>
7. <font color="#f57c00">in 操作能避免就避免,控制在100个以内</font>
8. <font color="#2196f3">采用utf8 字符集(3kb)</font>,utf8mb4存储表情
9. <font color="#2196f3">定长的字符用char,不定长的用varchar</font>
10. <font color="#2196f3">小数类型为 decimal</font>,<font color="#f44336">禁止使用 float 和 double</font>
<b><font color="#f57c00">MySQL字段类型的选择</font></b>
<b>1. 选择正确的数据类型,对于性能至关重要</b>。<br>
一般应该遵循下面两步:
<b>1)确定合适的大类型:</b>数字、字符串、时间、二进制
<b>2)确定具体的类型:</b>有无符号、取值范围、变长定长等
在MySQL数据类型设置方面,<font color="#2196f3">尽量用更小的数据类型</font>,<font color="#f57c00">因为它们通常有更好的性能,花费更少的硬件资源</font>。并且,<font color="#2196f3">尽量把字段定义为NOT NULL</font>,<font color="#f44336">避免使用NULL</font>
<b>2. 数值类型</b>优化建议:
1)如果整形数据没有负数,如ID号,<font color="#2196f3">建议指定为UNSIGNED无符号类型</font>,<font color="#f57c00">容量可以扩大一倍</font><br>
2)<font color="#2196f3">建议使用 TINYINT</font> <font color="#f44336">代替 ENUM、BITENUM、SET</font>
3)<font color="#f44336">避免使用整数的显示宽度</font><font color="#9e9e9e">(参看笔记最后)</font>,也就是说,<font color="#f44336">不要用INT(10)类似的方法指定字段显示宽度</font>,<font color="#2196f3">直接用INT</font>
4)<font color="#2196f3">DECIMAL 最适合保存准确度要求高,而且用于计算的数据</font>,比如价格。但是<font color="#f57c00">在使用DECIMAL类型的时候,注意长度设置</font>。
5)建议使用整形类型来运算和存储实数,方法是:实数乘以相应的倍数后再操作。<br>
6)<font color="#2196f3">整数通常是最佳的数据类型</font>,<font color="#f57c00">因为它速度快,并且能使用 AUTO_INCREMENT</font>。
<b>3. 日期和时间</b>优化建议:
1)<font color="#f57c00">MySQL能存储的最小时间粒度为秒</font>
2)<font color="#2196f3">建议用DATE数据类型来保存日期,</font><font color="#9e9e9e">MySQL中默认的日期格式是yyyy-mm-dd</font>。<br>
3)<font color="#2196f3">用MySQL的内建类型DATE、TIME、DATETIME来存储时间</font>,<font color="#f44336">而不是使用字符串</font>。
4)<font color="#2196f3">当数据格式为 TIMESTAMP 和 DATETIME 时,可以用 CURRENT_TIMESTAMP 作为默认</font><font color="#9e9e9e">(MySQL5.6以后)</font>,MySQL会自动返回记录插入的确切时间。
5)<font color="#f57c00">TIMESTAMP是UTC时间戳</font>,<font color="#f57c00">与时区相关</font>。
6)<font color="#f57c00">DATETIME 的存储格式是一个 YYYYMMDD HH:MM:SS 的整数</font>,<font color="#f57c00">与时区无关</font>,存了什么读出来就是什么
7)<font color="#2196f3">除非有特殊需求,一般建议使用 TIMESTAMP</font>,<font color="#f57c00">它比DATETIME更节约空间</font>,<font color="#f44336">但是像阿里这样的公司一般会用DATETIME,因为不用考虑TIMESTAMP将来的时间上限问题</font>。
8)有些<font color="#f44336">把Unix的时间戳保存为整数值</font>,但是<font color="#f44336">这通常没有任何好处</font>,<font color="#f44336">格式处理起来也不太方便</font>,不推荐
<b>4. 字符串</b>优化建议:
1)<font color="#f57c00">字符串的长度相差较大</font><font color="#2196f3">用 VARCHAR</font>;<font color="#f57c00">字符串短且所有值都接近一个长度</font><font color="#2196f3">用CHAR</font>
2)CHAR 和 VARCHAR:
<font color="#2196f3">适用于包括人名、邮政编码、电话号码和不超过255个字符长度的任意字母数字组合</font>
<font color="#f44336">那些要用来计算的数字,不要用VARCHAR类型保存</font>,因为<font color="#f44336">可能会导致一些与计算相关的问题</font>
3)<font color="#f44336">尽量少用 BLOB 和 TEXT</font>,如果<font color="#2196f3">实在要用</font>可以考虑将 BLOB 和 TEXT 字段<font color="#2196f3">单独存一张表,用id关联</font>
4)<font color="#f57c00">BLOB系列存储二进制字符串,与字符集无关;</font><font color="#f57c00">TEXT系列存储非二进制字符串,与字符集相关</font>
5)<font color="#f44336">BLOB 和 TEXT 都不能有默认值</font>
<b>补充:INT显示宽度</b>
我们经常会使用命令来创建数据表,而且同时会指定一个长度,如下所示:<br>
CREATE TABLE `user`(<br> `id` TINYINT(2) UNSIGNED<br> );<br>
<b>但是,</b><font color="#f44336">这里的长度并非是TINYINT类型存储的最大长度</font>,<font color="#2196f3">而是显示的最大长度</font>
这里user表的<b>id字段的类型</b>是 <b>TINYINT</b>,可以<b>存储的最大数值是255</b>
如果<font color="#2196f3">存入值小于等于255</font>,如200,虽然超过<font color="#f57c00">(2)</font>位,但是<font color="#f57c00">没有超出TINYINT类型长度</font><font color="#2196f3">,可以正常保存</font>
如果<font color="#f57c00">存入值大于255</font>,如500,那么MySQL会提示 <font color="#f44336">Out of range value for column 'id'</font>
这里<b>TINYINT(2)中2的作用</b>,是当需要<b>在查询结果前填充0</b>时,在建表语句中加上 <b>ZEROFILL </b>就可以实现,如下:
CREATE TABLE `user` (<br> `id` TINYINT(2) UNSIGNED ZEROFILL<br>);
如果存入值是5,那么查询结果输出就是05<br>
其实实际存储的值还是5,只是MySQL输出数据时在前面填充了0
在MySQL命令中,<b>字段的类型长度TINYINT(<font color="#ff9800">2</font>)、INT(<font color="#f57c00">11</font>)不会影响数据的插入</b>,<font color="#2196f3">只会在使用ZEROFILL时有用,让查询结果前填充0</font><br>
0 条评论
下一页