编写SQL语句是用来操作关系型数据库,操作的方式有两种——读取数据和写入数据,读取数据库的SQL语句上次已经说过了,这次就来看一下写入数据库的SQL语句。

CREATE DATABASE (数据库名)

无论是读取数据库还是写入数据库,前提必须是要有相应的数据库,如果没有我们只能创建。创建数据库很简单,执行SQL语句——CREATE DATABASE (数据库名)就行了,下面我们就来创建一个数据库来看看,在创建之前我们先看一下已经有哪些数据库,因为我们要创建一个不存在的数据库,创建已经存在的数据库会出错。

安全的数据库图形管理工具(4):SQL语句(2)_字段名

下面我们来创建一个数据库test,创建完之后我们再看一下test数据库是不是已经有了,如图所示。

安全的数据库图形管理工具(4):SQL语句(2)_字段_02

可以发现执行完创建数据库的这个数据库已经有了,说明没有什么问题。这个SQL语句有一个变量——数据库名,封装的方法有一个参数,这个参数就是数据库名。

CREATE TABLE语句

仅仅创建数据库是不够的,要想存放数据我们必须创建数据表,一个数据表具有表名,字段名两个属性,每个字段名都有字段类型和约束这两个属性。在创建表之前,我首先需要讲一些原理性的东西,主要是关于字段类型和约束的,字段名大家可以随便叫,只要不重复,但最好要有实际意义。字段类型和约束不能随便弄了,这个是已经规定好的协议,下面我们就来一一看看。

MySQL数据类型

MySQL数据类型有很多,最主要的也就那么几个,字符串,整数,浮点数,定点数,日期时间。当然还有很多我没接触过的类型,比如二进制,枚举,集合等。我也只敢讲一下我懂的数据类型。

字符串

字符串类型主要有两种,CHAR和VARCHAR,因为字符串是可变长对象,所以在这两个数据类型后面必须要有一个长度,这个长度就是所能存放的最大字符的个数。CHAR(10)和VARCHAR(10)表示都只能最多存放10个字符,CHAR(10)表示不管存放多少个字符,他都占用10个字符的空间(也就是10字节),那么如果是VARCHAR(10)就是存放一个字符只占用一个字符的空间吗?错,是占用两个字节,为什么是两个呢?多出来这一个字节用来保存实际使用了多少长度。因此如果有大量的数据都是正好占满规定的最大长度,这个时候用CHAR就比较省空间。既然实际长度只能用多出来的一个字节表示,一个字节所表示的无符号整数最大只能到255,所以无论是CHAR还是VARCHAR,最大只能定义255个长度,如果要定义超过255个长度的字符串怎么办?我们可以使用TEXT系列的类型,TEXT系列的类型有TINYTEXT(和VARCHAR差不多,最大长度也是2(1*8)-1=255,占用空间也是实际长度+1),TEXT(最大长度2(2*8)-1=65535,占用空间是实际长度+2),MEDIUMTEXT(最大长度2(3*8)-1,占用空间是实际长度+3),LONGTEXT(最大长度2(4*8)-1,占用空间是实际长度+4)。一般情况下VARCHAR基本够用,所以字符串用的最多的还是VARCHAR。

整数

整数就比较简单了,整数有以下几类TINYINT,SMALLINT,MEDIUMINT,INT,BIGINT,分别占用1字节,2字节,3字节,4字节,8字节。它们所能表示的数字范围是不一样的,还分有无符号位。

有符号整数

有符号整数就是第一位表示符号位(第一位是0就表示正数,第一位是1就表示负数),比如TINYINT是一个字节,一个字节是8位,它就只能用7位表示数值,7位全部都是1,二进制转十进制也就是127,那么它的范围不就是-127~127之间吗?其实不是,是-128~127(-2(1*8-1) ~ 2(1*8-1)-1)之间,为什么是这样,我们先看一下0这个数,如果真的是-127~127,那么+0和-0都是一个数值,一个数值有两种形式的表示不是太好,因为我完全可以把其中一个0的表示放到边界,让它可以多表示一个数,这里它放到了负数的边界,为什么是负数的边界?因为+0的二进制是00000000,在0的二进制表示我们用的是+0的二进制表示,还缺一个-0,-0的二进制是10000000,因为第一位符号位是1,所以它是一个负数。还没完,为什么要用+0来表示0?我完全可以用-0表示0,+0拿去表示128啊。这是因为负数的表示都是补码,补码就是反码的基础上+1得来的,反码就是源码中的每一位都取反得来的,我们以-128为例,我们先不表示-128,先写出128的二进制表示,也就是10000000(没有符号位),补码-1得出反码01111111,反码取反得到源码10000000也就是128,补码和源码一致,似乎既可以表示+128,也可以表示-128,但为了和之前定义的说法(第一位是1就表示负数)一致,所以只能是-128。

同理可得SMALLINT的表示范围是-2(2*8-1) ~ 2(2*8-1)-1,MEDIUMINT的表示范围是-2(3*8-1) ~ 2(3*8-1)-1,INT的表示范围是-2(4*8-1) ~ 2(4*8-1)-1,BIGINT的表示范围是-2(8*8-1) ~ 2(8*8-1)-1。

