一、数据库简介
传统记录数据的缺点:
不易保存、备份困难、查找不便
文件:
①使用简单,例如python中的open可以打开文件,用read/write对文件进行读写,close关闭文件
②对于数据容量较大的数据,不能够很好的满足,而且性能较差
③不易扩展
数据库:
①持久化存储
②读写速度极高
③保证数据的有效性
④对程序支持性非常好,容易扩展
数据库就是一种特殊的文件,存储着需要的数据
存放路径:**/var/lib/mysql** (需要切换到root用户才能打开)
关系型数据库的元素:
①数据行(记录)
②数据列(字段)--能够唯一标记某个字段,称为主键
③数据表(数据行的集合)
④数据库(数据表的集合)
RDBMS:关系数据库管理系统
所谓的关系型数据库RDBMS,是建立在关系模型基础上的数据库,借助于集合代数等数学概念和方法来处理数据库中的数据
关系型数据库的主要产品:
- oracle:在以前的大型项目中使用,银行,电信等项目
- mysql:web时代使用最广泛的关系型数据库
- ms sql server:在微软的项目中使用
- sqlite:轻量级数据库,主要应用在移动平台
C/S架构,通过SQL语句来操作数据库:
二、SQL与MySQL简介
(1)SQL简介:
SQL是结构化查询语言,是一种用来操作RDBMS的数据库语言,当前关系型数据库都支持使用SQL语言进行操作,也就是说可以通过 SQL 操作 oracle,sql server,mysql,sqlite 等等所有的关系型的数据库
- SQL语句主要分为:
- DQL:数据查询语言,用于对数据进行查询,如select
- DML:数据操作语言,对数据进行增加、修改、删除,如insert、udpate、delete
- TPL:事务处理语言,对事务进行处理,包括begin transaction、commit、rollback
- DCL:数据控制语言,进行授权与权限回收,如grant、revoke
- DDL:数据定义语言,进行数据库、表的管理等,如create、drop
- CCL:指针控制语言,通过控制指针完成表的操作,如declare cursor
- 对于web程序员来讲,重点是数据的crud(增删改查),必须熟练编写DQL、DML,能够编写DDL完成数据库、表的操作,其它语言如TPL、DCL、CCL了解即可
- SQL 是一门特殊的语言,专门用来操作关系数据库
- 不区分大小写
(2)MySQL简介:
MySQL是一个关系型数据库管理系统,由瑞典MySQL AB公司开发,后来被Sun公司收购,Sun公司后来又被Oracle公司收购,目前属于Oracle旗下产品
使用C和C++编写,并使用了多种编译器进行测试,保证源代码的可移植性
支持多种操作系统,如Linux、Windows、AIX、FreeBSD、HP-UX、MacOS、NovellNetware、OpenBSD、OS/2 Wrap、Solaris等
为多种编程语言提供了API,如C、C++、Python、Java、Perl、PHP、Eiffel、Ruby等
支持多线程,充分利用CPU资源
优化的SQL查询算法,有效地提高查询速度
提供多语言支持,常见的编码如GB2312、BIG5、UTF8
提供TCP/IP、ODBC和JDBC等多种数据库连接途径
提供用于管理、检查、优化数据库操作的管理工具
大型的数据库。可以处理拥有上千万条记录的大型数据库
支持多种存储引擎
MySQL 软件采用了双授权政策,它分为社区版和商业版,由于其体积小、速度快、总体拥有成本低,尤其是开放源码这一特点,一般中小型网站的开发都选择MySQL作为网站数据库
MySQL使用标准的SQL数据语言形式
Mysql是可以定制的,采用了GPL协议,你可以修改源码来开发自己的Mysql系统
在线DDL更改功能
复制全局事务标识
复制无崩溃从机
复制多线程从机
开源 免费 不要钱 使用范围广,跨平台支持性好,提供了多种语言调用的 API
是学习数据库开发的首选
(3)MySQL安装:
安装服务器端
sudo apt-get install mysql-server
服务器用于接收客户端的请求、执行sql语句、管理数据库
服务器端一般以服务方式管理,名称为mysql
启动服务
sudo service mysql start
查看进程中是否存在mysql服务
ps ajx|grep mysql
停止服务
sudo service mysql stop
重启服务
sudo service mysql restart
配置
- 配置文件目录为/etc/mysql/mysql.cnf
- 进入conf.d目录,打开mysql.cnf,发现并没有配置
- 进入mysql.conf.d目录,打开mysql.cnf,可以看到配置
bind-address表示服务器绑定的ip,默认为127.0.0.1
port表示端口,默认为3306
datadir表示数据库目录,默认为/var/lib/mysql
general_log_file表示普通日志,默认为/var/log/mysql/mysql.log
log_error表示错误日志,默认为/var/log/mysql/error.log
命令行客户端
在终端运行如下命令,按提示填写信息
sudo apt-get install mysql-client
详细连接的命令可以查看帮助文档
mysql --help
最基本的连接命令如下,输入后回车(-u 用户名 -p密码)
mysql -u root -pmysql
查看版本号
mysql> select version();
按ctrl+d或输入 quit 或 exit 退出
图形化界面客户端Navicat
下载或拷贝到Linux,然后解压
tar -zxvf navicat120_premium_cs_x64.tar.gz
进入解压后的目录,运行start文件
./start_navicat
选择不安装wine(wine的作用是使得Windows的软件可以在Linux中运行)
选择试用
打开后如果出现乱码,双击打开start_navicat文件,然后修改字符集
找到 export LANG=en_US.UTF-8
改成 export LANG=zh_CN.UTF-8
打开软件,选择连接---输入密码---测试连接
左侧的数据库图表双击才可以启动
新建数据库
三、数据类型和约束
在表中为了更加准确的存储数据,保证数据的正确有效,可以在创建表的时候,为表添加一些强制性的验证,包括数据字段的类型、约束
(一)数据类型
够用就行,尽量使用取值范围小的,而不用大的,这样可以更多的节省存储空间
常用数据类型如下:
- 整数:int,bit
- 小数:decimal
- 字符串:varchar,char
- 日期时间: date, time, datetime
- 枚举类型(enum):枚举中的下标从1开始,可以直接用下标赋值
特别说明的类型如下:
- decimal表示浮点数,如decimal(5,2)表示共存5位数,小数占2位
- char表示固定长度的字符串,如char(3),如果填充'ab'时会补一个空格为
'ab '
- varchar表示可变长度的字符串,如varchar(3),填充'ab'时就会存储'ab'
- 字符串text表示存储大文本,当字符大于4000时推荐使用
- 对于图片、音频、视频等文件,不存储在数据库中,而是上传到某个服务器上,然后在表中存储这个文件的保存路径
更全的数据类型可以参考http://blog.csdn.net/anxpp/article/details/51284106
数值类型(常用)
类型
字节大小
有符号范围(Signed)
无符号范围(Unsigned)
TINYINT
1
-128 ~ 127
0 ~ 255
SMALLINT
2
-32768 ~ 32767
0 ~ 65535
MEDIUMINT
3
-8388608 ~ 8388607
0 ~ 16777215
INT/INTEGER
4
-2147483648 ~2147483647
0 ~ 4294967295
BIGINT
8
-9223372036854775808 ~ 9223372036854775807
0 ~ 18446744073709551615
字符串
类型
字节大小
示例
CHAR
0-255
类型:char(3) 输入 'ab', 实际存储为'ab ', 输入'abcd' 实际存储为 'abc'
VARCHAR
0-255
类型:varchar(3) 输 'ab',实际存储为'ab', 输入'abcd',实际存储为'abc'
TEXT
0-65535
大文本
日期时间类型
类型
字节大小
示例
DATE
4
'2020-01-01'
TIME
3
'12:29:59'
DATETIME
8
'2020-01-01 12:29:59'
YEAR
1
'2017'
TIMESTAMP
4
'1970-01-01 00:00:01' UTC ~ '2038-01-01 00:00:01' UTC
(二)约束
- 主键primary key:物理上存储的顺序
- 非空not null:此字段不允许填写空值
- 惟一unique:此字段的值不允许重复
- 默认default:当不填写此值时会使用默认值,如果填写时以填写为准
- 外键foreign key:对关系字段进行约束,当为关系字段填写值时,会到关联的表中查询此值是否存在,如果存在则填写成功,如果不存在则填写失败并抛出异常
- 说明:虽然外键约束可以保证数据的有效性,但是在进行数据的crud(增加、修改、删除、查询)时,都会降低数据库的性能,所以不推荐使用,那么数据的有效性怎么保证呢?答:可以在逻辑层进行控制
四、数据库操作
链接数据库
SQL语句最后要有分号结尾
mysql -uroot -pmysql -uroot -pmysql
退出数据库
exit 或 quit 或 ctrl+D
显示数据库版本
select version();
显示时间
select now();
查看所有数据库
show databases;
创建数据库
create database 数据库名 charset=utf8;
create database python02 charset=utf8;
create database python01;
查看创建数据库的语句
show create database ...
show create database python02;
删除数据库
drop database 数据库名;
drop database python01;
drop database `python-04`; 使用tab键上方的`来分开字符串
查看当前使用的数据库
select database();
使用数据库
use 数据库名
use python02;
查看当前数据库中所有表
show tables;
创建表
auto_increment 自动增长
not_null 不能为空
primary key 主键
default 默认值
create table 数据表名字 (字段 类型 约束,字段 类型 约束)
create table test1(id int,name varchar(30));
创建class表(id,name)
mysql> create table class
-> (id int primary key not null auto_increment,
-> name varchar(30) not null);
查看表的结构
desc 数据表的名字;
desc test1;
创建student表
mysql> create table students(
-> id int unsigned primary key not null auto_increment,
-> name varchar(30) not null,
-> age tinyint unsigned,
-> high decimal(5,2),
-> gender enum('男','女','无性别','保密') default '保密',
-> cls_id int unsigned
-> );
查看创建表的语句
show create table 表名字;
show create table students;
修改表-添加字段
alter table 表名 add 列名 类型;
alter table students add birthday datetime;
修改表-修改字段
alter table 表名 modify 列名 类型及约束;
alter table students modify birthday date;
修改表-修改字段
alter table 表名 change 原名 新名 类型及约束;
alter table students change birthday birth date default "2000-01-01";
修改表-删除字段
alter table 表名 drop 列名;
alter table students drop high;
删除表
drop table test1;
五、增删改查
curd的解释: 代表创建(Create)、更新(Update)、读取(Retrieve)和删除(Delete)
增加
全列插入
insert into 表名 values(...);
主键字段可以用0 null default来占位
insert into classes values(0,"大数据");
部分插入
insert into 表名(列名...) values(值...);
insert into students(name, age) values('xq',14);
多行插入
insert into students(name, age) values('大乔',14),('孙尚香',15);
修改
update 表名 set 列1=值1,列2=值2... where 条件;
不写where就会全改
修改多个字段用逗号隔开
update students set cls_id=1 where id=2;
删除
物理删除
delete from 表名 where 条件;
逻辑删除
用一个字段表示这条信息是否已经不能再使用了
alter table students add is_delete bit default 0;
update students set is_delete=1 where id=6;
查询
查询所有列
select * from students;
指定条件查询
select * from students where age=14;
查询指定列
字段的顺序和查询时写的顺序一致
select name,gender from students where age=14;
可以使用as起别名
select name as "姓名",gender as "年龄" from students where age=14;
六、查询
(1)基本查询
查询所有字段
select * from students;
查询指定字段
select name,age from students;
使用as给字段起别名
select name as 姓名,age as 年龄 from students;
select students.age,students.gender from students;
使用as给表起别名
select s.age,s.gender from students as s;
清除重复行
distinct 字段
select distinct gender from students;
(2)条件查询
比较运算符
大于18岁
select * from students where age>18;
等于18岁 只有一个=
select * from students where age=18;
不等于18岁 不推荐<>,用**!=**
select * from students where age!=18;
逻辑运算符
and 18-28岁的学生信息
select * from students where age>18 and age<28;
or 18以上或身高超过180的
select * from students where age>18 or height>=180;
not 不在18以上的女性
select * from students where not age>18 and gender='女';
(3)模糊查询
like
%表示任意多个任意字符
_表示一个任意字符
查询以小开头的名字
select name from students where name like "小%";
查询有两个字的名字
select name from students where name like "__";
查询至少有两个字的名字
select name from students where name like "__%";
rlike 正则
查询以周开头,伦结尾的名字
select name from students where name rlike "^周.*伦$";
(4)范围查询
select name,age from students where age in (12,18,34);
select name,age from students where age not in (12,18,34);
select name,age from students where age between 18 and 34;
select name,age from students where age not between 18 and 34;
判断空
select * from students where height is null;
select * from students where height is not null;
(5)排序
order by 字段
asc从小到大排列,即升序。默认按照列值从小到大排列(asc)
desc从大到小排序,即降序。
查询年龄18-34之间男性,按年龄从小到大排序
select * from students where age>18 and age<34 and gender="男" order by age;
查询年龄18-34之间女性,按身高从大到小排序
select * from students where age>=18 and age<=34 and gender=2 order by height desc;
查询所有女性,按身高从大到小排序,身高相同则按照年龄从小到大排序
select * from students where gender=2 order by height desc,age;
(6)聚合函数
总数
count
女性总数
select count(*) as 总数 from students where gender=2;
最大值
max
select max(age) from students;
最小值
min
select min(age) from students;
求和
sum
所有人年龄总和
select sum(age) from students;
平均值
avg
计算平均年龄
select avg(age) from students;
select sum(age)/count(*) from students;
round(原数,保留小数位数) 四舍五入
select round(avg(age),2) from students;
(7)分组
group by
按照性别分组,查询每种性别的人数
select gender,count(*) from students group by gender;
看每一组包括哪些人
select gender,group_concat(name) from students group by gender;
计算男性的人数,where写在group前面
select gender,count(*) from students where gender=1 group by gender;
select gender,group_concat(name," ",age) from students where gender=1 group by gender;
having 对分组进行条件判断
查询平均年龄超过30的性别
select gender,group_concat(name) from students group by gender having avg(age)>30;
(8)分页
限制查询结果的个数
查询前两个结果
select * from students limit 2;
limit start,count
从第7个开始查5个
select * from students limit 6,5;
分页
limit (第n页-1)*每页个数,每页个数
但是不能用SQL直接计算,以后在程序中实现
limit必须写在最后
(9)连接查询
当查询结果的列来源于多张表时,需要将多张表连接成一个大的数据集,再选择合适的列返回
mysql支持三种类型的连接查询,分别为:
- 内连接查询:查询的结果为两个表匹配到的数据
- 右连接查询:查询的结果为两个表匹配到的数据,右表特有的数据,对于左表中不存在的数据使用null填充
- 左连接查询:查询的结果为两个表匹配到的数据,左表特有的数据,对于右表中不存在的数据使用null填充
inner join ... on
select ... from 表A inner join 表B;
select * from students inner join classes;
查询 有能够对应班级的学生以及班级信息
select * from students inner join classes on students.cls_id=classes.id;
按照要求显示姓名、班级
select students.*,classes.name from students inner join classes on students.cls_id=classes.id;
给数据表起名字
select s.name,c.name from students as s inner join classes as c on s.cls_id=c.id;
在以上的查询中,将班级姓名显示在第1列
select c.name,s.* from students as s inner join classes as c on s.cls_id=c.id;
查询 有能够对应班级的学生以及班级信息, 按照班级进行排序
select c.name,s.* from students as s inner join classes as c on s.cls_id=c.id order by c.name;
当时同一个班级的时候,按照学生的id进行从小到大排序
select c.name,s.* from students as s inner join classes as c on s.cls_id=c.id order by c.name,s.id;
left join
查询每位学生对应的班级信息,没有对应班级的学生也会显示出来,没有的班级名显示NULL
select * from students as s left join classes as c on s.cls_id=c.id;
查询没有对应班级信息的学生
select * from students as s left join classes as c on s.cls_id=c.id having c.id is null;
select * from students as s left join classes as c on s.cls_id=c.id where c.id is null;
right join on
将数据表名字互换位置,用left join完成
(10)自关联
CREATE TABLE `areas` (
`aid` int(11) NOT NULL,
`atitle` varchar(20) DEFAULT NULL,
`pid` int(11) DEFAULT NULL,
PRIMARY KEY (`aid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
从sql文件中导入数据
先cd到当前目录,再打开MySQL,切换数据库
source area.sql;
查找某个省所有市区
select * from areas where pid=2
自关联
select city.aid,province.atitle,city.atitle from areas as province inner join areas as city where province.aid=city.pid;
查询省的名称为“山西省”的所有城市
select city.* from areas as city inner join areas as province on city.pid=province.aid where province.atitle='山西省';
(11)子查询
查询最高的男生信息
select * from students where height=(select max(height) from students);
查出高于平均身高的学生信息
select * from students where height>(select avg(height) from students);
七、数据库设计
关系型数据库建议在E-R模型的基础上,我们需要根据产品经理的设计策划,抽取出来模型与关系,制定出表结构,这是项目开始的第一步。
在开发中有很多设计数据库的软件,常用的如power designer,db desinger等,这些软件可以直观的看到实体及实体间的关系。
设计数据库,可能是由专门的数据库设计人员完成,也可能是由开发组成员完成,一般是项目经理带领组员来完成。
范式
经过研究和对使用中问题的总结,对于设计数据库提出了一些规范,这些规范被称为范式(Normal Form)
目前有迹可寻的共有8种范式,一般需要遵守3范式即可
◆ 第一范式(1NF):强调的是列的原子性,即列不能够再分成其他几列。
考虑这样一个表:【联系人】(姓名,性别,电话) 如果在实际场景中,一个联系人有家庭电话和公司电话,那么这种表结构设计就没有达到 1NF。要符合 1NF 我们只需把列(电话)拆分,即:【联系人】(姓名,性别,家庭电话,公司电话)。1NF 很好辨别,但是 2NF 和 3NF 就容易搞混淆。
◆ 第二范式(2NF):首先是 1NF,另外包含两部分内容,一是表必须有一个主键;二是没有包含在主键中的列必须完全依赖于主键,而不能只依赖于主键的一部分。
考虑一个订单明细表:【OrderDetail】(OrderID,ProductID,UnitPrice,Discount,Quantity,ProductName)。 因为我们知道在一个订单中可以订购多种产品,所以单单一个 OrderID 是不足以成为主键的,主键应该是(OrderID,ProductID)。显而易见 Discount(折扣),Quantity(数量)完全依赖(取决)于主键(OderID,ProductID),而 UnitPrice,ProductName 只依赖于 ProductID。所以 OrderDetail 表不符合 2NF。不符合 2NF 的设计容易产生冗余数据。
可以把【OrderDetail】表拆分为【OrderDetail】(OrderID,ProductID,Discount,Quantity)和【Product】(ProductID,UnitPrice,ProductName)来消除原订单表中UnitPrice,ProductName多次重复的情况。
◆ 第三范式(3NF):首先是 2NF,另外非主键列必须直接依赖于主键,不能存在传递依赖。即不能存在:非主键列 A 依赖于非主键列 B,非主键列 B 依赖于主键的情况。
考虑一个订单表【Order】(OrderID,OrderDate,CustomerID,CustomerName,CustomerAddr,CustomerCity)主键是(OrderID)。 其中 OrderDate,CustomerID,CustomerName,CustomerAddr,CustomerCity 等非主键列都完全依赖于主键(OrderID),所以符合 2NF。不过问题是 CustomerName,CustomerAddr,CustomerCity 直接依赖的是 CustomerID(非主键列),而不是直接依赖于主键,它是通过传递才依赖于主键,所以不符合 3NF。 通过拆分【Order】为【Order】(OrderID,OrderDate,CustomerID)和【Customer】(CustomerID,CustomerName,CustomerAddr,CustomerCity)从而达到 3NF。 *第二范式(2NF)和第三范式(3NF)的概念很容易混淆,区分它们的关键点在于,2NF:非主键列是否完全依赖于主键,还是依赖于主键的一部分;3NF:非主键列是直接依赖于主键,还是直接依赖于非主键列。
不遵循1NF
不遵循2NF:主键有两个,但洗发水只依赖PID
不遵循3NF:存在传递依赖
最终表
E-R模型
- E表示entry,实体,设计实体就像定义一个类一样,指定从哪些方面描述对象,一个实体转换为数据库中的一个表
- R表示relationship,关系,关系描述两个实体之间的对应规则,关系的类型包括包括一对一、一对多、多对多
- 关系也是一种数据,需要通过一个字段存储在表中
- 实体A对实体B为1对1,则在表A或表B中创建一个字段,存储另一个表的主键值
- 实体A对实体B为1对多:在表B中创建一个字段,存储表A的主键值
- 实体A对实体B为多对多:新建一张表C,这个表只有两个字段,一个用于存储A的主键值,一个用于存储B的主键值
逻辑删除
- 对于重要数据,并不希望物理删除,一旦删除,数据无法找回
- 删除方案:设置isDelete的列,类型为bit,表示逻辑删除,默认值为0
- 对于非重要数据,可以进行物理删除
- 数据的重要性,要根据实际开发决定
八、SQL演练
-- 创建 "京东" 数据库
create database jing_dong charset=utf8;
-- 使用 "京东" 数据库
use jing_dong;
-- 创建一个商品goods数据表
create table goods(
id int unsigned primary key auto_increment not null,
name varchar(150) not null,
cate_name varchar(40) not null,
brand_name varchar(40) not null,
price decimal(10,3) not null default 0,
is_show bit not null default 1,
is_saleoff bit not null default 0
);
-- 向goods表中插入数据
insert into goods values(0,'r510vc 15.6英寸笔记本','笔记本','华硕','3399',default,default);
insert into goods values(0,'y400n 14.0英寸笔记本电脑','笔记本','联想','4999',default,default);
insert into goods values(0,'g150th 15.6英寸游戏本','游戏本','雷神','8499',default,default);
insert into goods values(0,'x550cc 15.6英寸笔记本','笔记本','华硕','2799',default,default);
insert into goods values(0,'x240 超极本','超级本','联想','4880',default,default);
insert into goods values(0,'u330p 13.3英寸超极本','超级本','联想','4299',default,default);
insert into goods values(0,'svp13226scb 触控超极本','超级本','索尼','7999',default,default);
insert into goods values(0,'ipad mini 7.9英寸平板电脑','平板电脑','苹果','1998',default,default);
insert into goods values(0,'ipad air 9.7英寸平板电脑','平板电脑','苹果','3388',default,default);
insert into goods values(0,'ipad mini 配备 retina 显示屏','平板电脑','苹果','2788',default,default);
insert into goods values(0,'ideacentre c340 20英寸一体电脑 ','台式机','联想','3499',default,default);
insert into goods values(0,'vostro 3800-r1206 台式电脑','台式机','戴尔','2899',default,default);
insert into goods values(0,'imac me086ch/a 21.5英寸一体电脑','台式机','苹果','9188',default,default);
insert into goods values(0,'at7-7414lp 台式电脑 linux )','台式机','宏碁','3699',default,default);
insert into goods values(0,'z220sff f4f06pa工作站','服务器/工作站','惠普','4288',default,default);
insert into goods values(0,'poweredge ii服务器','服务器/工作站','戴尔','5388',default,default);
insert into goods values(0,'mac pro专业级台式电脑','服务器/工作站','苹果','28888',default,default);
insert into goods values(0,'hmz-t3w 头戴显示设备','笔记本配件','索尼','6999',default,default);
insert into goods values(0,'商务双肩背包','笔记本配件','索尼','99',default,default);
insert into goods values(0,'x3250 m4机架式服务器','服务器/工作站','ibm','6888',default,default);
insert into goods values(0,'商务双肩背包','笔记本配件','索尼','99',default,default);
查询类型cate_name为 '超极本' 的商品名称、价格
select name,price from goods where cate_name = "超级本";
显示商品的种类
select distinct cate_name from goods;
select cate_name from goods group by cate_name;
select cate_name,group_concat(name) from goods group by cate_name;
求所有电脑产品的平均价格,并且保留两位小数
select round(avg(price),2) as "平均价格" from goods;
显示每种商品的平均价格
select cate_name,round(avg(price),2) from goods group by cate_name;
查询每种类型的商品中 最贵、最便宜、平均价、数量
select cate_name,max(price) as "最贵",min(price) as "最便宜",avg(price) as "平均",count(*) from goods group by cate_name;
查询所有价格大于平均价格的商品,并且按价格降序排序
select * from goods where price > (select avg(price) from goods) order by price desc;
查询每种类型中最贵的电脑信息
-- 创建商品分类表 create table if not exists goods_cates( id int unsigned primary key auto_increment, name varchar(40) not null );