java及idea及mysql使用排坑

java排坑

这是多久没写java程序了吧,打开idea一看,连建个java程序都没底了……。

idea相关操作及问题

第一个java程序

新建工程里,选java,不使用模板,取个名就建好工程框架了,然后在src文件夹里,alt+insert键,新建一个java class,给类取名(一般的项目结构为:项目 包 类,包就相当于c++里的命名空间),如com.example.hello,前面的是包名,最后一个是类名,这样就不用单独创建包了。
然后 ,写一段代码,点run就可以运行和调试了。

除了sout 、psvm外常用的idea 快速补全快捷键

代码提示

alt+/

选择性生成类中常用的方法

alt+insert

alt+enter,快速实现新定义的方法*,得把光标放到变量名或方法名上。

这两个都可以实现快速编写set,get函数。

类名自动补全

ctrl+alt+space

循环实现

fori,这个得在函数里面输入才管用。

foreach循环实现

iter,这个也得是在函数里实现。

删除光标所在行

ctrl+d

查询方法/接口定义的地方,(前往当前光标所在的方法的父类的方法 / 接口定义)

ctrl+单击;//返回,就是alt+left

选中大括号里的全部内容

alt+shift+up

批量修改同一变量名

shift+alt+r;//修改完后按enter.

注释与取消注释

ctrl+/或ctrl+shfit+/;//取消就是再按一遍

从当前行后面插入新的一行,这样就不用再按end跳到代码最后

ctrl+shfit+enter

解决Idea新建Directrly后不能再建立java类的问题?

需要右击新建的目录,往下,选择mark direct as resource root.

java如何输入指数问题?

在C、C++和java中,e代表“10的次幂”。
这个要区别于:在科学工程领域,“e”代表自然对数的基数,约等于2.718(Java中的Math.E给出了更精确的double型的值)。

      double expFloat = Math.pow(2,3);//2^3表式
        System.out.println(expFloat);

        double expDouble = 47e47d; // 'd' is optional
        double expDouble2 = 47e47; // Automatically double
        System.out.println(expDouble);//可以这么表达,但不直观,还是输出的定义时的形式。

使用java命令,编译java程序出现:“错误: 编码 GBK 的不可映射字符 (0x80)”错误??

Windows系统默认的编码格式是GBK编码,而我们常使用的IDEA、eclipse基本上都是UTF-8,所以出现了编码 GBK 的不可映射字符 (0x80)错误。
解决:javac -encoding utf-8 findDigial.java
编译成功后,执行java findDigial. 注意:执行时不用加.class.

mysql数据库相关

安装mysql

  1. 官网下载安装包,本电脑是10年前的电脑,因此版本不要太高,5.7版本就可以。
  2. 配置系统环境变量,将mysqa/bin配置到path路径下
  3. 配置安装根目录下的my.ini,加入以下代码
# These are commonly set, remove the # and set as required.
 basedir = D:\mysql-5.7.9-winx64
 datadir = D:\mysql-5.7.9-winx64\data#启动服务后,会自动生
 port = 3306
# server_id = .....

skip-grant-tables #跳过密码登录
  1. 启动mysql服务

启动管理员下的powershell,输入“mysqld -install”;成功的话会出现 Service successfully installed.

执行mysqld --initialize-insecure --user=mysql进行初始化数据文件,也就是上面所说的会自动生成data文件。

执行net start mysql启动mysql服务,

*登录数据库**执行mysql -u root -p登录进去,先输入之前的密码,没有的话,直接回车。

update mysql.user set authentication_ string=password(‘123456’) where user=‘root’ and Host = ‘localhost’;(注意有分号结尾),再执行flush privileges;进行刷新(注意有分号结尾)。

执行exit;退出服务

执行操作前的准备,mysql的数据类型

varchar(最长255)
可变长度的字符串,比较智能,节省空间。会根据实际的数据长度动态分配空间。

​ 优点:节省空间
​ 缺点:需要动态分配空间,速度慢。

char(最长255)
定长字符串,不管实际的数据长度是多少,分配固定长度的空间去存储数据。
使用不恰当的时候,可能会导致空间的浪费。

​ 优点:不需要动态分配空间,速度快。
​ 缺点:使用不当可能会导致空间的浪费。

varchar和char我们应该怎么选择?
​ 性别字段你选什么?因为性别是固定长度的字符串,所以选择char。
​ 姓名字段你选什么?每一个人的名字长度不同,所以选择varchar。

int(最长11)

​ 数字中的整数型。等同于java的int。

bigint
数字中的长整型。等同于java中的long。

float
单精度浮点型数据

double
双精度浮点型数据

date
短日期类型,日期,就是datetime中的date部分

datetime
长日期类型, 时间日期型,格式是YYYY-mm-dd HH:ii:ss,占6个字节

clob
字符大对象
最多可以存储4G的字符串。
比如:存储一篇文章,存储一个说明。
超过255个字符的都要采用CLOB字符大对象来存储。
Character Large OBject:CLOB

blob
二进制大对象
Binary Large OBject
专门用来存储图片、声音、视频等流媒体数据。
往BLOB类型的字段上插入数据的时候,例如插入一个图片、视频等,
你需要使用IO流才行。

命令行操作

依上面,登录进mysql后,就进入了mysql命令行,可以执行与sqlyog一样的操作。

数据库相关操作

sql脚本的编写

多行注释或行中间注释:/* */
单行添加注释:#,直到该行结束

脚本例子
drop database if EXISTS emp_manage;
create database emp_manage CHARACTER SET utf8 COLLATE utf8_general_ci;#COLLATE时校验、核对的意思。此代码指定数据库的校验规则,ci是case #insensitive的缩写,意思是大小写不敏感;相对的是cs,即case sensitive,大小写敏感。
use emp_manage;
	create table tbl_user(
	id int(10) primary key auto_increment,
	name varchar(50),
	age int(20),
	salary double(10,2),
	bir timeStamp
);

insert into tbl_user(name,age,salary,bir) 
values('张三',18,5000,Now());
insert into tbl_user(name,age,salary,bir) 
values('李四',20,10000,Now());


数据库和表的导入导出
数据导出?  
    注意:在windows的dos命令窗口中:  
        mysqldump bjpowernode>D:\bjpowernode.sql -uroot -p123456  
    可以导出指定的表吗?  
        mysqldump bjpowernode emp>D:\bjpowernode.sql -uroot -p123456  

