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
- 官网下载安装包,本电脑是10年前的电脑,因此版本不要太高,5.7版本就可以。
- 配置系统环境变量,将mysqa/bin配置到path路径下
- 配置安装根目录下的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 #跳过密码登录
- 启动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
字符集可以默认也可以选择和您创建数据库的字符集匹配的
列名命名规则和表名称命名规则一致:字母小写,单词之间用下划线_分割
-
更小的更好
选择你认为不会超越范围的最小类型,更小的数据类型通常更快,因为它们占用更少的磁盘、内存 和cpu缓存,并且处理时需要的cpu周期更少。 -
简单就好
简单的数据类型操作通常更快,例如整形的操作比字符串更快,因为字符串有字符集和校对规则 (排序规则)比整形更复杂。 -
尽量避免null
如果字段可以不存储null值,尽量把字段设置成not null。因为可为null的列使得索引、索引统计和 值比较都更复杂,可为null的列会使用更多的存储空间,在mysql里也需要特殊处理。
创建完表之后就要插入数据
这个就有些麻烦。一个个插入的话,就不如用命令方式。
其他功能不再一一演示,重点还是命令行的方式实现建表,插入等操作。
数据库,数据查询(这个在sqlyog与命令行是一样的)
基本的条件查询,where
select 字段1,字段2,字段3… from 表名 where 条件;
条件有:
-
= 等于
-
<>或!= 不等于
-
is null 为 null(is not null 不为空)
-
and 并且 or 或者
select * from emp where sal > 2500 and (deptno = 10 or deptno = 20); and和or同时出现,and优先级较高。如果想让or先执行,需要加“小括号”,以后在开发中,如果不确定优先级,就加小括号就行了。
-
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');
- not 可以取非,主要用在 is 或 in 中
is null
is not null
in
not in
- 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 '%\_%'; // \转义字符。
- 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
- 从某张表中查询数据,
- 先经过where条件筛选出有价值的数据。
- 对这些有价值的数据进行分组。
- 分组之后可以使用having继续筛选。
- select查询出来。
- 最后排序输出!
找出每个岗位的平均薪资,要求显示平均薪资大于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 |
+----+------+-------+
总结
自动提交
查看自动提交状态:
SELECT @@AUTOCOMMIT
;设置自动提交状态:
SET AUTOCOMMIT = 0
。手动提交
@@AUTOCOMMIT = 0
时,使用COMMIT
命令提交事务。事务回滚
@@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
) 。
事务的隔离性
事务的隔离性可分为四种 ( 性能从低到高 ) :
-
READ UNCOMMITTED ( 读取未提交 )
如果有多个事务,那么任意事务都可以看见其他事务的未提交数据。
-
READ COMMITTED ( 读取已提交 )
只能读取到其他事务已经提交的数据。
-
REPEATABLE READ ( 可被重复读 )
如果有多个连接都开启了事务,那么事务之间不能共享数据记录,否则只能共享已提交的记录。
-
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对多关系!
一个教室中有多个学生。
分析以上表是否满足第一范式?
满足第一范式,有主键。
分析以上表是否满足第二范式?
满足第二范式,因为主键不是复合主键,没有产生部分依赖。主键是单一主键。
分析以上表是否满足第三范式?
第三范式要求:不要产生传递依赖!
一年一班依赖01,01依赖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’;