无符号整数

无符号整数就是没有符号位,把每一位都拿来存放数字,还是以TINYINT为例,最小值的二进制就是8个0,最大值的二进制就是8个1,对应的范围就是0~255(0~21*8-1)。同理可得SMALLINT的表示范围是0~22*8-1,MEDIUMINT的表示范围是0~23*8-1,INT的表示范围是0~24*8-1,BIGINT的表示范围是0~28*8-1。

其实我们在选择整数类型的时候只要记住一点,够用就行。比如表示年龄我完全可以用无符号的TINYINT。

浮点数

浮点数和整数一样,都是数值,都可以分为有符号位和无符号位两种。浮点数主要有两种FLOAT(单精度,占4个字节)和DOUBLE(双精度,占8个字节)。它们所能表示的范围说起来太繁琐了,这里就不叙述了。一般情况下FLOAT够用,因此小数基本都是FLOAT。

定点数

浮点数真的可以精确表示吗?!大部分浮点数无法精确表示,浮点数可以精确表示的也就只有0.5=1/2,0.25=1/4……如果是0.1=1/10这就没有办法精确表示了,为什么呢?因为计算机中的数都是二进制格式,十进制数0.5转换成二进制是0.1,十进制数0.25转换成二进制就是0.01,十进制的0.1转换成二进制是什么大家可以自己算一下,可能算一天都算不到这个二进制数。(Python的float类型不能精确表示也是这个原因)但是我们有的时候要求必须精确,这个时候可以使用定点数类型,定点数类型有两个——DECIMAL和NUMERIC,这两个随便记住一个就行,因为两个差不多。这种类型还有一个好处就是不会越界,既然不会越界,存储空间一定是动态申请的,这种类型的缺点就是将数按照字符串格式保存,每一个数字是一个字节,小数点是一个字节,符号也是一个字节,非常耗空间。

日期时间

日期时间类型很简单,无非就是DATE,TIME,DATETIME,TIMESTAMP,YEAR等几种类型,如果只需要日期就用DATE类型(占4个字节,表示范围1000-01-01~9999-12-31),如果只需要时间就用TIME类型(占3个字节,表示范围-838:59:59~838:59:59),如果只需要年就用YEAR类型(占1个字节,表示范围1901~2155),如果什么都需要可以先考虑TIMESTAMP类型(占4个字节,表示范围和时区有关,如果是GMT+8时区(北京时间),表示范围就是1970-01-01 08:00:01~2038年的某一个时刻,如果是GMT+0时区(格林尼治时间),表示范围就是1970-01-01 00:00:01~2038年的某一个时刻),如果要想让数据保存到2040年以后,但又什么都需要,可以使用DATETIME类型(占8个字节,表示范围1000-01-01 00:00:00~9999-12-31 23:59:59)。这些数据虽然最小值都不是从0开始,但它们都有一个零值表示,这种情况一般出现在插入数据的时候,比如秒的最大范围是59,少数情况下是60(因为闰秒),但是我在插入的时候,把秒的值写成了70,就直接表示成为0000-00-00 00:00:00,这就是零值表示。

MySQL约束

MySQL约束也就5种——主键约束,默认约束,唯一约束,外键约束,非空约束。

默认约束

默认约束就是给拥有默认约束的字段设置默认值,在插入数据时如果不指明该字段的值,那么就采用在创建表时的默认值。

唯一约束

唯一约束就是确保拥有唯一约束的字段不重复,比较简单。

非空约束

非空约束就是确保拥有非空约束的字段不是空,也很简单。

主键约束

拥有主键约束的字段不能为空也不能重复,这么说,主键约束就是唯一约束+非空约束吗?不是的,因为存在一个东西叫复合主键,复合主键就是把一张表的多个字段给标记成主键,说到这里,可能有些人会觉得云里雾里。我们来看一个例子吧。

A

B

0

0

0

1

1

0

1

1

这个例子有两个字段A和B,如果两个字段都是唯一约束+非空约束的话,就不可能存在上面的记录,因为上面的记录A和B两个字段都存在重复,但是如果是A和B使用复合主键来约束,上面的数据是可以存在的,因为不存在重复的主键。实际上每个表都有主键约束,即使在不设置主键约束的情况下。如果没有设置主键约束,它默认就是把全部字段作为一个联合主键,这样可以确保数据唯一。但一般情况下都要设置主键,如果联合主键字段太多就会导致性能下降。

外键约束

外键约束在关系数据库的一对多关系和多对多关系中最常见,一个表可以有多个外键,每一个外键都必须和另一个表或者当前表的主键关联。被外键约束的列,取之必须在它关联的列中有对应值。