数据导入?  
    注意:需要先登录到mysql数据库服务器上。  
    然后创建数据库:create database bjpowernode;  
    使用数据库:use bjpowernode  
    然后初始化数据库:source D:\bjpowernode.sql  

show databases; 查看mysql中有哪些数据库

use test; 表示正在使用一个名字叫做test的数据库。

create database db01; 创建数据库

drop database db01; 删除一个数据库

show tables; 查看某个数据库下有哪些表

select version(); 查看mysql数据库的版本号

select database(); 查看当前使用的是哪个数据库

\c 用来终止一条命令的输入。

表相关操作 增删改

快速查看表结构

desc 表名称;

放到语句里用: information_schema.COLUMNS,如输入select * from information_schema.COLUMNS where TABLE_SCHEMA = ‘库名称’ and TABLE_NAME = ‘表名称’;

创建一个表
学号、姓名、年龄、性别、邮箱地址
    create table t_student(
        no int,
        name varchar(32),
        sex char(1),
        age int(3),
        email varchar(255)
    );
删除一个表
    drop table t_student; // 当这张表不存在的时候会报错!
    // 如果这张表存在的话,删除
    drop table if exists t_student;
修改表结构
alter table tablename add column-name datatype [primary key/default/not null/...] after 'columnX'  
例如:
alter table student add grade varchar(2) not null after age;
删除表结构中的一列
alter table tablename drop column;
修改表结构中的列
alter table 表名 modify 列名 数据类型//修改列数据类型
alter table student change gender sex varchar(10);//修改列名

表数据的 增删改

插入数据
  • insert into 表名(字段名1,字段名2,字段名3…) values(值1,值2,值3);
insert into t_student(no,name,sex,age,email)
    values(1,'zhangsan','m',20,'zhangsan@123.com');
insert into t_student(email,name,sex,age,no)
    values('lisi@123.com','lisi','f',20,2);
insert into t_student(no) values(3);
//省略字段名
insert into t_student values(2, 'lisi', 'f', 20, 'lisi@123.com');
  • insert语句但凡是执行成功了,那么必然会多一条记录。没有给其它字段指定值的话,默认值是NULL。
    一次插入多条数据

  • insert into t_user(字段名1,字段名2) values(),(),(),();

insert into t_user(id,name,birth,create_time) values
        (1,'zs','1980-10-11',now()), 
        (2,'lisi','1981-10-11',now()),
        (3,'wangwu','1982-10-11',now());
问题:
插入数据时,输错,命令行一直出’>???

再输入一个’就可以恢复了。

删除数据
delete
  • delete from 表名 where 条件;

  • 没有条件,整张表的数据会全部删除!

  • delete from t_user where id = 2;

truncate

truncate table dept_bak; (这种操作属于DDL操作。)

修改数据update
  • update 表名 set 字段名1=值1,字段名2=值2,字段名3=值3… where 条件;

  • 没有条件限制会导致所有数据全部更新。

update t_user set name = 'jack', birth = '2000-10-11' where id = 2;

update t_user set name = 'jack', birth = '2000-10-11', create_time = now() where id = 2;

安装sqlyog后桌面多了一个Ie图标,并且删除不了,如何处理?

在注册表路径下计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Desktop\NameSpace,右击查找ie图标的名称,然后找到注册表项,删除即可,要删除项而不是值

sqlyog使用(主要看在命令下创建数据库的效果)

创建数据库

直接在左边栏root@local上右击,选择新建数据库,基本字符集选utf8,数据库排列规则:选utf8_general_ci.如果是java,选基字符集选择utf8mb4,这是Java8默认字符集,及utf8mb4_unicode_ci,

工具->历史记录里,可以看到sql相应创建过程的sql语句。

创建表

鼠标移到数据库下面的表上,右击,新建表。

表名称根据阿里巴巴命名规则:字母小写,单词之间用下划线_分割
引擎选择InnoDB
字符集可以默认也可以选择和您创建数据库的字符集匹配的
列名命名规则和表名称命名规则一致:字母小写,单词之间用下划线_分割

  1. 更小的更好
    选择你认为不会超越范围的最小类型,更小的数据类型通常更快,因为它们占用更少的磁盘、内存 和cpu缓存,并且处理时需要的cpu周期更少。

  2. 简单就好
    简单的数据类型操作通常更快,例如整形的操作比字符串更快,因为字符串有字符集和校对规则 (排序规则)比整形更复杂。

  3. 尽量避免null
    如果字段可以不存储null值,尽量把字段设置成not null。因为可为null的列使得索引、索引统计和 值比较都更复杂,可为null的列会使用更多的存储空间,在mysql里也需要特殊处理。

创建完表之后就要插入数据

这个就有些麻烦。一个个插入的话,就不如用命令方式。

其他功能不再一一演示,重点还是命令行的方式实现建表,插入等操作。

数据库,数据查询(这个在sqlyog与命令行是一样的)

基本的条件查询,where

select 字段1,字段2,字段3… from 表名 where 条件;
条件有:

  1. = 等于

  2. <>或!= 不等于

  3. is null 为 null(is not null 不为空)

  4. and 并且 or 或者

    select  * from emp where sal > 2500 and (deptno = 10 or deptno = 20);
    andor同时出现,and优先级较高。如果想让or先执行,需要加“小括号”,以后在开发中,如果不确定优先级,就加小括号就行了。
    
  5. in 包含,相当于多个 or (not in 不在这个范围中)

查询工作岗位是MANAGER和SALESMAN的员工?
select empno,ename,job from emp where job = 'MANAGER' or job = 'SALESMAN';
select empno,ename,job from emp where job in('MANAGER', 'SALESMAN');
  1. not 可以取非,主要用在 is 或 in 中
is null
is not null
in
not in
  1. like

称为模糊查询,支持%或下划线匹配

  • %匹配任意多个字符
  • 下划线:任意一个字符。
  • (%是一个特殊的符号,_ 也是一个特殊符号)
找出名字中含有O的?
    mysql> select ename from emp where ename like '%O%';

找出名字中有“_”的?
    select name from t_student where name like '%_%'; //这样不行。

    mysql> select name from t_student where name like '%\_%'; // \转义字符。
  1. distinct
  • 把查询结果去除重复记录【distinct】

  • distinct只能出现在所有字段的最前方。

  • distinct出现在job,deptno两个字段之前,表示两个字段联合起来去重。

  • select distinct job from emp;

  • select distinct job,deptno from emp;

排序

select ename,sal from emp order by sal; // 默认是升序!!!

指定降序:desc 如:select ename,sal from emp order by sal desc;

