python数据库-MySQL与python的交互(52)

一、python3中安装PyMySQL模块
命令安装:
sudo apt-get install python-mysql
或者
pip install pymysql
2、使用在pyCharm中安装PyMySQL模块
二、PyMysql对象说明
1、Connection对象
- 用于建立与数据库的连接
- 创建对象:调用connect()方法
conn=connect(参数列表)
- 参数host:连接的mysql主机,如果本机是’localhost’
- 参数port:连接的mysql主机的端口,默认是3306
- 参数database:数据库的名称
- 参数user:连接的用户名
- 参数password:连接的密码
- 参数charset:通信采用的编码方式,默认是’gb2312’,要求与数据库创建时指定的编码一致,否则中文会乱码
对象的方法
-
- close()关闭连接
- commit()事务,所以需要提交才会生效
- rollback()事务,放弃之前的操作
- cursor()返回Cursor对象,用于执行sql语句并获得结果
2、Cursor对象
- 执行sql语句
- 创建对象:调用Connection对象的cursor()方法
cursor1=conn.cursor()
对象的方法
- close()关闭
- execute(operation [, parameters ])执行语句,返回受影响的行数
- fetchone()执行查询语句时,获取查询结果集的第一个行数据,返回一个元组
- next()执行查询语句时,获取当前行的下一行
- fetchall()执行查询时,获取结果集的所有行,一行构成一个元组,再将这些元组装入一个元组返回
- scroll(value[,mode])将行指针移动到某个位置
- mode表示移动的方式
- mode的默认值为relative,表示基于当前行移动到value,value为正则向下移动,value为负则向上移动
- mode的值为absolute,表示基于第一条数据的位置,第一条数据的位置为0
对象的属性
- rowcount只读属性,表示最近一次execute()执行后受影响的行数
- connection获得当前连接对象
三、对Mysql基本操作的封装
1、我们创建MySQLManager.py模块,对Mysql的基本操作封装
# coding=utf-8; # 导入模块pymysql模块 import pymysql # 封装MySQL数据库管理类 class MySQL_Manager(object): # 初始化方法 def __init__(self,host,port,database,user,password,charset='utf8'): # 配置连接MySQL数据库的基本信息 self.host = host self.port = port self.database = database self.user = user self.password = password self.charset = charset # 使用python3链接MySQL数据库 def connect(self): # 链接 self.connect = pymysql.connect(host = self.host, port = self.port, database = self.database, user = self.user, password = self.password,charset = self.charset) # 得到一个可以执行SQL语句的光标对象 self.cursor = self.connect.cursor() # 操作完毕后关闭 def close(self): # 关闭执行语句 self.cursor.close() # 关闭连接 self.connect.close() # 创建表操作 def create_table(self,sql,params=()): # 先连接 self.connect() # 执行创建语句 self.cursor.execute(sql,params) # 关闭连接 self.close() # 查询一条数据 def select_one(self,sql,params=()): result = None try: self.connect() self.cursor.execute(sql,params) result = self.cursor.fetchone() self.close() except Exception as e: print(e) return result # 查询全部数据 def select_all(self,sql,params=()): list=() try: self.connect() self.cursor.execute(sql,params) list = self.cursor.fetchall() self.close() except Exception as e: print(e) return list # 插入 def insert(self, sql, params=()): return self.__edit(sql, params) # 修改 def update(self, sql, params=()): return self.__edit(sql, params) # 删除 def delete(self, sql, params=()): return self.__edit(sql, params) # 插入、修改、删除其实一样的,只是sql代码不同,但是为了代码的阅读性更高,还是分开写 def __edit(self, sql, params): count = 0 try: self.connect() count = self.cursor.execute(sql, params) self.connect.commit() self.close() except Exception as e: print(e) return count
2.创建testMySQL.py模块对我们创建的MySQLManager.py模块测试
# coding = utf-8 from MySQLManager import * mysql_manager = MySQL_Manager("192.168.100.114",3306,"Hero","root","123456") # 创建表 create_sql = "create table hero(id int auto_increment primary key,name varchar(20) not null unique,skill varchar(20) not null) engine=innodb default charset=utf8;" mysql_manager.create_table(create_sql) # 添加数据 insert_sql = "insert into hero(id,name,skill) values(1,'李白','青莲剑歌');" mysql_manager.insert(insert_sql) # 查询语句 select_sql = "select * from hero;" list = mysql_manager.select_all(select_sql) print(list) # 修改 update_sql = "update hero set name='韩信' where id=1;" mysql_manager.update(update_sql) # 删除语句 delete_sql = "delete from hero where id=1;" mysql_manager.delete(delete_sql)
说明:
- 为什么MySQLManager里面的方法都有params参数,这里却没用?因为这只是测试,我把所有的参数都直接写在了sql语句中,但是为了程序的可扩展性,建议大家还是使用参数params
- testMysql.py文件每运行一次里面对数据库的操作就会执行一次,例如创建表的操作只需要执行一次,当我们创建表成功之后,就可以把创建表的方法注释掉,不用再创建了,同样的,添加,删除也是一样。
python数据库-MySQL数据库高级查询操作(51)

