该系列文章系个人读书笔记及总结性内容,任何组织和个人不得转载进行商业活动!
ALTER:改写历史
使用ALTER命令:
可以修改表,对其套用新的设计方法,且不会影响现有数据;
本章还会学到规范化的意义,我们要规范化我们的表;
由于重新建了本地数据库,相应的练习数据表需要重新建;
新建数据库:hq_drinks;
新建表:summer_drinks;
建表SQL:
mysql> CREATE TABLE summer_drinks
-> (
-> drink_name VARCHAR(30),
-> cost DEC(4,2),
-> burdening VARCHAR(50)
-> );
Query OK, 0 rows affected (0.02 sec)
新建的summer_drinks表:
包含了三个字段:饮品名称-drink_name、价格-cost、配料-burdening;
mysql> desc summer_drinks;
+------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+-------+
| drink_name | varchar(30) | YES | | NULL | |
| cost | decimal(4,2) | YES | | NULL | |
| burdening | varchar(50) | YES | | NULL | |
+------------+--------------+------+-----+---------+-------+
我们需要一些改变:
我们对该表新插入若干条数据;
我们现在想对这张表做一些改变,但又不想丢失数据;
当前的表内容:
mysql> SELECT * FROM summer_drinks;
+------------------+-------+------------------+
| drink_name | cost | burdening |
+------------------+-------+------------------+
| Apple Juice | 15.50 | Apple,Water |
| Orange Juice | 15.50 | Orange,Water |
| Banana Juice | 15.50 | Banana,Water |
| Strawberry Juice | 15.50 | Strawberry,Water |
+------------------+-------+------------------+
比如,我们想新添加一列表示饮品的量,单位是ml:
我们可以使用ALTER轻松添加它;
添加新列:
mysql> ALTER TABLE summer_drinks
-> ADD COLUMN milliliter INT;
指定新列的安放位置:
删除刚刚添加的列,重新添加,我们来看看如何指定该列的安放位置;
删除指定列:
mysql> ALTER TABLE summer_drinks
-> DROP COLUMN milliliter;
也可以删除多列:
mysql> ALTER TABLE drinks_sum
-> DROP COLUMN burdening_main,
-> DROP COLUMN burdening_sub;
使用关键字AFTER:
和之前我们接触的FIRST类似;
mysql> ALTER TABLE summer_drinks
-> ADD COLUMN milliliter INT AFTER cost;
现在我们再来看下表:
mysql> SELECT * FROM summer_drinks;
+------------------+-------+------------+------------------+
| drink_name | cost | milliliter | burdening |
+------------------+-------+------------+------------------+
| Apple Juice | 15.50 | NULL | Apple,Water |
| Orange Juice | 15.50 | NULL | Orange,Water |
| Banana Juice | 15.50 | NULL | Banana,Water |
| Strawberry Juice | 15.50 | NULL | Strawberry,Water |
+------------------+-------+------------+------------------+
关键字AFTER:
紧跟在新添加的列的名称后面;
AFTER是可选关键字;如果不使用它,新列则会添加到表的最后;
类似的我们还有:
FIRST、AFTER column、BEFORE column、LAST;
另外还有SECOND、THIRD可供使用;
修改表:
根据前一章关于ALTER的介绍,我么实践了添加新列;现在我们来看看如何修改表;
对于上边我们新建的summer_drinks表,之所以要修改它,是因为它并不符合我们规范化的要求;
burdening不满足原子性数据的要求,表没有主键;
其他名称我们也想统一下;
ALTER命令:
几乎能让你改变表里的一切,而且不需重新插入数据;
但要小心,如果改变的是列的类型,可能会有遗失数据的风险;
承担具体修改的关键字:
1.CHANGE:
可同时改变现有列的名称和数据类型;(修改数据类型可能会遗失数据)
2.MODIFY:
修改现有列的数据类型或位置;(修改数据类型可能会遗失数据)
3.ADD:
在当前表中添加一列——可自选类型;ADD新列的时候,可以指定新列的位置;
4.DROP:
从表中删除某列;(对应的也会删除数据)
5.其他关键字,比如RENAME等,实践时会介绍;
修改表名:
我们尝试为summer_drinks表起一个更加简洁的名字:
mysql> ALTER TABLE summer_drinks
-> RENAME TO drinks_sum;
修改列名:
我们尝试修改milliliter列名为meter:
mysql> ALTER TABLE drinks_sum
-> CHANGE COLUMN milliliter meter INT;
现在,我们为表添加两列:drink_id,drink_des,分别表示饮品的编号和描述;
mysql> ALTER TABLE drinks_sum
-> ADD COLUMN drink_id CHAR(2) FIRST,
-> ADD COLUMN drink_des VARCHAR(100);
现在我们的表是这样的:
mysql> SELECT * FROM drinks_sum;
+----------+------------------+-------+-------+------------------+-----------+
| drink_id | drink_name | cost | meter | burdening | drink_des |
+----------+------------------+-------+-------+------------------+-----------+
| NULL | Apple Juice | 15.50 | NULL | Apple,Water | NULL |
| NULL | Orange Juice | 15.50 | NULL | Orange,Water | NULL |
| NULL | Banana Juice | 15.50 | NULL | Banana,Water | NULL |
| NULL | Strawberry Juice | 15.50 | NULL | Strawberry,Water | NULL |
+----------+------------------+-------+-------+------------------+-----------+
重新装备列:
我们已经掌握了修改表名、修改列名,很好,但还不够,对于我们当前的表结构我们还需要:
1)添加主键:将drink_id列作为主键,名字替换为更简洁的id,同时列的类型也需要替换为关键字AUTO_INCREMENT需要的整数类型;
2)数据原子性支持:burdening列,拆分成burdening_main、burdening_sub;
ALTER和CHANGE:
我们来看看如何使用这两个关键字实现主键的需求:
把drink_id列名修改为id;
修改id的数据类型为INT;
添加AUTO_INCREMENT,将其标注为主键;
同时修改数据类型和名称,使用关键字CHANGE最为合适;
mysql> ALTER TABLE drinks_sum
-> CHANGE COLUMN drink_id id INT NOT NULL AUTO_INCREMENT,
-> ADD PRIMARY KEY(id);
Query OK, 4 rows affected (0.06 sec)
CHANGE COLUMN之后是旧列名 之后 紧跟 新列名及其数据类型、限制条件等;在上述命令中我们指定了主键;
现在的表是这样的:
mysql> SELECT * FROM drinks_sum;
+----+------------------+-------+-------+------------------+-----------+
| id | drink_name | cost | meter | burdening | drink_des |
+----+------------------+-------+-------+------------------+-----------+
| 1 | Apple Juice | 15.50 | NULL | Apple,Water | NULL |
| 2 | Orange Juice | 15.50 | NULL | Orange,Water | NULL |
| 3 | Banana Juice | 15.50 | NULL | Banana,Water | NULL |
| 4 | Strawberry Juice | 15.50 | NULL | Strawberry,Water | NULL |
+----+------------------+-------+-------+------------------+-----------+
如果需要改变两个列,我们可以以一条SQL语句来实现:只需要在ALTER TABLE后放两个CHANGE COLUMN,中间以逗号隔开;
注意数据类型也修改了:
如果把数据改成另一种类型,你可能会丢失数据;
如果你想改变的数据类型和原始数据类型不兼容,命令则不会执行;但真正的惨剧可能发生在类型兼容的情况下,你的数据可能被截断;
例如:从varchar(10)改为char(1),数据’Bonzo’将被砍成’B’;
相同的问题也会出在数字类型上,如精度问题引起的数据丢失;
只改变数据类型:
可以CHANGE COLUMN column column NEWTYPE;但我们有更简单的方式;
我们可以使用关键字MODIFY,他只会修改列的类型而不会干涉他的名称;
mysql> ALTER TABLE drinks_sum
-> MODIFY COLUMN drink_des VARCHAR(50);
这里注意:我们指定的新数据类型长度是50,原来是100,我们需要确定新类型不会造成旧数据被截断;
改变列的顺序:
创建表后就无法真正地改变列的顺序了;最多只能在指定位置添加新列,然后删除旧列并指定位置,但这样会丢失数据;
实际上列的顺序并不会造成问题,因为在SELECT查询中可以指定查询结果的列顺序;硬盘里存储数据的顺序并不重要;
SELECT column3,column2,column1 FROM table;
重新认识DROP:
更准确的来说,应该叫“卸除”;对于用不到的列,可以把它卸除DROP;
你的列越多,RDBMS的工作就越累,所占空间也越大;查询变慢、计算机的处理器也会运行得很辛苦;
一旦你卸除列,原本存储在该列中的一切内容都会跟着被卸除;因此使用DROP COLUMN时需要务必小心;或许应该先以SELECT选取出列,确定是我们要删除的列;
我们可以用一个ALTER TABLE,配合之前我们接触到的RENAME TO、ADD COLUMN、CHANGE COLUMN等子句进行搭配使用,多个子句中间以逗号分割;
没有蠢问题:
1.之前说过,MODIFY无法重新排列列的顺序,但是我们的SQL软件工具让我们重新排列他们,有是怎样做到的:
其实软件在背后做了好多,他会把列的内容复制到临时表中,然后卸除该列,再用ALTER创建同名列名,放到指定位置,而后把临时表的内容复制到新列中,最后再删除临时表;
一般而言,如果列中已经有内容,而且你使用的软件无法完成上述操作的话,最好不要对列的位置动手动脚,而是使用SELECT时指定顺序;
2.如果已经创建了主键,然后又意外想改成另一列,该怎样做:
只移除主键;
ALTER TABLE table DROP PRIMARY KEY;
对于AUTO_INCREMENT的处理可以这样:
ALTER TABLE table
CHANGE your_id your_id INT NOT NULL AUTO_INCREMENT;
——>
ALTER TABLE table
CHANGE your_id your_id INT NOT NULL;
然后重新添加即可;
之所以这样做是因为:每个表中只有一列可以加上AUTO_INCREMENT,该列必须为整数类型而且不能包含NULL;
小结:
1.想要同时改变列的名称和类型时请用CHANGE;
2.只想改变数据类型时请用MODIFY;
3.DROP COLUMN的功能是从表中卸除指名的列;
4.使用RENAME改变表的名称;
5.使用FIRST、LAST、BEFORE column、AFTER column、THIRD、FOUTH等关键字,可以调整列的顺序;
6.有些RDBMS只有在添加新列的时才允许改变列的顺序;
数据原子性支持:
burdening列,拆分成burdening_main、burdening_sub;
使用SELECT、UPDATE时搭配ALTER TABLE。可以把使用不便,没有原子性的列调整为具有精确原子性的列;
具体的做法:
1.寻找模式:
我们看到 饮料配料 列的文本都是以逗号隔开的主副料,所以burdening列存储的数据都具有相同的模式;
这一模式有助于我们将其分割为更具原子性的数据;
把逗号前的数据放在新列burdening_main;逗号后的数据放在burdening_sub;
2.抓取逗号左右部分的函数;
一些字符串函数:
1)SELECT最后/前的若干个字符:
RIGHT()和LEFT():
可以从一个方向选出指定数量的字符;
mysql> SELECT RIGHT(burdening,5) FROM drinks_sum;
+--------------------+
| RIGHT(burdening,5) |
+--------------------+
| Water |
| Water |
| Water |
| Water |
+--------------------+
4 rows in set (0.00 sec)
2)SELECT逗号前的所有内容:
SUBSTRING_INDEX()可以撷取列值的部分,也称为子字符串(Substring);
这个函数会找到指定字符或指定字符串前的所有内容;逗号需要用引号括起来;
mysql> SELECT SUBSTRING_INDEX(burdening,',',1) FROM drinks_sum;
+----------------------------------+
| SUBSTRING_INDEX(burdening,',',1) |
+----------------------------------+
| Apple |
| Orange |
| Banana |
| Strawberry |
+----------------------------------+
4 rows in set (0.00 sec)
SUBSTRING_INDEX()函数会寻找引号中的参数字符或字符串,取出其前边的所有内容;
最后的参数1,表示的是寻找第几个逗号,如果是2,那取出的将是第二个都好前的所有内容;
3)SELECT指定位置后的所有内容:
SUBSTRING:截取子串;
mysql> help SUBSTRING;
Name: 'SUBSTRING'
Description:
Syntax:
SUBSTRING(str,pos), SUBSTRING(str FROM pos), SUBSTRING(str,pos,len),
SUBSTRING(str FROM pos FOR len)
Examples:
mysql> SELECT SUBSTRING('Quadratically',5);
-> 'ratically'
mysql> SELECT SUBSTRING('foobarbar' FROM 4);
-> 'barbar'
mysql> SELECT SUBSTRING('Quadratically',5,6);
-> 'ratica'
mysql> SELECT SUBSTRING('Sakila', -3);
-> 'ila'
mysql> SELECT SUBSTRING('Sakila', -5, 3);
-> 'aki'
mysql> SELECT SUBSTRING('Sakila' FROM -4 FOR 2);
-> 'ki'
4)其他字符串辅助函数:
UPPER()、LOWER()、REVERSE():反转字符串里的字符排序、LTRIM()/RTRIM():清除左/右的多余空格;
LENGTH():返回字符串中的字符数量;
LOCATE(search, string):获取search在string中的位置;
mysql> SELECT SUBSTRING(burdening,1,LENGTH(burdening)) FROM drinks_sum;
+------------------------------------------+
| SUBSTRING(burdening,1,LENGTH(burdening)) |
+------------------------------------------+
| Apple,Water |
| Orange,Water |
| Banana,Water |
| Strawberry,Water |
+------------------------------------------+
4 rows in set (0.00 sec)
mysql> SELECT SUBSTRING(burdening,1, locate(',',burdening) - 1) FROM drinks_sum;
+---------------------------------------------------+
| SUBSTRING(burdening,1, locate(',',burdening) - 1) |
+---------------------------------------------------+
| Apple |
| Orange |
| Banana |
| Strawberry |
+---------------------------------------------------+
4 rows in set (0.01 sec)
值得注意的是:
字符串的起始字符下标是1;
字符串函数也不会改变存储在表中的内容,只是把字符串修改后的模样作为查询结果返回;
3.以现有列的内容填入新列:
1)我们新加两个列:burdening_main、burdening_sub;
mysql> ALTER TABLE drinks_sum
-> ADD COLUMN burdening_main VARCHAR(20) AFTER burdening,
-> ADD COLUMN burdening_sub VARCHAR(20) AFTER burdening_main;
2)使用UPDATE SET新列的值:
mysql> UPDATE drinks_sum
-> SET burdening_main = SUBSTRING(burdening,1, LOCATE(',',burdening) - 1),burdening_sub = SUBSTRING(burdening,LOCATE(',',burdening) + 1, LENGTH(burdening) - LOCATE(',',burdening));
分析:
我们使用UPDATE一次SET了两个列的值,由于没有WHERE子句,表中的每一行都会被设定为新值;
当第一次遍历完成表时,这条语句抓出第一条记录的burdening列并套用函数;
然后这条语句再运行一次,这次抓到第二行的burdening列,继续套用函数,依次类推直到结束没有任何记录符合条件为止;
为了确定我们插入的值,我们可以先用SELECT查询:
SELECT SUBSTRING(burdening,1, LOCATE(',',burdening) - 1) FROM drinks_sum;
SELECT SUBSTRING(burdening,LOCATE(',',burdening) + 1, LENGTH(burdening) - LOCATE(',',burdening)) FROM drinks_sum;
总结:
ALTER TABLE:以保留表中现有数据为前提,修改表的名称及整体结构;
ALTER搭配ADD:以你指定的顺序把列添加到表中;
ALTER搭配DROP:从表中卸除列;
ALTER搭配CHANGE:同时修改现有列的名称和类型;
ALTER搭配MODIFY:只修改现有列的类型;
String Functions:这些函数可修改字符串列的内容副本并以查询结果的形式返回,原始数据不会改变;