分页
  • limit作用:将查询结果集的一部分取出来。通常使用在分页查询当中。
    • 完整用法:limit startIndex, length startIndex是起始下标,length是长度。
    • 起始下标从0开始。
    • 缺省用法:limit 5; 这是取前5.
      取出工资排名在[3-5]名的员工?
    select 
        ename,sal
    from
        emp
    order by
        sal desc
    limit
        2, 3;
2表示起始位置从下标2开始,就是第三条记录。
3表示长度。
  • 每页显示3条记录

    • 第1页:limit 0,3 [0 1 2]
    • 第2页:limit 3,3 [3 4 5]
    • 第3页:limit 6,3 [6 7 8]
  • 每页显示pageSize条记录

    • 第pageNo页:limit (pageNo - 1) * pageSize , pageSize

函数

  • 单行处理函数的特点:一个输入对应一个输出。

  • 和单行处理函数相对的是:多行处理函数。(多行处理函数特点:多个输入,对应1个输出!)

    lower 转换小写 upper 转换大写
    select lower(ename) as ename from emp
    concat函数进行字符串的拼接
    select concat(empno,ename) from emp;
    length 取长度
    select length(ename) enamelength from emp;
    trim 去空格
    SELECT TRIM('?' FROM '???bar???');--删除指定的首尾字符 ?
    rand() 生成随机数
    select round(rand()*100,0) from emp; // 100以内的随机数
    
  • 分组函数
  • 多行处理函数的特点:输入多行,最终输出一行。

    • count 计数
    • sum 求和
    • avg 平均值
    • max 最大值
    • min 最小值
  • 注意:
    分组函数在使用的时候必须先进行分组,然后才能用。
    如果你没有对数据进行分组,整张表默认为一组。

找出最高工资?
        mysql> select max(sal) from emp;
找出最低工资?
        mysql> select min(sal) from emp;
计算工资和:
        mysql> select sum(sal) from emp;
计算平均工资:
        mysql> select avg(sal) from emp;
计算员工数量?
        mysql> select count(ename) from emp;

分组函数在使用的时候需要注意哪些?

  • 第一点:分组函数自动忽略NULL,你不需要提前对NULL进行处理。

  • 第二点:分组函数中count(*)和count(具体字段)有什么区别?*

    • count(具体字段):表示统计该字段下所有不为NULL的元素的总数。

    • count(*):统计表当中的总行数。(只要有一行数据count则++)

因为每一行记录不可能都为NULL,一行数据中有一列不为NULL,则这行数据就是有效的。

  • 第三点:分组函数不能够直接使用在where子句中。
    找出比最低工资高的员工信息。
    select ename,sal from emp where sal > min(sal);–表面上没问题,运行一下?
    ERROR 1111 (HY000): Invalid use of group function

    分组查询 grop by

  • 在一条select语句当中,如果有group by语句的话,select后面只能跟:参加分组的字段,以及分组函数。其它的一律不能跟。**
    找出每个工作岗位的工资和?

实现思路:按照工作岗位分组,然后对工资求和。
 select job,sum(sal) from emp group by job;
以上这个语句的执行顺序?
先从emp表中查询数据。根据job字段进行分组。然后对每一组的数据进行sum(sal)
联合分组

找出“每个部门,不同工作岗位”的最高薪资?

--技巧:两个字段联合成1个字段看。(两个字段联合分组)
select deptno, job, max(sal) from emp group by deptno, job;
分组过滤 having
  • having不能单独使用,having不能代替where,having必须和group by联合使用。
    找出每个部门平均薪资,要求显示平均薪资高于2500的。
select deptno,avg(sal) from emp group by deptno having avg(sal) > 2500;
  • where和having,优先选择where,where实在完成不了了,再选择having。

总结sql执行顺序

  • select --> from --> where --> group by --> having --> order by -->
    以上关键字只能按照这个顺序来,不能颠倒。

  • 执行顺序?
    1. from --> where --> group by --> having --> select --> order by

  1. 从某张表中查询数据,
  2. 先经过where条件筛选出有价值的数据。
  3. 对这些有价值的数据进行分组。
  4. 分组之后可以使用having继续筛选。
  5. select查询出来。
  6. 最后排序输出!
找出每个岗位的平均薪资,要求显示平均薪资大于1500的,除MANAGER岗位之外,  
要求按照平均薪资降序排。  
select   job, avg(sal) as avgsal  from  emp  where  
job <> 'MANAGER'  group by  job  having  avg(sal) > 1500  
order by  avgsal desc;  

连表查询

  • 从一张表中单独查询,称为单表查询

  • emp表和dept表联合起来查询数据,从emp表中取员工名字,从dept表中取部门名字。

  • 这种跨表查询,多张表联合起来查询数据,被称为连接查询。

select ename,dname from emp, dept;

  • 当两张表进行连接查询,没有任何条件限制的时候,最终查询结果条数,是两张表条数的乘积,这种现象被称为:笛卡尔积现象。

内连接之等值连接

查询每个员工所在部门名称,显示员工名和部门名?

  • emp e和dept d表进行连接。条件是:e.deptno = d.deptn

  •     //inner可以省略(带着inner可读性更好!!!一眼就能看出来是内连接)
        select e.ename,d.dname
        from e
        inner join d on e.deptno = d.deptno; // 条件是等量关系,所以被称为等值连接。
        sql99优点:表连接的条件是独立的,连接之后,如果还需要进一步筛选,再往后继续添加where
    

如果应付《数据库原理的实践考试》,准备这些就足够了。如2021年3月份真题:

答案:

SELECT `xueyuan`.`学院名`,AVG(ksxs.`笔试成绩`),AVG(ksxs.`上机成绩`),
AVG(ksxs.`总成绩`) FROM xueyuan INNER JOIN ksxs ON xueyuan.`学院号`=ksxs.`学院号` WHERE ksxs.`性别`='M' GROUP BY xueyuan.`学院名`
ORDER BY AVG(ksxs.`总成绩`) DESC;

数据库扩展内容

内连接之非等值连接

找出每个员工的薪资等级,要求显示员工名、薪资、薪资等级?

select e.ename, e.sal, s.grade  from  emp e  join  salgrade s  
on e.sal between s.losal and s.hisal; // 条件不是一个等量关系,称为非等值连接。  

内连接自己连接

查询员工的上级领导,要求显示员工名和对应的领导名?

技巧:一张表看成两张表。
select a.ename as '员工名', b.ename as '领导名'from emp a
joinemp b on a.mgr = b.empno; //员工的领导编号 = 领导的员工编号