一、什么是关系?
1、分析:有这么一组数据关于学生的数据
学号、姓名、年龄、住址、成绩、学科、学科(语文、数学、英语)
我们应该怎么去设计储存这些数据呢?
2、先考虑第一范式:列不可在拆分原则
这里面学科包含了三个学科,所以学科拆分为:语文学科、数学学科、英语学科,同样的成绩也要拆分为语文成绩、数学成绩、英语成绩。这样既满足了第一范式,各列可以设计为:
学号、姓名、年龄、住址、语文学科、数学学科、英语学科、语文成绩、数学成绩、英语成绩
3、在考虑第二范式:唯一标识
也就是说在1NF的基础上,非Key属性必须完全依赖于主键,第二范式(2NF)要求数据库表中的每个实例或记录必须可以被唯一地区分。选取一个能区分每个实体的属性或属性组,作为实体的唯一标识。即确定主键,我们可以选取学号为主键
4、接着在考虑第三范式:
确保表中各列与主键列直接相关,而不是间接相关。即各列与主键列都是一种直接依赖关系,则满足第三范式。
不难发现在这么多列中,年龄、住址和成绩、学科没有直接关系,也就是说我考多少分,和年龄及住址都无关,他们都是学生的信息,但都是不相关的信息,所以根据第三范式,我们需要将这些数据根据其相关性拆分为多个表。
5、表与表之间的关系
如果我们要想找到张三的语文成绩,那么我们就必须去成绩表中查找,因为成绩表中保存的所有人的所有学科成绩。但是在成绩表中查找的时候,需要从省标中查到张三的学号(stu_id)和从学科表中查到语文学科(sub_id)的编号,这个时候这三张表就发生了关系,这也就是关系型数据库的精髓,而根据这种表与表之间的关系也会衍生出很多的查询的高级操作
二、外键(foreign key)
外键约束:用于限制主表与从表数据完整性。
alter table scores add constraint 'stu_score_fk' foreign key(stu_id) references students(stu_id);
- 将scores表的stu_id外键关联到students表的stu_id字段(说明:这里scores表里面字段stu_id和students表里的stu_id重名了,最好避免重名)
- 每个外键都有一个名字,可以通过constraint指定
- 存在外键的表,称之为从表(子表),外键指向的表,称之为主表(父表)。
- 作用:保持数据一致性,完整性,主要目的是控制存储在外键表(从表)中的数据。例如,此时在从表插入或者修改数据时,如果stu_id的值在students表中不存在则会报错
- 外键也可以在创建表时可以直接创建约束
语法:
foreign key (外键字段) references 主表名 (关联字段)
例如:
create table scores( id int primary key auto_increment, stu_id int, sub_id int, score decimal(5,2), foreign key(stuid) references students(id), foreign key(subid) references subjects(id) );
[主表记录删除时的动作] [主表记录更新时的动作],此时需要检测一个从表的外键需要约束为主表的已存在的值。外键在没有关联的情况下,可以设置为null.前提是该外键列,没有not null。
三、外键的级联操作
- 在删除或者修改students表的数据时,如果这个stu_id值在scores中已经存在,则会抛异常
- 推荐使用逻辑删除,还可以解决这个问题
- 可以创建表时指定级联操作,也可以在创建表后再修改外键的级联操作
alter table scores add constraint stu_sco foreign key(stu_id) references students(stu_id) on delete cascade;
除了on delete还有on update都要注意级联操作
级联操作的类型包括:
- restrict(限制):默认值,抛异常,拒绝父表删除或者更新
- cascade(级联):如果主表的记录删掉,则从表中相关联的记录都将被删除,如果主表修改记录,则从表记录也将被修改
- set null:将外键设置为空
- no action:什么都不做
四、链接查询
在讲解第一个问题关系的时候,我们提到了,如果要查找张三的语文成绩,需要用到三个表,当我们查询结果来源于多张表的时候就需要使用连接查询
链接查询关键:找到表间的关系,当前的关系是
- students表的stu_id—scores表的stu_id
- subjects表的sub_id—scores表的sub_id
select students.stu_name,subjects.sub_name,scores.score from scores inner join students on scores.stu_id=students.stu_id inner join subjects on scores.sub_id=subjects.sub_id where students.stu_name='张三' and subjects.sub_name='语文';
+----------+----------+-------+
| stu_name | sub_name | score |
+----------+----------+-------+
| 张三 | 语文 | 80 |
+----------+----------+-------+
- 结论:当需要对有关系的多张表进行查询时,需要使用连接join
- 表A inner join 表B:表A与表B匹配的行会出现在结果中
- 表A left join 表B:表A与表B匹配的行会出现在结果中,外加表A中独有的数据,未对应的数据使用null填充
- 表A right join 表B:表A与表B匹配的行会出现在结果中,外加表B中独有的数据,未对应的数据使用null填充
- 在查询或条件中推荐使用“表名.列名”的语法
- 如果多个表中列名不重复可以省略“表名.”部分
- 如果表的名称太长,可以在表名后面使用’ as 简写名’或’ 简写名’,为表起个临时的简写名称
连接查询分类如下:
五、视图
视图就像我们python里面的函数一样,对SQL语言代码块的封装
-
- 对于复杂的查询,在多次使用后,维护是一件非常麻烦的事情
- 解决:定义视图
- 视图本质就是对查询的一个封装
- 定义视图
create view stuscore as select students.stu_name,subjects.sub_name,scores.score from scores inner join students on scores.stu_id=students.stu_id inner join subjects on scores.sub_id=subjects.sub_id;
这句话的意思就是用stuscore就相当于后面红色很长的一段SQL语句:
mysql> select *from stuscore; +----------+----------+-------+ | stu_name | sub_name | score | +----------+----------+-------+ | 张三 | 语文 | 80 | | 张三 | 数学 | 85 | | 张三 | 英语 | 88 | | 李四 | 语文 | 83 | | 李四 | 数学 | 84 | | 李四 | 英语 | 87 | | 王五 | 语文 | 81 | | 王五 | 数学 | 85 | | 王五 | 英语 | 85 | | 赵六 | 语文 | 88 | | 赵六 | 数学 | 88 | | 赵六 | 英语 | 87 | | 侯七 | 语文 | 84 | | 侯七 | 数学 | 83 | | 侯七 | 英语 | 82 | +----------+----------+-------+
六、自连接查询
有时在信息查询时需要进行对自身连接(自连接)自连接是单边操作,所以我们需要为表定义别名。举例说明,下面是一个新的学生成绩表,在下表中我们要找到比张三成绩高的。
+--------+----------+-----------+ | stu_id | stu_name | stu_score | +--------+----------+-----------+ | 1 | 张三 | 90 | | 2 | 李四 | 85 | | 3 | 王五 | 80 | | 4 | 赵六 | 95 | | 5 | 侯七 | 100 | +--------+----------+-----------+
一般情况我们看到这张表我们第一时间用语句进行操作:
select * from Score where stu_score>90;
可想而知,这是有多么简单,假设你并不知道数据库中张三的成绩是90或者数据量相当庞大呢?作为一个数据库管理员,我们就要用别的方式迅速找出所需要的数据。
方式一:分步查找:最简单的一种方式,也是最容易想到的操作“
select stu_score from Score where stu_name='张三';//得出张三的成绩 select *from Score where stu_score>90;
与采用自连接的方式相比,这种方法需要对中间结果进行人工干预,显然不利于程序中的自动处理操作。
方式二:自连接方式:
select * from Score as a,Score as b where a.stu_name='张三' and a.stu_score<b.stu_score;
+--------+----------+-----------+--------+----------+-----------+ | stu_id | stu_name | stu_score | stu_id | stu_name | stu_score | +--------+----------+-----------+--------+----------+-----------+ | 1 | 张三 | 90 | 4 | 赵六 | 95 | | 1 | 张三 | 90 | 5 | 侯七 | 100 | +--------+----------+-----------+--------+----------+-----------+
如果不想把张三的信息打印出来我们可以调整SQL语句为:
select b.* from Stu_score as a,Stu_score as b where a.stu_name='张三' and a.stu_score<b.stu_score;
+--------+----------+-----------+ | stu_id | stu_name | stu_score | +--------+----------+-----------+ | 4 | 赵六 | 95 | | 5 | 侯七 | 100 | +--------+----------+-----------+
注意:别名 a,b虽然名称不同,但是同一个表,定义别名的目的是更方便在自身进行删选。
执行select通过(中间表)所得到的b.*,,就是最终结果。
七、子查询
子查询也是常用的一种方式,就是在select里嵌套select。还是上面的例子,我们用子查询实现如下:
select * from Stu_score where stu_score>(select stu_score from Stu_score where stu_name='张三');
+--------+----------+-----------+ | stu_id | stu_name | stu_score | +--------+----------+-----------+ | 4 | 赵六 | 95 | | 5 | 侯七 | 100 | +--------+----------+-----------+
八、内置函数
1、字符串函数
1、查看字符的ascii码值ascii(str),str是空串时返回0
mysql> select ascii('a'); +------------+ | ascii('a') | +------------+ | 97 | +------------+
2、查看ascii码值对应的字符char(数字)
mysql> select char(97); +----------+ | char(97) | +----------+ | a | +----------+
3、拼接字符串concat(str1,str2…)
select concat(12,34,'ab'); +--------------------+ | concat(12,34,'ab') | +--------------------+ | 1234ab | +--------------------+
4、包含字符个数length(str)
mysql> select length('abc'); +---------------+ | length('abc') | +---------------+ | 3 | +---------------+
5、截取字符串
- left(str,len)返回字符串str的左端len个字符
- right(str,len)返回字符串str的右端len个字符
- substring(str,pos,len)返回字符串str的位置pos起len个字符
mysql> select substring('abc123',2,3); +-------------------------+ | substring('abc123',2,3) | +-------------------------+ | bc1 | +-------------------------+
6、去除空格
- ltrim(str)返回删除了左空格的字符串str
- rtrim(str)返回删除了右空格的字符串str
- trim([方向 remstr from str)返回从某侧删除remstr后的字符串str,方向词包括both、leading、trailing,表示两侧、左、右
mysql> select trim(' bar '); +------------------+ | trim(' bar ') | +------------------+ | bar | +------------------+
mysql> select trim(leading 'x' FROM 'xxxbarxxx'); +------------------------------------+ | trim(leading 'x' FROM 'xxxbarxxx') | +------------------------------------+ | barxxx | +------------------------------------+
mysql> select trim(trailing 'x' FROM 'xxxbarxxx'); +-------------------------------------+ | trim(trailing 'x' FROM 'xxxbarxxx') | +-------------------------------------+ | xxxbar | +-------------------------------------+
mysql> select trim(both 'x' FROM 'xxxbarxxx'); +---------------------------------+ | trim(both 'x' FROM 'xxxbarxxx') | +---------------------------------+ | bar | +---------------------------------+
7、替换字符串replace(str,from_str,to_str)
mysql> select replace('abc123','123','def'); +-------------------------------+ | replace('abc123','123','def') | +-------------------------------+ | abcdef | +-------------------------------+
8、大小写转换,函数如下
- lower(str)
- upper(str)
mysql> select lower('aBcD'); +---------------+ | lower('aBcD') | +---------------+ | abcd | +---------------+
2、数学函数
1、求绝对值abs(n)
mysql> select abs(-32); +----------+ | abs(-32) | +----------+ | 32 | +----------+
2、求m除以n的余数mod(m,n),同运算符%
mysql> select mod(10,3); +-----------+ | mod(10,3) | +-----------+ | 1 | +-----------+ mysql> select 10%3; +------+ | 10%3 | +------+ | 1 | +------+
3、floor(n),表示不大于n的最大整数
mysql> select floor(2.3); +------------+ | floor(2.3) | +------------+ | 2 | +------------+
4、ceiling(n),表示不小于n的最大整数
mysql> select ceiling(2.3); +--------------+ | ceiling(2.3) | +--------------+ | 3 | +--------------+
5、求四舍五入值round(n,d),n表示原数,d表示小数位置,默认为0
mysql> select round(1.6); +------------+ | round(1.6) | +------------+ | 2 | +------------+
6、求x的y次幂pow(x,y)
mysql> select pow(2,3); +----------+ | pow(2,3) | +----------+ | 8 | +----------+
7、随机数rand(),值为0-1.0的浮点数
mysql> select rand(); +--------------------+ | rand() | +--------------------+ | 0.0713081630610937 | +--------------------+
3、日期时间函数
- 获取子值,语法如下
- year(date)返回date的年份(范围在1000到9999)
- month(date)返回date中的月份数值
- day(date)返回date中的日期数值
- hour(time)返回time的小时数(范围是0到23)
- minute(time)返回time的分钟数(范围是0到59)
- second(time)返回time的秒数(范围是0到59)
mysql> select year('2019-7-11'); +-------------------+ | year('2019-7-11') | +-------------------+ | 2019 | +-------------------+
- 日期计算,使用+-运算符,数字后面的关键字为year、month、day、hour、minute、second
mysql> select '2019-7-11'+interval 1 day; +----------------------------+ | '2019-7-11'+interval 1 day | +----------------------------+ | 2019-07-12 | +----------------------------+
日期格式化date_format(date,format),format参数可用的值如下
-
获取年%Y,返回4位的整数
* 获取年%y,返回2位的整数
* 获取月%m,值为1-12的整数
-
获取日%d,返回整数
* 获取时%H,值为0-23的整数
* 获取时%h,值为1-12的整数
* 获取分%i,值为0-59的整数
* 获取秒%s,值为0-59的整数
九、事物
- 当一个业务逻辑需要多个sql语句完成时,如果其中某条sql语句出错,则希望整个操作都退回
- 使用事务可以完成退回的功能,保证业务逻辑的正确性
- 事务四大特性(简称ACID)
- 原子性(Atomicity):事务中的全部操作在数据库中是不可分割的,要么全部完成,要么均不执行
- 一致性(Consistency):几个并行执行的事务,其执行结果必须与按某一顺序串行执行的结果相一致
- 隔离性(Isolation):事务的执行不受其他事务的干扰,事务执行的中间结果对其他事务必须是透明的
- 持久性(Durability):对于任意已提交事务,系统必须保证该事务对数据库的改变不被丢失,即使数据库出现故障
- 要求:表的类型必须是innodb或bdb类型,才可以对此表使用事务
- 查看表的创建语句
show create table Stu_score;
- 修改表的类型:alter table ‘表名’ engine=innodb;
alter table Stu_score engine=innodb;
- 事务语句
开启begin;
提交commit;
回滚rollback;
在begin;后面写我们要操作的SQL语句组合也就是我们所谓的要执行的事物,但是这个时候写好的SQL语句就算我们回车之后,也不会执行,知道commit;执行之后才会被执行到数据库中,rollback回滚是我们begin;之后发现我们写错了,或者不想执行了,都可以在commit;之前回滚到上一次commit;的状态,很像版本控制器SVN和GIT一样
python数据库-数据库的介绍及安装(47)

一、数据库的介绍
数据库(Database)是存储与管理数据的软件系统,就像一个存入数据的物流仓库。每个数据库都有一个或多个不同的API接口用于创建,访问,管理,搜索和复制所保存的数据。我们也可以将数据存储在文件中,但是在文件中读写数据速度相对较慢,所以我们使用关系型数据库管理系统(RDBMS)来存储和管理的大数据量。
二、常见数据库技术品牌、服务与架构
发展了这么多年市场上出现了许多的数据库系统,最强的个人认为是Oracle,当然还有许多如:DB2、Microsoft SQL Server、MySQL、SyBase等,下图列出常见数据库技术品牌、服务与架构。
三、数据库的分类
数据库通常分为层次式数据库、网络式数据库和关系式数据库三种,而不同的数据库是按不同的数据结构来联系和组织的。而在当今的互联网中,最常见的数据库模型主要是两种,即关系型数据库和非关系型数据库。
关系型数据库代表:Oracle、MySql、SQL Server
非关系型数据库代表:Mongodb、Redis
四、E-R模型
- 当前物理的数据库都是按照E-R模型进行设计的
- E表示entry,实体
- R表示relationship,关系
- 一个实体转换为数据库中的一个表
- 关系描述两个实体之间的对应规则,包括
- 一对一
- 一对多
- 多对多
- 关系转换为数据库表中的一个列 *在关系型数据库中一行就是一个对象
五、三大范式
1、 什么是范式
要设计规范化的数据库,就要求我们根据数据库设计范式――也就是数据库设计的规范原则来做。范式可以指导我们更好地设计数据库的表结构,减少冗余的数据,借此可以提高数据库的存储效率,数据完整性和可扩展性。
设计关系数据库时,遵从不同的规范要求,设计出合理的关系型数据库,这些不同的规范要求被称为不同的范式,各种范式呈递次规范,越高的范式数据库冗余越小。目前关系数据库有六种范式:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴德斯科范式(BCNF)、第四范式(4NF)和第五范式(5NF,又称完美范式)。满足最低要求的范式是第一范式(1NF)。在第一范式的基础上进一步满足更多规范要求的称为第二范式(2NF),其余范式以次类推。一般说来,数据库只需满足第三范式(3NF)就行了。
2、三大范式
第一范式(1NF)
所谓第一范式(1NF)是指在关系模型中,对列添加的一个规范要求,所有的列都应该是原子性的,即数据库表的每一列都是不可分割的原子数据项,而不能是集合,数组,记录等非原子数据项。即实体中的某个属性有多个值时,必须拆分为不同的属性。在符合第一范式(1NF)表中的每个域值只能是实体的一个属性或一个属性的一部分。简而言之,第一范式就是无重复的域。
例如:表1-1中,其中”工程地址”列还可以细分为省份,城市等。在国外,更多的程序把”姓名”列也分成2列,即”姓”和“名”。
虽然第一范式要求各列要保存原子性,不能再分,但是这种要求和我们的需求是相关联的,如上表中我们对”工程地址”没有省份,城市这样方面的查询和应用需求,则不需拆分,”姓名”列也是同样如此。
表1-1 原始表
工程号 |
工程名称 |
工程地址 |
员工编号 |
员工名称 |
薪资待遇 |
职务 |
P001 |
港珠澳大桥 |
广东珠海 |
E0001 |
Jack |
6000/月 |
工人 |
P001 |
港珠澳大桥 |
广东珠海 |
E0002 |
Join |
7800/月 |
工人 |
P001 |
港珠澳大桥 |
广东珠海 |
E0003 |
Apple |
8000/月 |
高级技工 |
P002 |
南海航天 |
海南三亚 |
E0001 |
Jack |
5000/月 |
工人 |
第二范式(2NF)
在1NF的基础上,非Key属性必须完全依赖于主键。第二范式(2NF)是在第一范式(1NF)的基础上建立起来的,即满足第二范式(2NF)必须先满足第一范式(1NF)。第二范式(2NF)要求数据库表中的每个实例或记录必须可以被唯一地区分。选取一个能区分每个实体的属性或属性组,作为实体的唯一标识。
第二范式(2NF)要求实体的属性完全依赖于主关键字。所谓完全依赖是指不能存在仅依赖主关键字一部分的属性,如果存在,那么这个属性和主关键字的这一部分应该分离出来形成一个新的实体,新实体与原实体之间是一对多的关系。为实现区分通常需要为表加上一个列,以存储各个实例的唯一标识。简而言之,第二范式就是在第一范式的基础上属性完全依赖于主键。
例如:表1-1中,一个表描述了工程信息,员工信息等。这样就造成了大量数据的重复。按照第二范式,我们可以将表1-1拆分成表1-2和表1-3:
表1-2 工程信息表
工程编号 |
工程名称 |
工程地址 |
P001 |
港珠澳大桥 |
广东珠海 |
P002 |
南海航天 |
海南三亚 |
表1-3 员工信息表
员工编号 |
员工姓名 |
职务 |
薪资水平 |
E0001 |
Jack |
工人 |
3000/月 |
E0002 |
Join |
工人 |
3000/月 |
E0003 |
Apple |
高级技工 |
6000/月 |
这样,表1-1就变成了两张表,每个表只描述一件事,清晰明了。
第三范式(3NF)
第三范式是在第二范式基础上,更进一层,第三范式的目标就是确保表中各列与主键列直接相关,而不是间接相关。即各列与主键列都是一种直接依赖关系,则满足第三范式。
第三范式要求各列与主键列直接相关,我们可以这样理解,假设张三是李四的兵,王五则是张三的兵,这时王五是不是李四的兵呢?从这个关系中我们可以看出,王五也是李四的兵,因为王五依赖于张三,而张三是李四的兵,所以王五也是。这中间就存在一种间接依赖的关系而非我们第三范式中强调的直接依赖。
现在我们来看看在第二范式的讲解中,我们将表1-1拆分成了两张表。这两个表是否符合第三范式呢。在员工信息表中包含:”员工编号”、”员工名称”、”职务”、”薪资水平”,而我们知道,薪资水平是有职务决定,这里”薪资水平”通过”职务”与员工相关,则不符合第三范式。我们需要将员工信息表进一步拆分,如下:
员工信息表:员工编号,员工名称,职务
职务表:职务编号,职务名称,薪资水平
现在我们已经了解了数据库规范化设计的三大范式,下面我们再来看看对表1-1优化后的数据表:
员工信息表(Employee)
员工编号 |
员工姓名 |
职务编号 |
E0001 |
Jack |
1 |
E0002 |
Join |
1 |
E0003 |
Apple |
2 |
工程信息表(ProjectInfo)
工程编号 |
工程名称 |
工程地址 |
P001 |
港珠澳大桥 |
广东珠海 |
P002 |
南海航天 |
海南三亚 |
职务表(Duty)
职务编号 |
职务名称 |
工资待遇 |
1 |
工人 |
3000/月 |
2 |
高级技工 |
6000/月 |
工程参与人员记录表(Project_ Employee_info)
编号 |
工程编号 |
人员编号 |
1 |
P001 |
E0001 |
2 |
P001 |
E0002 |
3 |
P002 |
E0003 |
通过对比我们发现,表多了,关系复杂了,查询数据变的麻烦了,编程中的难度也提高了,但是各个表中内容更清晰了,重复的数据少了,更新和维护变的更容易了,哪么如何平衡这种矛盾呢?
4、范式与效率
在我们设计数据库时,设计人员、客户、开发人员通常对数据库的设计有一定的矛盾,客户更喜欢方便,清晰的结果,开发人员也希望数据库关系比较简单,降低开发难度,而设计人员则需要应用三大范式对数据库进行严格规范化,减少数据冗余,提高数据库可维护性和扩展性。由此可以看出,为了满足三大范式,我们数据库设计将会与客户、开发人员产生分歧,所以在实际的数据库设计中,我们不能一味的追求规范化,既要考虑三大范式,减少数据冗余和各种数据库操作异常,又要充分考虑到数据库的性能问题,允许适当的数据库冗余。
六、MySQL介绍
MySQL所使用的 SQL 语言是用于访问数据库的最常用标准化语言。MySQL 软件采用了双授权政策,分为社区版和商业版,由于其体积小、速度快、总体拥有成本低,尤其是开放源码这一特点,一般中小型网站的开发都选择 MySQL 作为网站数据库。
MySQL官网:https://www.mysql.com/
MySQL下载:https://www.mysql.com/downloads/
MySQL以前一直是开源免费的,被Oracle收购后有些变化:以前的版本都是免费的,社区版按GPL协议开源免费,商业版提供更加丰富的功能,但收费。
社区版的下载地址:https://dev.mysql.com/downloads/ (免费)
企业版的下载地址:https://www.mysql.com/downloads/(收费)
七、MySQL的卸载
为什么还没安装就要先学习卸载呢?因为很多同学的电脑可能以前装过MySQL,在再次装之前如果以前的没有卸载或者卸载的不干净,都会影响MySQL的再次安装,所以在安装之前我们先确保我们电脑上没有上次安装的残留。
1、mac系统下删除
通过命令行删除
sudo rm /usr/local/mysql sudo rm -rf /usr/local/mysql* sudo rm -rf /Library/StartupItems/MySQLCOM sudo rm -rf /Library/PreferencePanes/My* rm -rf ~/Library/PreferencePanes/My* sudo rm -rf /Library/Receipts/mysql* sudo rm -rf /Library/Receipts/MySQL* sudo rm -rf /var/db/receipts/com.mysql.*
2、ubuntu系统下删除
sudo apt-get remove mysql-* dpkg -l |grep ^rc|awk '{print $2}' |sudo xargs dpkg -P
八、MySQL的安装
1、mac 系统安装
在安装的时候会让你填写一个root用户的密码,用来做MySQL的登录使用
安装好了之后再你电脑的系统偏好设置面板中能找到MySQL
点击进入到MySQL面板
安装好了之后MySQL服务默认是开启的,不用的时候通过stop可以关闭,此时mac系统我们的MySQL就安装好了。
2、ubuntu下安装MySQL
2.1、安装
sudo apt-get install mysql-server mysql-client
2.2、启动
service mysql start
2.3、停止
service mysql stop
2.4、重启
service mysql restart
3、windows下就不带着大家安装了,我想大家都会
九、图形工具(Navicat)链接数据库
如果一直使用命令行去操作数据库,这样对于数据的整理,表结构的观察是很不方便,所以我们在安装好了MySQL服务之后,我们都会使用图形界面工具去编辑和处理数据库,图形界面工具有很多,这里给大家介绍一个很好用的数据库连接工具Navicat,这个工具正版是收费的,当然网上有很多破解版和破解攻略,我相信大家都会的。
Navicat官网:https://www.navicat.com.cn/
Navicat产品下载:https://www.navicat.com.cn/products
下载安装好了之后,打开如下图
我们操作连接按钮,连接到我们要操作的数据库
这个时候我们创建一个表验证一下我们的链接是成功的,通过Navicat可以操作数据库,
使用终端登录MySQL验证
OK、到此我们连接成功,就可以对数据库进程曾删改查等操作了,具体操作请看下一篇