1.简介
数据库,简而言之可视为电子化的文件柜——存储电子文件的处所,用户可以对文件中的数据进行新增、查询、更新、删除等操作。
所谓“数据库”是以一定方式储存在一起、能予多个用户共享、具有尽可能小的冗余度、与应用程序彼此独立的数据集合。
2.数据库管理系统
主条目:数据库管理系统
数据库管理系统(英语:Database Management System,简称DBMS)是为管理数据库而设计的电脑软件系统,一般具有存储、截取、安全保障、备份等基础功能。数据库管理系统可以依据它所支持的数据库模型来作分类,例如关系式、XML;或依据所支持的计算机类型来作分类,例如服务器群集、移动电话;或依据所用查询语言来作分类,例如SQL、XQuery;或依据性能冲量重点来作分类,例如最大规模、最高运行速度;亦或其他的分类方式。不论使用哪种分类方式,一些DBMS能够跨类别,例如,同时支持多种查询语言。
3.类型
- 对象型
- 层次型《轻量级》
- 网状型
- 关系型
3.1.关系数据库(SQL)
- MySQL
-
- MariaDB(MySQL的代替品,英文维基百科从MySQL转向MariaDB)
- [Percona Server](https://baike.baidu.com/item/Percona Server)(MySQL的代替品·)
- PostgreSQL
- [Microsoft Access](https://baike.baidu.com/item/Microsoft Access)
- [Microsoft SQL Server](https://baike.baidu.com/item/Microsoft SQL Server)
- Google Fusion Tables
- FileMaker
- Oracle数据库
- Sybase
- dBASE
- Clipper
- FoxPro
3.2.非关系型数据库(NoSQL)
主条目:NoSQL
键值(key-value)数据库
- [Apache Cassandra](https://baike.baidu.com/item/Apache Cassandra)(为Facebook所使用):高度可扩展
- Dynamo
- LevelDB(Google)
- Kafka
- Redis
3.3.数据库模型
- 对象模型
- 层次模型(轻量级数据访问协议)xml
- 网状模型(大型数据储存)
网状数据库是采用网状原理和方法,以网状数据模型为基础建立的数据库。一般是指由网状数据库管理系统产生的网状数据库系统。网状数据模型是以记录类型为结点的网络结构,即一个结点可以有一个或多个下级结点,也可以有一个或多个上级结点,两个结点之间甚至可以有多种联系,例如“教师”与“课程”两个记录类型,可以有“任课”和“辅导”两种联系,称之为复合链。两个记录类型之间的值可以是多对多的联系,例如一门课程被多个学生修读,一个学生选修多门课程。
3.3.1.关系模型
关系型数据库,是指采用了关系模型来组织数据的数据库,其以行和列的形式存储数据,以便于用户理解,关系型数据库这一系列的行和列被称为表,一组表组成了数据库。用户通过查询来检索数据库中的数据,而查询是一个用于限定数据库中某些区域的执行代码。关系模型可以简单理解为二维表格模型,而一个关系型数据库就是由二维表及其之间的关系组成的一个数据组织。
3.3.2.设计原则
在进行关系型数据库的设计过程中,要遵循以下几个原则,借此可以提高数据库的存储效率、数据完整性和可扩展性。
1 .命名规范化
在概念模型设计中,对于出现的实体、属性及相关表的结构要统一。例如在数据库设计中,指定学生Student,专指本科生,相关的属性有:学号、姓名、性别、出生年月等,及每个属性的类型、长度、取值范围等都要进行确定,这样就能保证在命名时不会出现同名异义或异名同义、属性特征及结构冲突等问题。
2 .数据的一致性和完整性
在关系型数据库中可以采用域完整性、实体完整性和参照完整性等约束条件来满足其数据的一致性和完整性,用check、default、null、主键和外键约束来实现。
3.数据冗余
数据库中的数据应尽可能地减少冗余,这就意味着重复数据应该减少到最少。例如:若一个部门职员的电话存储在不同的表中,假设该职员的电话号码发生变化时,冗余数据的存在就要求对多个表进行更新操作,若某个表不幸被忽略了,那么就会造成数据不一致的情况。所以在数据库设计中一定要尽可能存在少地冗余。
4.范式理论
在关系数据库设计时,一般是通过设计满足某一范式来获得一个好的数据库模式,通常认为3NF在性能、扩展性和数据完整性方面达到了最好的平衡,因此,一般数据库设计要求达到3NF,消除数据依赖中不合理的部分,最终实现使一个关系仅描述一个实体或者实体间一种联系的目的。
5.性能
3.4.数据库范式
第一范式
一范式就是属性不可分割。属性是什么?就是表中的字段。
不可分割的意思就按字面理解就是最小单位,不能再分成更小单位了。
这个字段只能是一个值,不能被拆分成多个字段,否则的话,它就是可分割的,就不符合一范式。
不过能不能分割并没有绝对的答案,看需求,也就是看你的设计目标而定。
举例:
学生信息组成学生信息表,有姓名、年龄、性别、学号等信息组成。
姓名不可拆分吧?所以可以作为该表的一个字段。
但我要说这个表要在国外使用呢?外家姓和名要分开,都有特别的意义,所以姓名字段是可拆分的,分为姓字段和名字段。
简单来说,一范式是关系数据库的基础,但字段是否真的不可拆分,根据你的设计目标而定。–>
第二范式
二范式就是要有主键,要求其他字段都依赖于全部主键。
为什么要有主键?没有主键就没有唯一性,没有唯一性在集合中就定位不到这行记录,所以要主键。
其他字段为什么要依赖于主键?因为不依赖于主键,就找不到他们。更重要的是,其他字段组成的这行记录和主键表示的是同一个东西,而主键是唯一的,它们只需要依赖于主键,也就成了唯一的。
如果有同学不理解依赖这个词,可以勉强用“相关”这个词代替,也就是说其他字段必须和它们的主键相关。因为不相关的东西不应该放在一行记录里。
举例:
学生信息组成学生表,姓名可以做主键么?
不能!因为同名的话,就不唯一了,所以需要学号这样的唯一编码才行。
那么其他字段依赖于主键是什么意思?
就是“张三”同学的年龄和性别等字段,不能存储别人的年龄性别,必须是他自己的,因为张三的学号信息就决定了,这行记录归张三所有,不能给无关人员使用。-
第三范式
三范式就是要消除传递依赖,方便理解,可以看做是“消除冗余”。
消除冗余应该比较好理解一些,就是各种信息只在一个地方存储,不出现在多张表中。
比如说大学分了很多系(中文系、英语系、计算机系……),这个系别管理表信息有以下字段组成:
系编号,系主任,系简介,系架构。
那么再回到学生信息表,张三同学的年龄、性别、学号都有了,我能不能把他的系编号,系主任、系简介也一起存着?
如果你问三范式,当然不行,因为三范式不同意。
因为系编号,系主任、系简介已经存在系别管理表中,你再存入学生信息表,就是冗余了。
三范式中说的传递依赖,就出现了。
这个时候学生信息表中,系主任信息是不是依赖于系编号了?而这个表的主键可是学号啊!
所以按照三范式,处理这个问题的时候,学生表就只能增加一个系编号字段。
3.5.SQL创建关系数据库
sql语句不区分大小写
3.5.1.建库
show databases; #查看当前数据库服务器下有什么数据库
use 库名; #切换当前数据库
show tables; #查看当前数据库下有哪些表
select * from CHARACTER_SETS;
create database mydb; -- 创建库的命令
3.5.2.建表
create table student(
code int,
name varchar(50),
sex char(2),
phone char(18),
enter_time date
);
desc emp; --展示表结构
3.6.数据操作
DDL(创建,移除数据库对象例表),DML(添删改数据),DQL(查询)
3.6.1.添加
--添加一条学生数据
insert into student(code,name,sex,phone,enter_time) values(1111,'张三','男','19887678767',now());
--如果给学生表中每列均赋值则可以不用写字段列表,只写值列表
insert into student values(1111,'张三','男','19887678767',now());
创建范例表
(导入数据)
-- 删除数据表
DROP TABLE IF EXISTS emp ;
DROP TABLE IF EXISTS dept;
DROP TABLE IF EXISTS bonus;
DROP TABLE IF EXISTS salgrade;
-- 创建数据表
-- 员工表
CREATE TABLE dept (
deptno INT PRIMARY KEY, -- 部门编号
dname VARCHAR(14) , -- 部门名称
loc VARCHAR(13) -- 部门地址
) ;
-- 员工表
CREATE TABLE emp (
empno INT PRIMARY KEY, -- 员工编号
ename VARCHAR(10), -- 员工姓名
job VARCHAR(9), -- 员工职务
mgr INT, -- 经理的员工编号
hiredate DATE, -- 入职日期
sal DOUBLE, -- 员工收入
comm DOUBLE, -- 奖金
deptno INT -- 部门表外键,代表当前员工属于哪个部门
);
alter table emp add foreign key (deptno) references dept (deptno);
-- 奖金表
CREATE TABLE bonus (
ename VARCHAR(10) , -- 员工姓名
job VARCHAR(9) , -- 员工职务
sal DOUBLE, -- 员工收入
comm DOUBLE -- 员工奖金
) ;
-- 工资等级
CREATE TABLE salgrade (
grade INT, -- 工资等级
losal DOUBLE, -- 当前等级最低收入
hisal DOUBLE -- 当前等级最高收入
);
-- 插入测试数据 —— dept
INSERT INTO dept VALUES (10,'ACCOUNTING','NEW YORK');
INSERT INTO dept VALUES (20,'RESEARCH','DALLAS');
INSERT INTO dept VALUES (30,'SALES','CHICAGO');
INSERT INTO dept VALUES (40,'OPERATIONS','BOSTON');
-- 插入测试数据 —— emp
INSERT INTO emp VALUES (7369,'SMITH','CLERK',7902,str_to_date('17-12-1980','%d-%m-%Y'),800,NULL,20);
INSERT INTO emp VALUES (7499,'ALLEN','SALESMAN',7698,str_to_date('20-2-1981','%d-%m-%Y'),1600,300,30);
INSERT INTO emp VALUES (7521,'WARD','SALESMAN',7698,str_to_date('22-2-1981','%d-%m-%Y'),1250,500,30);
INSERT INTO emp VALUES (7566,'JONES','MANAGER',7839,str_to_date('2-4-1981','%d-%m-%Y'),2975,NULL,20);
INSERT INTO emp VALUES (7654,'MARTIN','SALESMAN',7698,str_to_date('28-9-1981','%d-%m-%Y'),1250,1400,30);
INSERT INTO emp VALUES (7698,'BLAKE','MANAGER',7839,str_to_date('1-5-1981','%d-%m-%Y'),2850,NULL,30);
INSERT INTO emp VALUES (7782,'CLARK','MANAGER',7839,str_to_date('9-6-1981','%d-%m-%Y'),2450,NULL,10);
INSERT INTO emp VALUES (7788,'SCOTT','ANALYST',7566,str_to_date('19-04-1987','%d-%m-%Y'),3000,NULL,20);
INSERT INTO emp VALUES (7839,'KING','PRESIDENT',NULL,str_to_date('17-11-1981','%d-%m-%Y'),5000,NULL,10);
INSERT INTO emp VALUES (7844,'TURNER','SALESMAN',7698,str_to_date('8-9-1981','%d-%m-%Y'),1500,0,30);
INSERT INTO emp VALUES (7876,'ADAMS','CLERK',7788,str_to_date('23-05-1987','%d-%m-%Y'),1100,NULL,20);
INSERT INTO emp VALUES (7900,'JAMES','CLERK',7698,str_to_date('3-12-1981','%d-%m-%Y'),950,NULL,30);
INSERT INTO emp VALUES (7902,'FORD','ANALYST',7566,str_to_date('3-12-1981','%d-%m-%Y'),3000,NULL,20);
INSERT INTO emp VALUES (7934,'MILLER','CLERK',7782,str_to_date('23-1-1982','%d-%m-%Y'),1300,NULL,10);
-- 插入测试数据 —— salgrade
INSERT INTO salgrade VALUES (1,700,1200);
INSERT INTO salgrade VALUES (2,1201,1400);
INSERT INTO salgrade VALUES (3,1401,2000);
INSERT INTO salgrade VALUES (4,2001,3000);
INSERT INTO salgrade VALUES (5,3001,9999);
-- 事务提交
COMMIT;
-- 查询数据
SELECT * FROM emp;
SELECT * FROM dept;
SELECT * FROM bonus;
SELECT * FROM salgrade;
3.6.2.删除
delete from student; -- 删除所有数据
-- 根据条件删除
delete from student where code = 1114;
delete from student where name = '李四';
3.6.3.修改
-- 给所有员工工资涨10%
update emp set sal=sal*1.1;
-- 修改特定学生的信息
update student set name='张三',sex='女' where code=1112;
3.6.4.查询
- 查询所有列
select * from emp;
select ename,job,mgr,hiredate,sal,comm,deptno,empno from emp;
- 投影查询
select ename,job,hiredate,sal,comm from emp; -- 投影查询
- 查询中的运算
--查询每个员工的姓名及年收入
select ename 员工姓名,sal * 12 年收入 from emp;
--查询每个员工的姓名及年(收入+奖金)
select ename 员工姓名,(sal + COALESCE(comm,0)) * 12 年收入 from emp;
select null + 12 from dual; – 测试用例 空值和任何数据加减乘除都为空。
- 条件查询
-- 查询好厨师
select * from emp where job='CLERK' and sal > 1000;
-- 查询工资在1000-4000之间的厨师或销售员的姓名及年收入
select ename 姓名,sal * 12 年收入 from emp where sal between 1000 and 4000 and (job = 'SALESMAN' or job = 'CLERK');
select ename 姓名,sal * 12 年收入 from emp where sal between 1000 and 4000 and job in ('SALESMAN','CLERK');
-- 查询姓S的员工信息 模糊查询
select * from emp where ename like 'S%'
-- 假设每页数据有2条,我想查第二页员工数据
select * from emp limit 0,2;
-- 查询第1,3,6,9条员工数据
set @rownum=0; --在mysql数据库设置一个变量的语法
select * from (select @rownum:=@rownum+1 序号,ename,job from emp)t where t.序号 in (1,3,6,9); --涉及子查询:先在子查询中创建一个带序号的视图(视图要有名t),在针对视图查询
-- 查询工资大于98年后入职的任意一个员工工资的员工数据
select * from emp where sal > any(select sal from emp where hiredate > str_to_date('1998-0-1','%Y-%m-%d'))
-- 查询81到87年的员工数据
select * from emp where hiredate between str_to_date('1981-0-0','%Y-%m-%d') and str_to_date('1987-0-0','%Y-%m-%d');
- 统计查询
--五个统计函数
--求最大值
select max(sal) from emp;
select CONCAT('最大值 ',max(sal),'人命币') from emp;
--求最小值
select CONCAT('最小值',min(sal),'人命币') from emp;
--求平均值
select CONCAT('平均值',avg(sal),'人命币') from emp;
--求和
select CONCAT('总共',sum(sal),'人命币') from emp;
--求总记录数
select CONCAT('总共',count(*),'条') from emp;
- 分组统计
-- 查询各部门员工的平均年收入
select deptno , avg((sal + COALESCE(comm,0)) * 12) 平均年收入 from emp group by deptno
-- 查询不同职务的员工的总年收入
select job , sum((sal + COALESCE(comm,0)) * 12) 平均年收入 from emp group by job
-- 查询各部门下各职务员工的平均年收入
select deptno,job,avg((sal + COALESCE(comm,0)) * 12) 平均年收入 from emp group by deptno,job
-- 查询10,20部门下各职务员工的平均年收入
select deptno,job,avg((sal + COALESCE(comm,0)) * 12) 平均年收入 from emp group by deptno,job having deptno in (10,20) -- 不推荐
select deptno,job,avg((sal + COALESCE(comm,0)) * 12) 平均年收入 from emp where deptno in (10,20) group by deptno,job -- 推荐
- 排序
select * from emp order by sal + COALESCE(comm,0) desc;
select * from emp order by sal + COALESCE(comm,0),hiredate ;
desc 为降序,默认为升序 arsc .
当升序有多个升序约束时,应综合考虑。
- 连接查询
--等值连接 86标准
-- 查询员工基本信息及其所在部门的信息
select * from emp,dept where emp.deptno = dept.deptno
select empno,ename,job,sal,comm,hiredate,deptno,dname,loc from emp,dept where emp.deptno = dept.deptno; -- 这个查询是错误的,因为连接后投影列必须加上表名做前缀,表示列属于哪个表
select emp.empno,emp.ename,emp.job,emp.sal,emp.comm,emp.hiredate,emp.deptno,dept.dname,dept.loc from emp,dept where emp.deptno = dept.deptno; -- 这个是正确的
select e.empno,e.ename,e.job,e.sal,e.comm,e.hiredate,e.deptno,d.dname,d.loc from emp e,dept d where emp.deptno = dept.deptno -- 一般表名使用别名
--等值连接 92标准
select e.ename,e.job,e,sal,e.comm,d.dname,d.loc from emp e join dept d on e.deptno = d.deptno;
-- 非等值连接
-- 查询员工姓名,收入信息及其工资的等级
select e.ename,e.sal,s.grade from emp e inner join salgrade s on e.sal between s.losal and s.hisal;
-- 查询员工姓名,收入,经理姓名
select e.empno,e.sal,e.ename 员工姓名,e1.ename 经理姓名 from emp e join emp e1 on e.mgr = e1.empno;
-- 查询员工姓名,收入信息及其工资的等级及员工的经理的姓名
select e.ename,e.sal,e1.ename,s.grade from emp e join salgrade s join emp e1 on (e.sal between s.losal and s.hisal) and e.mgr = e1.empno -- 了解
select e.ename,e.sal,e1.ename,s.grade from emp e join salgrade s on e.sal between s.losal and s.hisal join emp e1 on e.mgr = e1.empno -- 推荐
-- 外连接
-- 右外连接
-- (内连接)查询员工姓名,收入信息及其工资的所有等级(即便工资等级没有员工也要列出)
select e.ename,e.sal,s.grade from emp e right outer join salgrade s on e.sal between s.losal and s.hisal;
-- 左外连接
select e.ename,e.sal,s.grade from salgrade s left join emp e on e.sal between s.losal and s.hisal;
-- 全外连接
select e.ename,e.sal,s.grade from salgrade s full outer join emp e on e.sal between s.losal and s.hisal; -- 这个会报错,因为mysql不支持全外连接
右外连接 , 右边列表不受限制 。
左外连接,省略left , 左边列表不受限制。