外连接

  • 内连接:(A和B连接,AB两张表没有主次关系。平等的。)
  • 内连接的特点:完成能够匹配上ON后面的条件的数据查询出来。
// outer是可以省略的,带着可读性强。
select e.ename,d.dname from emp e right outer join dept d
on e.deptno = d.deptno;
  • right代表什么:表示将join关键字右边的这张表看成主表,主要是为了将

  • 这张表的数据全部查询出来,捎带着关联查询左边的表。

  • 在外连接当中,两张表连接,产生了主次关系。

    select e.ename,d.dname from dept d left outer join  emp e on e.deptno = d.deptno;
    带有right的是右外连接,又叫做右连接。
    带有left的是左外连接,又叫做左连接。
    任何一个右连接都有左连接的写法。
    任何一个左连接都有右连接的写法。
    

    多表连接

三张表,四张表怎么连接?

    语法:
        select 
            ...
        from
            a
        join
            b
        on
            a和b的连接条件
        join
            c
        on
            a和c的连接条件
        right join
            d
        on
            a和d的连接条件

找出每个员工的部门名称以及工资等级,要求显示员工名、部门名、薪资、薪资等级?

select  e.ename,e.sal,d.dname,s.grad from emp e
    join dept d
    on e.deptno = d.deptno
    join salgrade s
    on e.sal between s.losal and s.hisal;

子查询

  • select语句中嵌套select语句,被嵌套的select语句称为子查询。

  • 子查询可以出现在 select后面,from 后面,where 后面。

where子句中的子查询

找出比最低工资高的员工姓名和工资?

  • select ename,sal from emp where sal > (select min(sal) from emp);
from子句中的子查询
  • from后面的子查询,可以将子查询的查询结果当做一张临时表。(技巧)
    找出每个岗位的平均工资的薪资等级。
select t.*, s.grade from 
(select job,avg(sal) as avgsal from emp group by job) t join salgrade s on t.avgsal between s.losal and s.hisal;

视图

视图是从一个或多个表中导出来的表,是一种虚拟存在的表。视图就像一个窗口,通过这个窗口可以看到系统专门提供的数据,这样用户可以不看整个数据库表中的数据,而只关心对自己有用的数据。
视图本身并不包含数据。作为一个select语句保存在数据字典中的。

创建视图

CREATE VIEW <视图名> AS <SELECT语句>;
创建视图需要登陆用户有相应的权限,查看权限方法:mysql下输入
select user,Select_priv,Create_view_priv from mysql.user;

例子

create view view_student
as select id,name ,class_id,sex from student;

查看视图

desc view_student;

通过视图可检索数据

select * form view_sudent;
也可以修改数据:
update view_student set name=‘小王王’ where name=‘小王’;

多表创建视图:

create view view_student_teacher
as select class.id as teacher_id,teacher,class,student.id,student.name,sex
from class
left join student on class.id=student.class_id;

修改视图

当基本表的某些字段发生变化的时候,可以通过修改视图来保持与基本表的一致性。ALTER语句来修改视图。
alter view view_student_teacher
as select teacher,class,name,sex
from class
left join student on class.id=student.class_id;

删除视图

DROP VIEW viewname [,viewnamen];

约束

  • 在创建表的时候,我们可以给表中的字段加上一些约束,来保证这个表中数据的完整性、有效性!!!
约束类型
  • 非空约束:not null
  • 唯一性约束: unique
  • 主键约束: primary key (简称PK)
  • 外键约束:foreign key(简称FK)
  • 检查约束:check(mysql不支持,oracle支持)

我们这里重点学习四个约束:not null,unique,primary key,foreign key。

not null
  • 非空约束not null约束的字段不能为NULL。
drop table if exists t_vip;
    create table t_vip(
        id int,
        name varchar(255) not null  // not null只有列级约束,没有表级约束!
    );
insert into t_vip(id,name) values(1,'zhangsan');

insert into t_vip(id) values(3);
    ERROR 1364 (HY000): Field 'name' doesn't have a default value
unique
  • 唯一性约束unique约束的字段不能重复,但是可以为NULL
    drop table if exists t_vip;
    create table t_vip(
        id int,
        name varchar(255) unique,
        email varchar(255)
    );
    insert into t_vip(id,name,email) values(2,'lisi','lisi@123.com');
    insert into t_vip(id,name,email) values(3,'wangwu','wangwu@123.com');
    select * from t_vip;

    insert into t_vip(id,name,email) values(4,'wangwu','wangwu@sina.com');
    ERROR 1062 (23000): Duplicate entry 'wangwu' for key 'name'

    insert into t_vip(id) values(4);
    insert into t_vip(id) values(5);
    +------+----------+------------------+
    | id   | name     | email            |
    +------+----------+------------------+
    |    1 | zhangsan | zhangsan@123.com |
    |    2 | lisi     | lisi@123.com     |
    |    3 | wangwu   | wangwu@123.com   |
    |    4 | NULL     | NULL             |
    |    5 | NULL     | NULL             |
    +------+----------+------------------+
    name字段虽然被unique约束了,但是可以为NULL
联合唯一
  • name和email两个字段联合起来具有唯一性!!!!
drop table if exists t_vip;
create table t_vip(
    id int,
    name varchar(255),
    email varchar(255),
    unique(name,email) // 约束没有添加在列的后面,这种约束被称为表级约束。
);
insert into t_vip(id,name,email) values(1,'zhangsan','zhangsan@123.com');
insert into t_vip(id,name,email) values(2,'zhangsan','zhangsan@sina.com');
    name和email两个字段联合起来唯一!!!

insert into t_vip(id,name,email) values(3,'zhangsan','zhangsan@sina.com');
    ERROR 1062 (23000): Duplicate entry 'zhangsan-zhangsan@sina.com' for key 'name'
  • 在mysql当中,如果一个字段同时被not null和unique约束的话,该字段自动变成主键字段。(注意:oracle中不一样!)
primary key
  • 主键值是每一行记录的唯一标识。主键值是每一行记录的身份证号!!!

  • 主键的特征:not null + unique(主键值不能是NULL,同时也不能重复!)

drop table if exists t_vip;
        // 1个字段做主键,叫做:单一主键
        create table t_vip(
            id int primary key,  //列级约束
            name varchar(255),
            primary key(id)  // 表级约束
        );
        insert into t_vip(id,name) values(1,'zhangsan');
        insert into t_vip(id,name) values(2,'lisi');

        //错误:不能重复
        insert into t_vip(id,name) values(2,'wangwu');
        ERROR 1062 (23000): Duplicate entry '2' for key 'PRIMARY'

        //错误:不能为NULL
        insert into t_vip(name) values('zhaoliu');
        ERROR 1364 (HY000): Field 'id' doesn't have a default value