下面我们就来创建两个表,一个是存放用户信息,一个是存放用户聊天记录。因为一个用户可以说很多句话,这是一对多的关系,所以存在外键约束。用户信息表有用户名(VARCHAR(255),主键),密码(VARCHAR(255),非空)。用户聊天记录表有ID(UNSIGNED INT,主键,自增),用户名(VARCHAR(255),外键参考用户信息表的主键,非空),用户说话内容(VARCHAR(255),非空),说话时间(DATETIME,非空)。之所以我要多设置一个字段ID,是因为可能存在用户刷屏情况,如果同一个用户在一秒内发了两次相同的消息,这样的话就会出现完全重复的两行(时间也只不过精确到秒而已),从而导致各种错误。

安全的数据库图形管理工具(4):SQL语句(2)_字段_03

从图中我们可以看出,两张表都创建成功了,创建表的语法格式如下:

CREATE TABLE 表名 (
字段名1 类型 (其它选项), /*其他选项就是负责定义约束和规定一些其他东西的,是可选的。非空约束:NOT NULL,唯一约束:UNIQUE,默认约束:DEFAULT (默认值),自增:AUTO_INCREMENT*/
...
字段名n 类型 (其它选项),
PRIMARY KEY(字段名p1,字段名p2...) /*主键约束,多个字段共同形成联合主键用逗号隔开,联合主键用得比较少*/
KEY 字段名k1(字段名k1),
CONSTRAINT 字段名k1 FOREIGN KEY(字段名k1) REFERENCES 表名(字段名f1) /*外键约束,字段名f可以是当前表的另一个字段,也可以是其他表的某个字段*/
...
);

创建表的SQL语句封装就有一些复杂了,因为可以填写的信息太多了,但是从中也发现了一些规律,每个字段都具有差不多的属性,必须要有字段名和字段类型,约束和自增等都是可选。因此我们可以把字段给抽象成一个类,这个类的构造方法必须有2个必选参数,4个默认参数。两个必选参数是字段名和字段类型,4个默认参数分别是null(默认False,bool类型),unique(默认False,bool类型),default(默认None),auto_increment(默认False,bool类型)。这样,就可以对字段类进行实例化产生字段对象,把字段对象放到一个list中传给创建表的这个方法,此时,创建表的第一个参数出现了。还有两个参数,第二个参数是用来设置主键字段(默认None,set类型,如果有联合主键我们就可以把字段存放在set中),第三个参数是设置外键的(默认None,set类型,这个set里面的每一个元素是一个元组,每个元组有三个元素(字段名, 参考表名, 参考字段名)),其实还有一些缺陷,但暂时不用管,毕竟我感觉菜鸟能懂约束啥的就已经很不错了,再说了数据库大佬应该很容易看懂我封装的思路,也就可以很轻松的扩展。

INSERT INTO 表名(字段名1, 字段名2…) VALUES(值1, 值2…)

当我们创建完一个表之后,就需要往里面插入数据,插入数据非常简单,语法如同标题所示。下面我来演示一下,如图所示。

安全的数据库图形管理工具(4):SQL语句(2)_字段名_04

这一点也不难,封装成方法只要两个参数,第一个参数是表名,第二个参数是一个字典:键是字段名,值是字段值。

DELETE FROM 表名 (WHERE 条件)

当一个表的数据太多,会影响数据库性能。这个时候我们首先考虑的是删除数据记录,删除数据也是非常简单,语法如同标题所示。下面我来操作一下,如图所示。

安全的数据库图形管理工具(4):SQL语句(2)_数据库_05

我在这里就是简单的删除了密码为123的所有用户。这个SQL语句依旧可以封装,而且也不是太难,一个必选参数,一个默认参数,必选参数是表名,默认参数是条件。

UPDATE 表名 SET 字段名1=新值1, 字段名2=新值2… (WHERE 条件)

有增加,有删除,当然也就少不了修改。修改数据非常简单,执行语法如同标题所示。下面我来演示一下,如图所示。

安全的数据库图形管理工具(4):SQL语句(2)_字段_06

我这里只是简单的把密码为234的用户的密码更改成了432。还是将这个SQL语句封装一下吧,总共三个参数,前两个必选,最后一个可选,第一个必选参数是表名,第二个必选参数是一个字典,键是字段名,值是新值,那个可选参数是条件。

DROP TABLE 表名

当我们想要删除数据库的一张表,就执行对应的SQL语句,语法如同标题所示。下面我来演示一下,如图所示。

安全的数据库图形管理工具(4):SQL语句(2)_字段_07

这个SQL语句封装成方法只要一个参数就行,这个参数是表名。

DROP DATABASE 数据库名

如果我们想要删除一个数据库,执行对应的SQL语句即可,语法如同标题所示。下面我来简单的演示一下,如图所示。

安全的数据库图形管理工具(4):SQL语句(2)_字段名_08

这个SQL语句依旧可以封装,封装好的方法有一个参数——数据库名。

现在读取数据库和写入数据库都讲完了,明天将真真正正对这些方法实现封装,因为可能会有一些细节问题。

今天的文章有不懂的可以加群,群号:822163725,备注:小陈学Python,不备注可是会被拒绝的哦~!


安全的数据库图形管理工具(4):SQL语句(2)_数据库_09