复合主键
drop table if exists t_vip;
        // id和name联合起来做主键:复合主键!!!!
        create table t_vip(
            id int,
            name varchar(255),
            email varchar(255),
            primary key(id,name)
        );
insert into t_vip(id,name,email) values(1,'zhangsan','zhangsan@123.com');
insert into t_vip(id,name,email) values(1,'lisi','lisi@123.com');

//错误:不能重复
insert into t_vip(id,name,email) values(1,'lisi','lisi@123.com');
    ERROR 1062 (23000): Duplicate entry '1-lisi' for key 'PRIMARY'
  • 在实际开发中不建议使用:复合主键。建议使用单一主键!
  • 因为主键值存在的意义就是这行记录的身份证号,只要意义达到即可,单一主键可以做到。
一个表中主键约束能加两个吗?
        drop table if exists t_vip;
        create table t_vip(
            id int primary key,
            name varchar(255) primary key
        );
        ERROR 1068 (42000): Multiple primary key defined
  • 结论:一张表,主键约束只能添加1个。(主键只能有1个。)

主键分类

  • 自然主键:主键值是一个自然数,和业务没关系。

  • 业务主键:主键值和业务紧密关联,例如拿银行卡账号做主键值。这就是业务主键!

主键自增
  • 在mysql当中,有一种机制,可以帮助我们自动维护一个主键值
rop table if exists t_vip;
create table t_vip(
    id int primary key auto_increment, //auto_increment表示自增,从1开始,以1递增!
    name varchar(255)
);
        insert into t_vip(name) values('zhangsan');
        insert into t_vip(name) values('zhangsan');
        insert into t_vip(name) values('zhangsan');
        insert into t_vip(name) values('zhangsan');
        insert into t_vip(name) values('zhangsan');
        insert into t_vip(name) values('zhangsan');

        +----+----------+
        | id | name     |
        +----+----------+
        |  1 | zhangsan |
        |  2 | zhangsan |
        |  3 | zhangsan |
        |  4 | zhangsan |
        |  5 | zhangsan |
foreign key
  • 如果一个实体的某个字段指向另一个实体的主键,就称为外键
  • 被指向的实体,称之为主实体(主表),也叫父实体(父表)。
  • 负责指向的实体,称之为从实体(从表),也叫子实体(子表)
create table t_class(
    classno int primary key,
    classname varchar(255)
);
create table t_student(
    no int primary key auto_increment,
    name varchar(255),
    cno int,
    foreign key(cno) references t_class(classno)
);

insert into t_class(classno, classname) values(100, '北京市大兴区亦庄镇第二中学高三1班');
insert into t_class(classno, classname) values(101, '北京市大兴区亦庄镇第二中学高三1班');

insert into t_student(name,cno) values('jack', 100);
insert into t_student(name,cno) values('lilei', 100);
insert into t_student(name,cno) values('hanmeimei', 100);
insert into t_student(name,cno) values('zhangsan', 101);
insert into t_student(name,cno) values('lisi', 101);
  • 外键可以为空,可以理解成 一名学生肯定会关联到一个存在的班级,但来了一个转校生,还没有分班,他现在属于学生子表,但还没有关联到班级主表中的任何一条记录。

t_class是父表,t_student是子表

删除表的顺序? 先删子,再删父。

创建表的顺序? 先创建父,再创建子。

删除数据的顺序?先删子,再删父。

插入数据的顺序?先插入父,再插入子

  • 子表中的外键引用的父表中的某个字段,被引用的这个字段不一定是主键,但至少具有unique约束。
索引
显示一个表的索引信息

SHOW INDEX FROM table_name\G

创建索引

CREATE INDEX indexName ON table_name (column_name)
修改表方式添加
ALTER table tableName ADD INDEX indexName(columnName)
创建表时指定
CREATE TABLE mytable(
ID INT NOT NULL,
username VARCHAR(16) NOT NULL,
INDEX [indexName] (username(length))
);

删除索引

DROP INDEX [indexName] ON mytable;

唯一索引

它与前面的普通索引类似,不同的就是:索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。如:
CREATE UNIQUE INDEX indexName ON mytable(username(length))
其他创建方法与普通索引一样。

事务

在 MySQL 中,事务其实是一个最小的不可分割的工作单元。事务能够保证一个业务的完整性

比如我们的银行转账:

-- a -> -100
UPDATE user set money = money - 100 WHERE name = 'a';

-- b -> +100
UPDATE user set money = money + 100 WHERE name = 'b';

在实际项目中,假设只有一条 SQL 语句执行成功,而另外一条执行失败了,就会出现数据前后不一致。

因此,在执行多条有关联 SQL 语句时,事务可能会要求这些 SQL 语句要么同时执行成功,要么就都执行失败。

如何控制事务 - COMMIT / ROLLBACK

在 MySQL 中,事务的自动提交状态默认是开启的。

-- 查询事务的自动提交状态
SELECT @@AUTOCOMMIT;
+--------------+
| @@AUTOCOMMIT |
+--------------+
|            1 |
+--------------+

自动提交的作用:当我们执行一条 SQL 语句的时候,其产生的效果就会立即体现出来,且不能回滚

什么是回滚?举个例子:

CREATE DATABASE bank;

USE bank;

CREATE TABLE user (
    id INT PRIMARY KEY,
    name VARCHAR(20),
    money INT
);

INSERT INTO user VALUES (1, 'a', 1000);

SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
|  1 | a    |  1000 |
+----+------+-------+

可以看到,在执行插入语句后数据立刻生效,原因是 MySQL 中的事务自动将它提交到了数据库中。那么所谓回滚的意思就是,撤销执行过的所有 SQL 语句,使其回滚到最后一次提交数据时的状态。

在 MySQL 中使用 ROLLBACK 执行回滚:

-- 回滚到最后一次提交
ROLLBACK;

SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
|  1 | a    |  1000 |
+----+------+-------+

由于所有执行过的 SQL 语句都已经被提交过了,所以数据并没有发生回滚。那如何让数据可以发生回滚?

-- 关闭自动提交
SET AUTOCOMMIT = 0;

-- 查询自动提交状态
SELECT @@AUTOCOMMIT;
+--------------+
| @@AUTOCOMMIT |
+--------------+
|            0 |
+--------------+

将自动提交关闭后,测试数据回滚:

INSERT INTO user VALUES (2, 'b', 1000);

-- 关闭 AUTOCOMMIT 后,数据的变化是在一张虚拟的临时数据表中展示,
-- 发生变化的数据并没有真正插入到数据表中。
SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
|  1 | a    |  1000 |
|  2 | b    |  1000 |
+----+------+-------+

-- 数据表中的真实数据其实还是:
+----+------+-------+
| id | name | money |
+----+------+-------+
|  1 | a    |  1000 |
+----+------+-------+

-- 由于数据还没有真正提交,可以使用回滚
ROLLBACK;

-- 再次查询
SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
|  1 | a    |  1000 |
+----+------+-------+

那如何将虚拟的数据真正提交到数据库中?使用 COMMIT :

INSERT INTO user VALUES (2, 'b', 1000);
-- 手动提交数据(持久性),
-- 将数据真正提交到数据库中,执行后不能再回滚提交过的数据。
COMMIT;

-- 提交后测试回滚
ROLLBACK;

-- 再次查询(回滚无效了)
SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
|  1 | a    |  1000 |
|  2 | b    |  1000 |
+----+------+-------+

总结

  1. 自动提交

    • 查看自动提交状态:SELECT @@AUTOCOMMIT

    • 设置自动提交状态:SET AUTOCOMMIT = 0

  2. 手动提交

    @@AUTOCOMMIT = 0 时,使用 COMMIT 命令提交事务。

  3. 事务回滚

    @@AUTOCOMMIT = 0 时,使用 ROLLBACK 命令回滚事务。

事务的实际应用,让我们再回到银行转账项目:

-- 转账
UPDATE user set money = money - 100 WHERE name = 'a';

-- 到账
UPDATE user set money = money + 100 WHERE name = 'b';

SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
|  1 | a    |   900 |
|  2 | b    |  1100 |
+----+------+-------+

这时假设在转账时发生了意外,就可以使用 ROLLBACK 回滚到最后一次提交的状态:

-- 假设转账发生了意外,需要回滚。
ROLLBACK;

SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
|  1 | a    |  1000 |
|  2 | b    |  1000 |
+----+------+-------+

这时我们又回到了发生意外之前的状态,也就是说,事务给我们提供了一个可以反悔的机会。假设数据没有发生意外,这时可以手动将数据真正提交到数据表中:COMMIT

手动开启事务 - BEGIN / START TRANSACTION

事务的默认提交被开启 ( @@AUTOCOMMIT = 1 ) 后,此时就不能使用事务回滚了。但是我们还可以手动开启一个事务处理事件,使其可以发生回滚:

-- 使用 BEGIN 或者 START TRANSACTION 手动开启一个事务
-- START TRANSACTION;
BEGIN;
UPDATE user set money = money - 100 WHERE name = 'a';
UPDATE user set money = money + 100 WHERE name = 'b';

-- 由于手动开启的事务没有开启自动提交,
-- 此时发生变化的数据仍然是被保存在一张临时表中。
SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
|  1 | a    |   900 |
|  2 | b    |  1100 |
+----+------+-------+

-- 测试回滚
ROLLBACK;

SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
|  1 | a    |  1000 |
|  2 | b    |  1000 |
+----+------+-------+

仍然使用 COMMIT 提交数据,提交后无法再发生本次事务的回滚。

BEGIN;
UPDATE user set money = money - 100 WHERE name = 'a';
UPDATE user set money = money + 100 WHERE name = 'b';

SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
|  1 | a    |   900 |
|  2 | b    |  1100 |
+----+------+-------+

-- 提交数据
COMMIT;

-- 测试回滚(无效,因为表的数据已经被提交)
ROLLBACK;
事务的 ACID 特征与使用

事务的四大特征:

  • A 原子性:事务是最小的单位,不可以再分割;
  • C 一致性:要求同一事务中的 SQL 语句,必须保证同时成功或者失败;
  • I 隔离性:事务1 和 事务2 之间是具有隔离性的;
  • D 持久性:事务一旦结束 ( COMMIT ) ,就不可以再返回了 ( ROLLBACK ) 。
事务的隔离性

事务的隔离性可分为四种 ( 性能从低到高 )

  1. READ UNCOMMITTED ( 读取未提交 )

    如果有多个事务,那么任意事务都可以看见其他事务的未提交数据

  2. READ COMMITTED ( 读取已提交 )

    只能读取到其他事务已经提交的数据

  3. REPEATABLE READ ( 可被重复读 )

    如果有多个连接都开启了事务,那么事务之间不能共享数据记录,否则只能共享已提交的记录。

  4. SERIALIZABLE ( 串行化 )

    所有的事务都会按照固定顺序执行,执行完一个事务后再继续执行下一个事务的写入操作

查看当前数据库的默认隔离级别:

-- MySQL 8.x, GLOBAL 表示系统级别,不加表示会话级别。
SELECT @@GLOBAL.TRANSACTION_ISOLATION;
SELECT @@TRANSACTION_ISOLATION;
+--------------------------------+
| @@GLOBAL.TRANSACTION_ISOLATION |
+--------------------------------+
| REPEATABLE-READ                | -- MySQL的默认隔离级别,可以重复读。
+--------------------------------+

-- MySQL 5.x
SELECT @@GLOBAL.TX_ISOLATION;
SELECT @@TX_ISOLATION;

修改隔离级别:

-- 设置系统隔离级别,LEVEL 后面表示要设置的隔离级别 (READ UNCOMMITTED)。
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

-- 查询系统隔离级别,发现已经被修改。
SELECT @@GLOBAL.TRANSACTION_ISOLATION;
+--------------------------------+
| @@GLOBAL.TRANSACTION_ISOLATION |
+--------------------------------+
| READ-UNCOMMITTED               |
+--------------------------------+
脏读

测试 READ UNCOMMITTED ( 读取未提交 ) 的隔离性:

INSERT INTO user VALUES (3, '小明', 1000);
INSERT INTO user VALUES (4, '淘宝店', 1000);

SELECT * FROM user;
+----+-----------+-------+
| id | name      | money |
+----+-----------+-------+
|  1 | a         |   900 |
|  2 | b         |  1100 |
|  3 | 小明      |  1000 |
|  4 | 淘宝店    |  1000 |
+----+-----------+-------+

-- 开启一个事务操作数据
-- 假设小明在淘宝店买了一双800块钱的鞋子:
START TRANSACTION;
UPDATE user SET money = money - 800 WHERE name = '小明';
UPDATE user SET money = money + 800 WHERE name = '淘宝店';

-- 然后淘宝店在另一方查询结果,发现钱已到账。
SELECT * FROM user;
+----+-----------+-------+
| id | name      | money |
+----+-----------+-------+
|  1 | a         |   900 |
|  2 | b         |  1100 |
|  3 | 小明      |   200 |
|  4 | 淘宝店    |  1800 |
+----+-----------+-------+

由于小明的转账是在新开启的事务上进行操作的,而该操作的结果是可以被其他事务(另一方的淘宝店)看见的,因此淘宝店的查询结果是正确的,淘宝店确认到账。但就在这时,如果小明在它所处的事务上又执行了 ROLLBACK 命令,会发生什么?

-- 小明所处的事务
ROLLBACK;

-- 此时无论对方是谁,如果再去查询结果就会发现:
SELECT * FROM user;
+----+-----------+-------+
| id | name      | money |
+----+-----------+-------+
|  1 | a         |   900 |
|  2 | b         |  1100 |
|  3 | 小明      |  1000 |
|  4 | 淘宝店    |  1000 |
+----+-----------+-------+

这就是所谓的脏读,一个事务读取到另外一个事务还未提交的数据。这在实际开发中是不允许出现的。

读取已提交

把隔离级别设置为 READ COMMITTED

SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
SELECT @@GLOBAL.TRANSACTION_ISOLATION;
+--------------------------------+
| @@GLOBAL.TRANSACTION_ISOLATION |
+--------------------------------+
| READ-COMMITTED                 |
+--------------------------------+

这样,再有新的事务连接进来时,它们就只能查询到已经提交过的事务数据了。但是对于当前事务来说,它们看到的还是未提交的数据,例如:

-- 正在操作数据事务(当前事务)
START TRANSACTION;
UPDATE user SET money = money - 800 WHERE name = '小明';
UPDATE user SET money = money + 800 WHERE name = '淘宝店';

-- 虽然隔离级别被设置为了 READ COMMITTED,但在当前事务中,
-- 它看到的仍然是数据表中临时改变数据,而不是真正提交过的数据。
SELECT * FROM user;
+----+-----------+-------+
| id | name      | money |
+----+-----------+-------+
|  1 | a         |   900 |
|  2 | b         |  1100 |
|  3 | 小明      |   200 |
|  4 | 淘宝店    |  1800 |
+----+-----------+-------+


-- 假设此时在远程开启了一个新事务,连接到数据库。
$ mysql -u root -p12345612

-- 此时远程连接查询到的数据只能是已经提交过的
SELECT * FROM user;
+----+-----------+-------+
| id | name      | money |
+----+-----------+-------+
|  1 | a         |   900 |
|  2 | b         |  1100 |
|  3 | 小明      |  1000 |
|  4 | 淘宝店    |  1000 |
+----+-----------+-------+

但是这样还有问题,那就是假设一个事务在操作数据时,其他事务干扰了这个事务的数据。例如:

-- 小张在查询数据的时候发现:
SELECT * FROM user;
+----+-----------+-------+
| id | name      | money |
+----+-----------+-------+
|  1 | a         |   900 |
|  2 | b         |  1100 |
|  3 | 小明      |   200 |
|  4 | 淘宝店    |  1800 |
+----+-----------+-------+

-- 在小张求表的 money 平均值之前,小王做了一个操作:
START TRANSACTION;
INSERT INTO user VALUES (5, 'c', 100);
COMMIT;

-- 此时表的真实数据是:
SELECT * FROM user;
+----+-----------+-------+
| id | name      | money |
+----+-----------+-------+
|  1 | a         |   900 |
|  2 | b         |  1100 |
|  3 | 小明      |  1000 |
|  4 | 淘宝店    |  1000 |
|  5 | c         |   100 |
+----+-----------+-------+

-- 这时小张再求平均值的时候,就会出现计算不相符合的情况:
SELECT AVG(money) FROM user;
+------------+
| AVG(money) |
+------------+
|  820.0000  |
+------------+

虽然 READ COMMITTED 让我们只能读取到其他事务已经提交的数据,但还是会出现问题,就是在读取同一个表的数据时,可能会发生前后不一致的情况。这被称为不可重复读现象 ( READ COMMITTED )

幻读

将隔离级别设置为 REPEATABLE READ ( 可被重复读取 ) :

SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SELECT @@GLOBAL.TRANSACTION_ISOLATION;
+--------------------------------+
| @@GLOBAL.TRANSACTION_ISOLATION |
+--------------------------------+
| REPEATABLE-READ                |
+--------------------------------+

测试 REPEATABLE READ ,假设在两个不同的连接上分别执行 START TRANSACTION :

-- 小张 - 成都
START TRANSACTION;
INSERT INTO user VALUES (6, 'd', 1000);

-- 小王 - 北京
START TRANSACTION;

-- 小张 - 成都
COMMIT;

当前事务开启后,没提交之前,查询不到,提交后可以被查询到。但是,在提交之前其他事务被开启了,那么在这条事务线上,就不会查询到当前有操作事务的连接。相当于开辟出一条单独的线程。

无论小张是否执行过 COMMIT ,在小王这边,都不会查询到小张的事务记录,而是只会查询到自己所处事务的记录:

SELECT * FROM user;
+----+-----------+-------+
| id | name      | money |
+----+-----------+-------+
|  1 | a         |   900 |
|  2 | b         |  1100 |
|  3 | 小明      |  1000 |
|  4 | 淘宝店    |  1000 |
|  5 | c         |   100 |
+----+-----------+-------+

这是因为小王在此之前开启了一个新的事务 ( START TRANSACTION ) ,那么在他的这条新事务的线上,跟其他事务是没有联系的,也就是说,此时如果其他事务正在操作数据,它是不知道的。

然而事实是,在真实的数据表中,小张已经插入了一条数据。但是小王此时并不知道,也插入了同一条数据,会发生什么呢?

INSERT INTO user VALUES (6, 'd', 1000);
-- ERROR 1062 (23000): Duplicate entry '6' for key 'PRIMARY'

报错了,操作被告知已存在主键为 6 的字段。这种现象也被称为幻读,一个事务提交的数据,不能被其他事务读取到

串行化

顾名思义,就是所有事务的写入操作全都是串行化的。什么意思?把隔离级别修改成 SERIALIZABLE :

SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SELECT @@GLOBAL.TRANSACTION_ISOLATION;
+--------------------------------+
| @@GLOBAL.TRANSACTION_ISOLATION |
+--------------------------------+
| SERIALIZABLE                   |
+--------------------------------+

还是拿小张和小王来举例:

-- 小张 - 成都
START TRANSACTION;

-- 小王 - 北京
START TRANSACTION;

-- 开启事务之前先查询表,准备操作数据。
SELECT * FROM user;
+----+-----------+-------+
| id | name      | money |
+----+-----------+-------+
|  1 | a         |   900 |
|  2 | b         |  1100 |
|  3 | 小明      |  1000 |
|  4 | 淘宝店    |  1000 |
|  5 | c         |   100 |
|  6 | d         |  1000 |
+----+-----------+-------+

-- 发现没有 7 号王小花,于是插入一条数据:
INSERT INTO user VALUES (7, '王小花', 1000);

此时会发生什么呢?由于现在的隔离级别是 SERIALIZABLE ( 串行化 ) ,串行化的意思就是:假设把所有的事务都放在一个串行的队列中,那么所有的事务都会按照固定顺序执行,执行完一个事务后再继续执行下一个事务的写入操作 ( 这意味着队列中同时只能执行一个事务的写入操作 ) 。

根据这个解释,小王在插入数据时,会出现等待状态,直到小张执行 COMMIT 结束它所处的事务,或者出现等待超时。

数据库三范式

第一范式:要求任何一张表必须有主键,每一个字段原子性不可再分。

第二范式:建立在第一范式的基础之上,要求所有非主键字段完全依赖主键,
不要产生部分依赖。

第三范式:建立在第二范式的基础之上,要求所有非主键字段直接依赖主键,
不要产生传递依赖。

  • 声明:三范式是面试官经常问的,所以一定要熟记在心!

设计数据库表的时候,按照以上的范式进行,可以避免表中数据的冗余,空间的浪费。

第一范式
学生编号 学生姓名 联系方式
    ------------------------------------------
    1001        张三        zs@gmail.com,1359999999
    1002        李四        ls@gmail.com,13699999999
    1001        王五        ww@163.net,13488888888

以上是学生表,满足第一范式吗?
    不满足,第一:没有主键。第二:联系方式可以分为邮箱地址和电话

学生编号(pk) 学生姓名    邮箱地址            联系电话
----------------------------------------------------
1001                张三        zs@gmail.com    1359999999
1002                李四        ls@gmail.com    13699999999
1003                王五        ww@163.net        13488888888
第二范式
    学生编号         学生姓名     教师编号 教师姓名
    ----------------------------------------------------
    1001            张三        001        王老师
    1002            李四        002        赵老师
    1003            王五        001        王老师
    1001            张三        002        赵老师
这张表描述了学生和老师的关系:(1个学生可能有多个老师,1个老师有多个学生)
这是非常典型的:多对多关系!

分析以上的表是否满足第一范式?
    不满足第一范式。

怎么满足第一范式呢?修改

学生编号+教师编号(pk)        学生姓名  教师姓名
----------------------------------------------------
1001            001                张三            王老师
1002            002                李四            赵老师
1003            001                王五            王老师
1001            002                张三            赵老师

学生编号 教师编号,两个字段联合做主键,复合主键(PK: 学生编号+教师编号)
经过修改之后,以上的表满足了第一范式。但是满足第二范式吗?
    不满足,“张三”依赖1001,“王老师”依赖001,显然产生了部分依赖。
    产生部分依赖有什么缺点?
        数据冗余了。空间浪费了。“张三”重复了,“王老师”重复了。

为了让以上的表满足第二范式,你需要这样设计:
    使用三张表来表示多对多的关系!!!!
    学生表
    学生编号(pk)        学生名字
    ------------------------------------
    1001                    张三
    1002                    李四
    1003                    王五

    教师表
    教师编号(pk)        教师姓名
    --------------------------------------
    001                    王老师
    002                    赵老师

    学生教师关系表
    id(pk)            学生编号(fk)            教师编号(fk)
    ------------------------------------------------------
    1                        1001                        001
    2                        1002                        002
    3                        1003                        001
    4                        1001                        002
  • 多对多,三张表,关系表两个外键!!!!!!!!!!!!!!!
第三范式
    学生编号(PK)             学生姓名  班级编号      班级名称
---------------------------------------------------------1001                张三        01            一年一班
​        1002                李四        02            一年二班
​        1003                王五        03            一年三班
​        1004                赵六        03            一年三班

以上表的设计是描述:班级和学生的关系。很显然是1对多关系!
一个教室中有多个学生。

分析以上表是否满足第一范式?
    满足第一范式,有主键。

分析以上表是否满足第二范式?
    满足第二范式,因为主键不是复合主键,没有产生部分依赖。主键是单一主键。

分析以上表是否满足第三范式?
    第三范式要求:不要产生传递依赖!
    一年一班依赖0101依赖1001,产生了传递依赖。
    不符合第三范式的要求。产生了数据的冗余。

那么应该怎么设计一对多呢?

    班级表:一
    班级编号(pk)                班级名称
    ----------------------------------------
    01                                一年一班
    02                                一年二班
    03                                一年三班

    学生表:多

    学生编号(PK) 学生姓名 班级编号(fk)
    -------------------------------------------
    1001                张三            01            
    1002                李四            02            
    1003                王五            03            
    1004                赵六            03        
  • 一对多,两张表,多的表加外键!!!!!!!!!!!!

关于三范式的实际使用

数据库设计三范式是理论上的,实践和理论有的时候有偏差。
最终的目的都是为了满足客户的需求,有的时候会拿冗余换执行速度。
因为在sql当中,表和表之间连接次数越多,效率越低。(笛卡尔积)
有的时候可能会存在冗余,但是为了减少表的连接次数,这样做也是合理的,并且对于开发人员来说,sql语句的编写难度也会降低。

问题

写group by时遇到this is incompatible with sql_mode=only_full_group_by?

这个选项的意思是,只查询group by后面的字段,显然受了限制。
直接 在sqlyog里输入:
SELECT @@GLOBAL.sql_mode;
SELECT @@SESSION.sql_mode;
可以看到sql_mode确实是开启了only_……选项。
解决:
set @@GLOBAL.sql_mode=’ ';
set sql_mode =‘STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION’;
彻底解决的话在,mysql配置文件加:
在 [mysqld]和[mysql]下添加
SET sql_mode =‘STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION’;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

guangod

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值