思考一个问题:
淘宝网,京东、微信,抖音 都有各自的功能,那么当我们退出系统的时候,下次再访问时,为什么信息还存在?
为了解决上述问题,使用更加利于管理数据的东东-数据库,它能更有效的管理数据。
举一个生活化的案例说明:
如果说 图书馆是保存书籍的,那么数据库就是保存数据的。
详见我单独发的《MySQL-5.7.19版本安装详解》
在我们实际开发中,会用到一些图形化MySQL的管理软件
其中用的比较多的是Navicat和SQLyog
这两个软件安装的方式就是傻瓜式安装,不做过多讲解
下载地址:
我这里就先用SQLyog操作了
SQLyog安装完以后打开,新建一个本地的MySQL
然后电脑ip那里可以填写localhost或者127.0.0.1(电脑本机ip),密码可以填写上去也可以不填,其他都不用动,点连接就行
设置完以后,即可登录MySQL;注意:一定要保证MySQL 服务是运行的状态!
说明:
练习:
#演示创建数据库
#1. 练习:创建一个名称为sys_db01的数据库。[图形化和指令 演示)
#使用指令创建数据库
CREATE DATABASE sys_db01
#删除数据库
DROP DATABASE sys_db01
#2. 创建一个使用utf8字符集的sys_db02数据库
CREATE DATABASE sys_db02 CHARACTER SET utf8
#3. 创建一个使用utf8字符集,并带校对规则的sys_db03数据库
CREATE DATABASE sys_db03 CHARACTER SET utf8 COLLATE utf8_bin
# 校对规则 utf8_bin 区分大小写 默认utf8_general_ci 不区分大小写
一、当我们在sys_db02数据库中创建一个表t1时,创建时啥都不设置的话,校验规则就是默认utf8_general_ci 不区分大小写
在表t1里面添加两个数据
现在我们进行查询
# 下面是一条查询的sql , selset 查询 * 表示所有字段 FROM 从哪个表查
# WHERE 从哪个字段 NAME = 'jack' 查询名字是 jack
SELECT *
FROM t1
WHERE NAME = 'jack'
查询结果如下
二、当我们在sys_db03数据库中创建一个表t1时,创建时啥都不设置的话,校验规则就是默认utf8_bin区分大小写
在表t1里面添加两个数据(与上面相同)
现在我们进行查询
# 下面是一条查询的sql , selset 查询 * 表示所有字段 FROM 从哪个表查
# WHERE 从哪个字段 NAME = 'jack' 查询名字是 jack
SELECT *
FROM t1
WHERE NAME = 'jack'
查询结果如下
练习:
# 演示删除和查询数据库
#1. 查看当前数据库服务器中的所有数据库
SHOW DATABASES
#2. 查看前面创建的hsp db01数据库的定义信息
SHOW CREATE DATABASE ‘sys_db01‘
#说明:在创建数据库,表的时候,为了规避关键字,可以使用反引号解决
#3. 删除前面创建的hsp db01数据库
DROP DATABASE sys_db01
查看当前数据库服务器中的所有数据库示意图
查看前面创建的hsp db01数据库的定义信息示意图
删除前面创建的hsp db01数据库示意图
备份数据库(注意:在DOS执行命令行
mysqldump -u 用户名 -p -B 数据库1 数据库2 数据库n > 文件名.sq
恢复数据库(注意: 进入Mysql命令行再执行)
Source 文件名.sgl
练习:
#删除数据库
DROP DATABASE sys_db02
#恢复数据库(注意:进入mysql命令行再执行)
source f:\\bak.sql
#第二个恢复方法,直接将bak.sql的内容放到查询编辑器中执行
mysqldump -u 用户名 -p密码 数据库 表1 表2 表n > d:文件名sq
#直接在命令行输入mysqldump -U root -p sys_db03 t1 > f:\\table03.sql就备份好了
注意: sys_db02创建表时,要根据需保存的数据创建相应的列,并根据数据的类型定义相应的列类型。
例: 下面那个user表
id 整形 [图形化,指令]
name 字符串
password 字符串
birthday 日期
CREATE TABLE `user`(
id INT,
`name` VARCHAR(255),
`password` VARCHAR(255),
birthday DATE)
CHARACTER SET utf8 COLLATE utf8_bin ENGINE INNODB;
然后在sys_db02页面执行这行命令,就创建成功了
说明
#演示整型
#使用tinyint来演示范围 有符号 -128~127 没有符号 0~255
#表的字符集、校验规则,存储引擎使用默认
# 1. 如果没有指定 unsigned ,则 TINYINT 就是有符号
# 2. 如果指定 unsigned , 则 TINYINT 就是无符号 0-255
CREATE TABLE t3(
id TINYINT);#有符号
CREATE TABLE t4(
id TINYINT UNSIGNED);#无符号
INSERT INTO t3 VALUES(-128);#非常简单的添加语句
INSERT INTO t4 VALUES(0);#非常简单的添加语句
SELECT * FROM t3;
SELECT * FROM t4;
create table t10 (id tinyint ); //默认是有符号的
create table t11 (id tinyint unsigned ); //无符号的
基本使用
mysql> create table t05 (num bit(8));
mysql> insert into t05 (1, 3);
mysql> insert into t05 values(2, 65);
细节说明
演示:
#演示 bit 类型使用
#说明
# 1. bit(m) m在 1-64
# 2. 添加数据范围
# 3. 显示按照bit
# 4. 查询时,仍然可以按照数值来查询
CREATE TABLE t05(num BIT(8));
INSERT INTO t05 VALUES(255);
SELECT * FROM t05;
SELECT * FROM t05 WHERE num = 1;
FLOAT/DOUBLE [UNSIGNEDI
DECIMAL [M,D] [UNSIGNED]
演示decimal类型、float、double使用
#演示decimal类型、float、double使用
#创建表
CREATE TABLE t06(
num1 FLOAT,
num2 DOUBLE,
num3 DECIMAL(30,20));
#添加数据
INSERT INTO t06 VALUES(88.151351516164161,
88.151351516164161,88.151351516164161);
SELECT * FROM t06
#decimal可以存放很大的数
CREATE TABLE t07(
num DECIMAL(65))
INSERT INTO t07 VALUES(151154111544152148154164616484165184);
SELECT * FROM t07;
CHAR(size)
固定长度字符串 最大255 字符
VARCHAR(size)
0~65535可变长度字符串 最大65532字节 [utf8编码最大21844字符 1-3个字节用于记录大小]
应用案例 charVarchar.sql 文件
#演示字符串类型使用 char varchar
#注释的快捷键 shift+ctrl+c
#取消注释 shift+ctrl+r
-- 1. CHAR(size)
-- 固定长度字符串 最大255 字符
-- 2. VARCHAR(size)
-- 0~65535可变长度字符串 最大65532字节 [utf8编码最大21844字符 1-3个字节用于记录大小]
-- 如果表的编码为 utf8 varchar(size)=(65535-3)/3 = 21844
-- 如果表的编码为 gbk varchar(size)=(65535-3)/2 = 32766
CREATE TABLE t08(
`NAME` CHAR(255));
CREATE TABLE t09(
`name` VARCHAR(21844));
CREATE TABLE t10(
`name` VARCHAR(32766) CHARSET gbk);
细节1 charVarcharDetail.sql
细节2
细节3
什么时候使用 char,什么时候使用varchar1.
细节4
#细节1:char(4)和varchar(4) 这个4表示的是字符,而不是字节
CREATE TABLE t11(
`name` CHAR(4));
INSERT INTO t11 VALUES('abcd');#如果是5个就存放失败
SELECT * FROM t11;
CREATE TABLE t12(
`name` VARCHAR(4));
INSERT INTO t12 VALUES('大家好a')#如果是5个就存放失败
SELECT * FROM t12;
#细节4:
#如果varchar不够用,可以考虑mediumtext或者longtext
#如果想简单点,可以直接使用text
CREATE TABLE t13(content1 TEXT,content2 MEDIUMTEXT,content3 LONGTEXT);
INSERT INTO t13 VALUES('sys学java','sys学java加油','sys学java~~~');
SELECT * FROM t13;
CREATE TABLE birthday6(
t1 DATE, t2 DATETIME,
t3 TIMESTAMP NOT NULL DEFAULT
CURRENT TIMESTAMP ON UPDATE
CURRENT TIMESTAMP ); timestamp时间截
mysql> INSERT INTO birthday (t1,t2)
VALUES(‘2022-11-11’,‘2022-11-11 10:10:10’);
日期类型的细节说明
TimeStamp在Insert和update时,自动更新datetime.sql
#演示时间相关的类型
#创建一张表 , date ,datetime , timestamp
CREATE TABLE t14(
birthday DATE,-- 生日
job_time DATETIME,-- 记录年月日 时分秒
login_time TIMESTAMP
NOT NULL DEFAULT CURRENT_TIMESTAMP
ON UPDATE CURRENT_TIMESTAMP);-- 登陆时间,如果希望login_time列自动更新
SELECT * FROM t14;
INSERT INTO t14 (birthday,job_time)
VALUES('2023.6.1','2023.6.1 10:10:10');
-- 如果我们更新了 t14表的某条记录,login_time列会自动的以当前的时间进行更新
创建一个员工表 emp (课堂练习),选用适当的数据类型 createtable.sql
代码如下:
#创建一个员工表 emp (课堂练习),选用适当的数据类型
-- 字段 属性
-- id 整形
-- name 字符型
-- sex 字符型
-- brithday 日期型(DATE)
-- entry date 日期型(DATE)
-- job 字符型
-- Salary 小数型
-- resume 文本型
CREATE TABLE `emp`(
id INT,
`name` VARCHAR(32),
sex CHAR(1),
birthday DATE,
entry_date DATETIME,
job VARCHAR(32),
salary FLOAT,
`resume` TEXT)
CHARSET utf8 COLLATE utf8_bin ENGINE INNODB;
-- 添加一条记录
INSERT INTO `emp`
VALUES(168,'张三','男','1993-11-26',
'2023-7-11 8:00:00','java','15000','java');
-- 查询
SELECT * FROM `emp`;
效果如下:
使用 ALTER TABLE 语句追加,修改,或删除列的语法:
修改表名: Rename table 表名 to 新表名
修改表字符集: alter table 表名 character set 字符集:
修改表列名:alter table user change column name username varchar(20);
#修改表的操作练习
-- 1. 员工表emp的上增加一个image列,varchar类型(要求在resume后面)
ALTER TABLE emp -- 修改表emp
-- 增加 image列,varchar类型,不允许为空,后面加个空字符串
ADD image VARCHAR(32) NOT NULL DEFAULT ''
AFTER RESUME -- 加在resume字段后面
DESC employee -- 显示表结构,可以查看表的所有列
-- 2. 修改job列,使其长度为60。
ALTER TABLE emp
MODIFY job VARCHAR(60) NOT NULL DEFAULT''
-- 3. 删除sex列。
ALTER TABLE emp
DROP sex
-- 4. 表名改为employee。
RENAME TABLE emp TO employee
-- 5. 修改表的字符集为utf8
ALTER TABLE employee CHARACTER SET utf8
-- 6. 列名name修改为user name
ALTER TABLE employee
CHANGE `name` user_name VARCHAR(64) NOT NULL DEFAULT ''
数据库C[create]R[read]U[update]D[delete]语句
练习1:
# 练习 insert 语句
-- 1. 创建一张商品表goods (id int,goods_name varchar(10) ,price double );
-- 2. 添加2条记录
CREATE TABLE goods(
id INT,
goods_name VARCHAR(10),
price DOUBLE);
-- 添加数据
INSERT INTO `goods`(id,goods_name,price)
VALUES(1,'小米手机',1999);
INSERT INTO `goods`(id,goods_name,price)
VALUES(2,'苹果手机',9999);
-- 查阅
SELECT * FROM goods;
效果如下
练习2:
插入的数据应与字段的数据类型相同。
– 比如 把 ‘abc’ 添加到 int 类型会错误
数据的长度应在列的规定范围内,例如:不能将一个长度为 80 的字符串加入到长度为 40 的列中。
在 values 中列出的数据位置必须与被加入的列的排列位置相对应。
字符和日期型数据应包含在单引号中。
列可以插入空值[前提是该字段允许为空],insert into table value(null)
insert into tab_name (列名…) values (),(),() 形式添加多条记录
如果是给表中的所有字段添加数据,可以不写前面的字段名称
默认值的使用,当不给某个字段值时,如果有默认值就会添加默认值,否则报错
– 如果某个列 没有指定 not null ,那么当添加数据时,没有给定值,则会默认给 null
– 如果我们希望指定某个列的默认值,可以在创建表时指定
演示如下:
#说明 insert 语句的细节
CREATE TABLE goods2(
id INT,
goods_name VARCHAR(10),
price DOUBLE NOT NULL DEFAULT 100);
-- 1. 插入的数据应与字段的数据类型相同。
-- 比如 把 'abc' 添加到 int 类型会错误
INSERT INTO `goods2`(id,goods_name,price)
VALUES('abc','小米手机',1999);-- 会直接报错
-- 2. 数据的长度应在列的规定范围内,例如:不能将一个长度为 80 的字符串加入到长度为 40 的列中。
INSERT INTO `goods2`(id,goods_name,price)
VALUES(3,'锤子手机锤子手机锤子手机锤子手机',1999);-- goods_name的长度为10,超过范围
-- 3. 在 values 中列出的数据位置必须与被加入的列的排列位置相对应。
INSERT INTO `goods2`(id,goods_name,price)
VALUES('小米手机',1,1999);-- 位置不对应
-- 4. 字符和日期型数据应包含在单引号中。
INSERT INTO `goods2`(id,goods_name,price)
VALUES(1,小米手机,1999);-- varchar 不用单引号会报错
-- 5. 列可以插入空值[前提是该字段允许为空],insert into table value(null)
INSERT INTO `goods2`(id,goods_name,price)
VALUES(NULL,'小米手机',1999);-- 在定义字段属性的时候后面没有写 not null就可以填空值
-- 6. insert into tab_name (列名..) values (),(),() 形式添加多条记录
INSERT INTO `goods2`(id,goods_name,price)
VALUES(2,'榔头手机',2999),(3,'菠萝手机',2199);
-- 7. 如果是给表中的所有字段添加数据,可以不写前面的字段名称
INSERT INTO `goods2`
VALUES(4,'砖头手机',1999);
-- 8. 默认值的使用,当不给某个字段值时,如果有默认值就会添加默认值,否则报错
-- 如果某个列 没有指定 not null ,那么当添加数据时,没有给定值,则会默认给 null
-- 如果我们希望指定某个列的默认值,可以在创建表时指定
INSERT INTO `goods2`(id,goods_name)
VALUES(5,'椰子手机');
SELECT * FROM goods2;
表格如下:
要求:在上面创建的employee表中修改表中的纪录
#演示update语句
-- 1. 将所有员工薪水修改为5000元。
-- [如果没有带where条件,会修改所有的记录,因此用的时候要小心]
UPDATE employee SET salary = 5000
-- 2. 将姓名为 张三的员工薪水修改为3000元
UPDATE employee
SET salary = 3000
WHERE user_name = '张三';
-- 3. 将小明的薪水在原有基础上增加1000元
UPDATE employee
SET salary = salary + 1000
WHERE user_name = '小明';
#查阅
SELECT * FROM employee;
修改后的表格如下:
-- 4. 可以修改多个值
UPDATE employee
SET salary = salary + 1000,job = '法官'
WHERE user_name = '小明';
#delete演示
-- 1. 删除表中名称为’小红’的记录。
DELETE FROM employee
WHERE user_name = '小红';
-- 2. 删除表中所有记录。
DELETE FROM employee
SELECT * FROM employee;
-- Delete语句不能删除某一列的值 (可使用update 设为 null 或者'')
UPDATE employee SET job = ''
WHERE user_name = '小明';
-- 使用delete语句仅删除记录,不删除表本身。如要删除表,使用drop table语句。drop table 表名
DROP TABLE employee;
-- select 语句【重点 难点】
CREATE TABLE student(
id INT NOT NULL DEFAULT 1,
`NAME` VARCHAR(20) NOT NULL DEFAULT '',
chinese FLOAT NOT NULL DEFAULT 0.0,
english FLOAT NOT NULL DEFAULT 0.0,
math FLOAT NOT NULL DEFAULT 0.0);
INSERT INTO student(id,`name`,chinese,english,math)
VALUES(1,'孙裕松',88,85,92),(2,'张飞',78,66,98),
(3,'关羽',78,75,82),(4,'刘备',98,86,96),
(5,'吕布',86,90,86),(6,'赵云',92,88,95);
-- 1. 查询表中所有学生的信息。
SELECT * FROM student;
-- 2. 查询表中所有学生的姓名和对应的英语成绩
SELECT `name`,english FROM student;
-- 3. 过滤表中重复数据 distinct 。
SELECT DISTINCT * FROM student
-- 4. 要查询的记录,每个字段都相同,才会去重
SELECT DISTINCT chinese FROM student
-- select 语句【重点 难点】
-- 1. 统计每个学生的总分
SELECT `name`,(chinese+english+math) FROM student
-- 2. 在所有学生总分加10分的情况
SELECT `name` ,(chinese+english+math+10) FROM student
-- 3. 使用别名表示学生分数。
SELECT `name`,(chinese+english+math+10) AS total_score FROM student
效果如下:
练习题1:
使用where子句:
-- select 语句【重点 难点】
-- 1. 查询姓名为赵云的学生成绩
SELECT * FROM student
WHERE `name` = '赵云';
-- 2. 查询英语成绩大于90分的同学
SELECT * FROM student
WHERE english > 90;
-- 3. 查询总分大于200分的所有同学
SELECT * FROM student
WHERE (chinese+english+math)>200;
-- 使用where子句:
-- 1. 查询math大于60 并且(and) id大于5的学生成绩
SELECT * FROM student
WHERE math > 90 AND id > 5;
-- 2. 查询英语成绩大于语文成绩的同学
SELECT * FROM student
WHERE english > chinese;
-- 3. 查询总分大于200分 并且 数学成绩小于语文成绩,的姓刘的学生
-- 刘% 表示名字以 刘开头的就可以
SELECT * FROM student
WHERE (chinese+english+math)>200 AND
math < chinese AND `name` LIKE '刘%'
练习题2:
-- 练习题2:
-- 1. 查询英语分数在 80- 90之间的同学
SELECT * FROM student
WHERE english >= 80 AND english <= 90;
SELECT * FROM student
WHERE english BETWEEN 80 AND 90;
-- 2. 查询英语分数为89,90,91的同学。
SELECT * FROM student
WHERE english = 89 OR english = 90 OR english = 91;
SELECT * FROM student
WHERE english IN (89 , 90 , 91);
-- 3. 查询所有姓李的学生成绩。
SELECT * FROM student
WHERE `name` LIKE '孙%';
-- 4. 查询数学分>80,语文分>80的同学
SELECT * FROM student
WHERE math >80 AND chinese > 80;
练习题3:
-- 练习题3:
-- 1. 查询语文分数在 70 - 80之间的同学
SELECT * FROM student
WHERE chinese IN (70,80);
-- 2. 查询总分为189,190,191的同学
SELECT * FROM student
WHERE (chinese+english+math) IN (189,190,191);
-- 3. 查询所有姓赵 或者 姓张的学生成绩
SELECT * FROM student
WHERE `name` LIKE '赵%' OR `name` LIKE '张%';
-- 4. 查询数学比语文多30分的同学
SELECT * FROM student
WHERE (math-chinese) >= 30;
基本介绍
练习:
-- 演示 order by使用
-- 1. 对数学成绩排序后输出[升序]
SELECT * FROM student
ORDER BY math;
-- 2. 对总分按从高到低的顺序输出
SELECT `name`,(chinese+english+math) AS total_score FROM student
ORDER BY total_score DESC;
-- 3. 对姓张的学生成绩排序输出(升序)
SELECT `name` ,(chinese + english + math) AS total_score FROM student
WHERE `name` LIKE '张%'
ORDER BY total_score ASC;
# 演示 mysql 的统计函数的使用
-- 1. 统计一个班级共有多少学生?
SELECT COUNT(*) FROM student;
-- 2. 统计数学成绩大于90的学生有多少个?
SELECT COUNT(*) FROM student
WHERE math > 90;
-- 3. 统计总分大于250的人数有多少?
SELECT COUNT(*) FROM student
WHERE (chinese + english + math) > 250;
-- 4. count(*)和 count(列) 的区别
-- count(*) 返回满足条件的记录的行数
-- count(列):统计满足条件的某列有多少个,但是会排出null
CREATE TABLE t15(
id VARCHAR(10));
INSERT INTO t15 VALUES ('jack');
INSERT INTO t15 VALUES ('lucy');
INSERT INTO t15 VALUES ('john');
INSERT INTO t15 VALUES ('hebe');
INSERT INTO t15 VALUES (NULL);
SELECT * FROM t15;
SELECT COUNT(*) FROM t15;-- 5
SELECT COUNT(id) FROM t15;-- 4
Sum函数返回满足where条件的行的和: 一般使用在数值列
注意:sum仅对数值起作用,没有意义
注意:对多列求和,“,”号不能少。
-- 演示 sum的使用
-- 1. 统计一个班级数学总成绩?
SELECT SUM(math) FROM student;
-- 2. 统计一个班级语文、英语、数学各科的总成绩
SELECT SUM(math) AS math_total,SUM(english) AS english_total,
SUM(chinese) AS chinese_total FROM student;
-- 3. 统计一个班级语文、英语、数学的成绩总和
SELECT SUM(math + english + chinese) AS total_score FROM student;
-- 4. 统计一个班级语文成绩平均分
SELECT SUM(chinese)/COUNT(*) FROM student;
AVG函数返回满足where条件的一列的平均值
-- 演示avg的使用
-- 1. 求一个班级数学平均分?
SELECT AVG(math) FROM student;
-- 2. 求一个班级总分平均分
SELECT AVG(math + chinese + english) FROM student;
Max/min函数返回满足where条件的一列的最大/最小值
-- 演示 max/min 的使用
-- 1. 求班级最高分和最低分(数值范围在统计中特别有用)
SELECT MAX(chinese + math + english) AS total_score_high,
MIN(chinese + math + english) AS total_score_low FROM student;
-- 2. 求班级数学最高分和最低分
SELECT MAX(math),MIN(math) FROM student;
使用group by 子句对列进行分组 [先创建测试表]
使用having 子句对分组后的结果进行过滤
先创建要用到的表
CREATE TABLE dept( /*部门表*/
deptno MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,
dname VARCHAR(20) NOT NULL DEFAULT "",
loc VARCHAR(13) NOT NULL DEFAULT ""
);
INSERT INTO dept VALUES(10, 'ACCOUNTING', 'NEW YORK'),-- 会计
(20, 'RESEARCH', 'DALLAS'), -- 研究人员
(30, 'SALES', 'CHICAGO'), -- 销售员
(40, 'OPERATIONS', 'BOSTON');-- 运营总监
SELECT * FROM dept;
#创建表EMP雇员
CREATE TABLE emp
(empno MEDIUMINT UNSIGNED NOT NULL DEFAULT 0, /*编号*/
ename VARCHAR(20) NOT NULL DEFAULT "", /*名字*/
job VARCHAR(9) NOT NULL DEFAULT "",/*工作*/
mgr MEDIUMINT UNSIGNED ,/*上级编号*/
hiredate DATE NOT NULL,/*入职时间*/
sal DECIMAL(7,2) NOT NULL,/*薪水*/
comm DECIMAL(7,2) ,/*红利*/
deptno MEDIUMINT UNSIGNED NOT NULL DEFAULT 0 /*部门编号*/
);
INSERT INTO emp VALUES(7369, 'SMITH', 'CLERK', 7902, '1990-12-17', 800.00,NULL , 20),
(7499, 'ALLEN', 'SALESMAN', 7698, '1991-2-20', 1600.00, 300.00, 30),
(7521, 'WARD', 'SALESMAN', 7698, '1991-2-22', 1250.00, 500.00, 30),
(7566, 'JONES', 'MANAGER', 7839, '1991-4-2', 2975.00,NULL,20),
(7654, 'MARTIN', 'SALESMAN', 7698, '1991-9-28',1250.00,1400.00,30),
(7698, 'BLAKE','MANAGER', 7839,'1991-5-1', 2850.00,NULL,30),
(7782, 'CLARK','MANAGER', 7839, '1991-6-9',2450.00,NULL,10),
(7788, 'SCOTT','ANALYST',7566, '1997-4-19',3000.00,NULL,20),
(7839, 'KING','PRESIDENT',NULL,'1991-11-17',5000.00,NULL,10),
(7844, 'TURNER', 'SALESMAN',7698, '1991-9-8', 1500.00, NULL,30),
(7900, 'JAMES','CLERK',7698, '1991-12-3',950.00,NULL,30),
(7902, 'FORD', 'ANALYST',7566,'1991-12-3',3000.00, NULL,20),
(7934,'MILLER','CLERK',7782,'1992-1-23', 1300.00, NULL,10);
SELECT * FROM emp;
#工资级别表
CREATE TABLE salgrade
(
grade MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,/*工资级别*/
losal DECIMAL(17,2) NOT NULL,/*该级别的最低工资*/
hisal DECIMAL(17,2) NOT NULL/*该级别的最高工资*/
);
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);
SELECT * FROM salgrade;
然后演示 group by 和 having 的使用
# 演示 group by + having
-- 1. group by用于对查询的结果分组统计,(示意图)
-- 2. having子句用于限制分组显示结果
-- 3. 如何显示每个部门的平均工资和最高工资?
-- 分析: 使用avg 和 max
-- 按照部门来分组查询
SELECT AVG(sal),MAX(sal),deptno
FROM emp GROUP BY deptno;
-- 4. 显示每个部门的每种岗位的平均工资和最低工资?
-- 分析:1. 显示每个部分的平均工资和最低工资
-- 2. 显示每个部分的每种岗位的平均工资和最低工资
SELECT AVG(sal),MIN(sal),deptno,job
FROM emp GROUP BY deptno,job;
-- 5. 显示平均工资低于2000的部门号和它的平均工资 // 别名?
-- 思路分析[化繁为简,各个击破]
-- 1. 先显示各部门号的平均工资
SELECT AVG(sal) ,deptno
FROM emp GROUP BY deptno;
-- 2. 然后在1的结果上进行过滤,保留平均工资低于2000的
SELECT AVG(sal) ,deptno
FROM emp GROUP BY deptno
HAVING AVG(sal) < 2000;
-- 3. 使用别名进行过滤,提高效率
SELECT AVG(sal) AS avg_sal ,deptno
FROM emp GROUP BY deptno
HAVING avg_sal < 2000;
-- 演示字符串相关函数的使用 使用emp表来演示
-- CHARSET(str) 返回字串字符集
SELECT CHARSET(ename) FROM emp;
-- CONCAT (string2 [.... ]) 连接字串
SELECT CONCAT (ename ,' job is ' ,job) FROM emp;
-- INSTR (string ,substring ) 返回substring在string中出现的位置,没有返回0
-- dual 亚元表
SELECT INSTR('sys学java','java') FROM DUAL;
-- UCASE (string2 ) 转换成大写
SELECT UCASE(ename) FROM emp;
-- LCASE (string2 ) 转换成小写
SELECT LCASE(ename) FROM emp;
-- LEFT (string2 ,length ) 从string2中的左边起取length个字符
-- RIGHT (string2 ,length ) 从string2中的右边起取length个字符
SELECT LEFT(ename,3) FROM emp;
SELECT RIGHT(ename,3) FROM emp;
-- LENGTH (string ) string长度[按照字节]
SELECT LENGTH('sys学java') FROM emp;
-- REPLACE (str ,search_str ,replace str )
-- 在str中用replace str替换search str
SELECT ename , REPLACE(job,'MANAGER','经理') FROM emp;
SELECT * FROM emp;
-- STRCMP (string1 ,string2 ) 逐字符比较两字串大小,
SELECT STRCMP('jack','jadk') FROM DUAL;
-- SUBSTRING (str , position [,length])
-- 从str的position开始[从1开始计算],取length个字符
-- 从 ename 列的第一个位置开始,截取3个字符
SELECT SUBSTRING(ename,1,3) FROM emp;
-- LTRIM (string2 ) RTRIM (string2 ) TRIM(string)
-- 去除前端空格或后端空格
SELECT LTRIM(' sys java ') FROM DUAL;
SELECT RTRIM(' sys java ') FROM DUAL;
SELECT TRIM(' sys java ') FROM DUAL;
-- 以首字母小写的方式显示所有员工emp表的姓名 str.sql 使用两种方式
-- 第一种方式
-- 先截取首字母,转为小写 LCASE( LEFT(ename,1)
-- 然后截取后面的字母 UCASE(SUBSTRING(ename,2))
-- 将两段字母拼接到一起 CONCAT (LCASE( LEFT(ename,1)) , UCASE(SUBSTRING(ename,2)))
SELECT CONCAT (LCASE( LEFT(ename,1)) , UCASE(SUBSTRING(ename,2))) FROM emp;
-- 第二种方式
-- 先截取首字母,转为小写 LCASE( LEFT(ename,1)
-- 然后再截取首字母 LEFT(ename,1)
-- 将这两个字母替换 REPLACE(ename,LEFT(ename,1) ,LCASE( LEFT(ename,1)))
SELECT REPLACE(ename,LEFT(ename,1) ,LCASE( LEFT(ename,1))) FROM emp;
rand0返回一个随机浮点值 v范围在 0到1 之间(即,其范围为 0 s vs 1.0)。
若已指定一个整数参数 N,则它被用作种子值,用来产生重复序列。
-- 演示数学相关函数
-- ABS(num) 绝对值
SELECT ABS(-10) FROM DUAL; -- 10
-- BIN (decimal number ) 十进制转二进制
SELECT BIN(10) FROM DUAL; -- 1010
-- CEILING (number2 ) 向上取整,得到比num2 大的最小整数
SELECT CEILING(10.2) FROM DUAL; -- 11
-- CONV(number2,from base,to base) 进制转换
SELECT CONV(16,16,10) FROM DUAL; -- 22
-- FLOOR(number2 ) 向下取整,得到比 num2 小的最大整数
SELECT FLOOR(11.9) FROM DUAL; -- 11
-- FORMAT (number,decimal_places ) 保留小数位数(四舍五入)
SELECT FORMAT(16.23452,3) FROM DUAL; -- 16.235
-- HEX (DecimalNumber ) 转十六进制
SELECT HEX(10) FROM DUAL; -- A
-- LEAST (number , number2 [,..]) 求最小值
SELECT LEAST(1,2,5,3,2,1) FROM DUAL; -- 1
-- MOD (numerator ,denominator ) 求余
SELECT MOD(12,5) FROM DUAL; -- 2
-- RAND([seed]) RAND([seed])其范围为0≤v≤1.0
-- 1. 如果使用 rand() 每次返回不同的随机数 ,在 0 ≤ v ≤ 1.0
-- 2. 如果使用 rand(seed) 返回随机数, 范围 0 ≤ v ≤ 1.0, 如果 seed 不变,
-- 该随机数也不变了
SELECT RAND() FROM DUAL;
SELECT RAND(5) FROM DUAL;
查询
细节说明:
在实际开发中,我们也经常使用int来保存一个unix时间截
然后使用 from unixtime( 进行转换,还是非常有实用价值的
-- 日期时间相关函数
-- CURRENT_DATE ( ) 当前日期
SELECT CURRENT_DATE() FROM DUAL;
-- CURRENT_TIME ( ) 当前时间
SELECT CURRENT_TIME() FROM DUAL;
-- CURRENT_TIMESTAMP ( ) 当前时间戳
SELECT CURRENT_TIMESTAMP() FROM DUAL;
-- 创建测试表 信息表
CREATE TABLE mes(
id INT ,
content VARCHAR(30),
sendtime DATETIME);
-- 添加一条记录
INSERT INTO mes
VALUES(1,'北京时间',CURRENT_TIMESTAMP);
INSERT INTO mes
VALUES(2,'今日快报',NOW());
INSERT INTO mes
VALUES(3,'叶子快报',NOW());
-- 查询
SELECT * FROM mes;
-- DATE (datetime ) 返回datetime的日期部分
-- DATE_ADD (date2, INTERVAL d_value d_type ) 在date2中加上日期或时间
-- DATE_SUB (date2 , INTERVAL d_value d_type ) 在date2上减去一个时间
-- DATEDIFF (date1 ,date2 ) 两个日期差(结果是天)
-- 应用实例
-- 显示所有信息,发布日期只显示日期,不用显示时间.
SELECT id,content,DATE(sendtime)
FROM mes;
-- 请查询在10分钟内发布的帖子
-- 用 date_add
SELECT *
FROM mes
WHERE DATE_ADD(sendtime,INTERVAL 10 MINUTE) >= NOW();
-- 用 date_sub
SELECT *
FROM mes
WHERE DATE_SUB(NOW(),INTERVAL 10 MINUTE) <= sendtime;
-- 请在mysql 的sql语句中求出 2011-11-11 和1990-1-1 相差多少天
SELECT DATEDIFF('2011-11-11','1990-01-01') FROM DUAL; -- 7984
-- 请用mysql 的sql语句中求出你活了多少天?[练习]
SELECT DATEDIFF(NOW(),'1998-11-26') FROM DUAL; -- 10783
-- 如果你能活80岁,求出你还能活多少天.[练习]
-- 思路:
-- 先算出活到80岁是到哪一年 DATE_ADD('1993-11-26',INTERVAL 80 YEAR)
-- 然后算出80岁哪一年到现在的时间差
-- DATEDIFF( DATE_ADD('1998-11-26',INTERVAL 80 YEAR),NOW())
SELECT DATEDIFF( DATE_ADD('1998-11-26',INTERVAL 80 YEAR),NOW()) FROM DUAL;
-- 细节注意:
-- INTERVAL 80 YEAR : YEAR 可以是 年月日,时分秒
-- '1986-11-11' 可以 date,datetime timestamp
-- TIMEDIFF(datel,date2) 两个时间差(多少小时多少分钟多少秒)
SELECT TIMEDIFF('11:11:11','08:08:08') FROM DUAL;-- 03:03:03
-- YEAR | Month | DAYI DATE (datetime) 年月日
SELECT YEAR(NOW()) FROM DUAL; -- 只显示年
SELECT MONTH(NOW()) FROM DUAL; -- 只显示月
SELECT DAY(NOW()) FROM DUAL; -- 只显示日
SELECT YEAR('2023-6-5') FROM DUAL; -- 只显示年
-- unix_timestamp(); 返回的是1970-1-1 到现在的秒数
SELECT UNIX_TIMESTAMP() FROM DUAL;
-- FROM_UNIXTIME(); 可以把一个 unix_timestamp 的秒数,转换成指定格式的日期
-- %Y-%m-%d 格式表示 年-月-日 ,这是规定好的
-- Y-%m-%d %H:%i:%s 格式表示 年-月-日 时:分:秒,这也是规定好的
-- 意义:在开发中可以存放一个整数,然后表示时间,通过FROM_UNIXTIME 转换
SELECT FROM_UNIXTIME(1561615656,'%Y-%m-%d') FROM DUAL;
SELECT FROM_UNIXTIME(1561615656,'%Y-%m-%d %H:%i:%s') FROM DUAL;
-- 在实际开发中,我们也经常使用int来保存一个unix时间截
-- 然后使用 from unixtime( )进行转换,还是非常有实用价值的
简单演示一下用法
-- 演示加密函数和系统函数
-- USER() 查询用户
-- 可以查看登录到 mysql 的有哪些用户,以及登录的 IP
SELECT USER() FROM DUAL; -- 用户@IP 地址
-- DATABASE() 查询当前使用数据库名称
SELECT DATABASE();
-- MD5(str) 为字符串算出一个 MD5 32 的字符串,常用(用户密码)加密
-- root 密码是 hsp -> 加密 md5 -> 在数据库中存放的是加密后的密码
SELECT MD5('sys') FROM DUAL; -- 36bcbb801f5052739af8220c6ea51434
-- 计算用 MD5 加密后的密码有多少位
SELECT LENGTH(MD5('sys')) FROM DUAL; -- 32
-- 演示用户表 ,存放密码时,是md5
CREATE TABLE sys_user
(id INT ,
`name` VARCHAR(32) NOT NULL DEFAULT '',
pwd CHAR(32) NOT NULL DEFAULT '');
INSERT INTO sys_user
VALUES(1,'孙裕松',MD5('sys'));
SELECT * FROM sys_user;
-- 当这样加密后,根据对应的名字和密码查是查不到的
-- 查出来的是一张空表
SELECT * FROM sys_user
WHERE `name` = '孙裕松' AND pwd = 'sys';
-- 密码要加上 md5 才能查询到
SELECT * FROM sys_user -- 涉及到SQL注入问题
WHERE `name` = '孙裕松' AND pwd = MD5('sys');
-- PASSWORD(str) -- 加密函数,
-- MySQL 数据库的用户密码就是 PASSWORD 函数加密
SELECT PASSWORD('sys') FROM DUAL; -- 数据库的 *BE353D0D7826681F8B7C136ED9824915F5B99E7D
-- select * from mysql.user \G 从原文密码 str 计算并返回密码字符串
-- 通常用于对 mysql 数据库的用户密码加密
-- mysql.user 表示 数据库.表
SELECT * FROM mysql.user;
先看如下需求
流程控制演示及上述问题的解决:
-- 演示流程控制函数
-- IF(expr1,expr2,expr3) 如果 expr1 为 True ,则返回 expr2 否则返回 expr3
SELECT IF(TRUE,'上海','深圳') FROM DUAL;
-- IFNULL(expr1,expr2) 如果 expr1 不为空 NULL,则返回 expr1,否则返回 expr2
SELECT IFNULL(NULL,'sys学java') FROM DUAL;
-- SELECT CASE WHEN expr1 THEN expr2 WHEN expr3 THEN expr4 ELSE expr5 END; [类似多重分支.]
-- 如果 expr1 为 TRUE,则返回 expr2,如果 expr3 为 t, 返回 expr4, 否则返回 expr5
SELECT CASE
WHEN TRUE THEN 'lucy'
WHEN FALSE THEN 'tom'
ELSE 'jack' END;
-- 1. 查询emp 表, 如果 comm 是null,则显示0.0
-- 正常查询
SELECT ename,comm
FROM emp;
-- 将 null 显示为 0.0 ;
-- 方法一:
-- 判断是否为 null 要使用 is null, 判断不为空 使用 is not
SELECT ename,IF(comm IS NULL,0.0,comm)
FROM emp;
-- 方法二:
SELECT ename,IFNULL(comm,0.0)
FROM emp;
-- 2. 如果emp 表的 job 是 CLERK 则显示 职员,
-- 3. 如果是 MANAGER 则显示经理
-- 4. 如果是 SALESMAN 则显示 销售人员,其它正常显示
SELECT ename,(CASE job
WHEN 'CLERK' THEN '职员'
WHEN 'MANAGER' THEN '经理'
WHEN 'SALESMAN' THEN '销售人员'
ELSE job END) AS job
FROM emp;
前面我们讲的mysql表的基本查询都是对一张表进行的查询,但是在实际的软件开发中,还远远的不够。
下面将使用前面创建 三张表 (emp,dept,salgrade) 为大家演示如何进行多表查询
如何查找1992.1.1后入职的员工?
%: 表示到多个字符_: 表示单个字符
如何显示首字符为S的员工姓名和工资?
如何显示第三个字符为大写O的所有员工的姓名和工资?
如何显示没有上级的雇员的情况
查询表结构 selectinc.sql
使用order by子句
如何按照工资的从低到高的顺序,显示雇员的信息?
按照部门号升序而雇员的工资降序排列,显示雇员信息?
-- 查询增强
-- 使用where子句
-- 如何查找1992.1.1后入职的员工?
-- 说明:在mysql中,日期类型可以直接比较,
-- 需要注意时间格式和表格中的格式一样
SELECT * FROM emp
WHERE hiredate > '1992-01-01';
-- 如何使用like操作符
-- %: 表示0到多个字符 _: 表示单个任意字符
-- 如何显示首字符为S的员工姓名和工资?
SELECT ename,sal FROM emp
WHERE ename LIKE 'S%'
-- 如何显示第三个字符为大写O的所有员工的姓名和工资?
SELECT ename,sal FROM emp
WHERE ename LIKE '__O%'
-- 如何显示没有上级的雇员的情况 mgl就是上级
SELECT * FROM emp
WHERE mgr IS NULL
-- 查询表结构
DESC emp
-- 使用order by子句
-- 如何按照工资的从低到高的顺序,显示雇员的信息?
SELECT * FROM emp
ORDER BY sal ASC
-- 按照部门号升序而雇员的工资降序排列,显示雇员信息?
SELECT * FROM emp
ORDER BY deptno ASC,sal DESC
按雇员的id号升序取出,每页显示3条记录,请分别显示 第1页,第2页,第3页
基本语法: select … limit start, rows
表示从start+1 行开始取,取出rows行,start 从0开始计算
公式:
练习
演示代码如下:
-- 演示分页查询
-- 1. 按雇员的id号升序取出,每页显示3条记录,请分别显示 第1页,第2页,第3页
-- 2. 基本语法: select ... limit start, rows
-- 表示从start+1 行开始取,取出rows行,start 从0开始计算
-- 第一页
SELECT * FROM emp
ORDER BY empno ASC
LIMIT 0,3
-- 第二页
SELECT * FROM emp
ORDER BY empno ASC
LIMIT 3,3
-- 第三页
SELECT * FROM emp
ORDER BY empno ASC
LIMIT 6,3
-- 推导一个公式
SELECT * FROM emp
ORDER BY empno ASC
LIMIT 每页显示的记录数*(第几页-1),每页显示的记录数
-- 1. 按雇员的empno号降序取出,每页显示5条记录。
SELECT * FROM emp
ORDER BY empno DESC
LIMIT 0,5
SELECT * FROM emp
ORDER BY empno DESC
LIMIT 5,5
SELECT * FROM emp
ORDER BY empno DESC
LIMIT 10,5
-- 2. 请分别显示 第3页,第5页 对应的sql语句
-- 第三页
SELECT * FROM emp
ORDER BY empno DESC
LIMIT 10,5
-- 第五页
SELECT * FROM emp
ORDER BY empno DESC
LIMIT 20,5
-- 增强 group by 的使用
-- 1. 显示每种岗位的雇员总数、平均工资
SELECT COUNT(*),AVG(sal) ,job
FROM emp
GROUP BY job
-- 2. 显示雇员总数,以及获得补助的雇员数
-- 获得补助的雇员数 就是 comm 列为非null
SELECT COUNT(*),COUNT(comm)
FROM emp;
-- 拓展:统计没有获得补助的雇员数
-- 写法一:
SELECT COUNT(*),COUNT(IF(comm IS NULL,1,NULL))
FROM emp;
-- 写法二:
SELECT COUNT(*),COUNT(*)-COUNT(comm)
FROM emp
-- 3. 显示管理者的总人数 也就是 mgr 里面的人
-- DISTINCT可选,指显示结果时,是否去掉重复数据
SELECT COUNT(DISTINCT mgr)
FROM emp;
-- 4. 显示雇员工资的最大差额
SELECT MAX(sal)-MIN(sal)
FROM emp;
如果select语句同时包含有group by ,having,limit , order by 那么他们的顺序是group by , having,order by , limit
-- 1. 请统计各个部门的平均工资,
-- 2. 并且是大于1000的
-- 3. 并且按照平均工资从高到低排序,
-- 4. 取出前两行记录.
-- 分析 各个部门 用到group by
-- 平均工资 用到avg
-- 大于1000 用到 having
-- 从高到低排序 用到 order by
-- 取出前两行记录 用到 limit
SELECT deptno,AVG(sal) AS avg_sal
FROM emp
GROUP BY deptno
HAVING avg_sal > 1000
ORDER BY avg_sal DESC
LIMIT 0,2
如上图所示,应该有一张商品表,有一张评论表;
从而引出了多表查询;
第一题初步分析:
SELECT *
FROM emp;
SELECT *
FROM dept;
SELECT *
FROM emp,dept;
由此我们发现当两个表查询时的规则为:
SELECT ename,sal,dname,emp.deptno
FROM emp,dept
WHERE emp.deptno = dept.deptno
因此可以看出剩下的题目也是如此解决
全部代码如下:
-- 多表查询
-- 1. 显示雇员名,雇员工资及所在部门的名字? [笛卡尔集]
-- 分析:
-- (1)雇员名,雇员工资 来自 emp 表
-- (2)部门的名字 来自 dept 表
-- (3)需求对 emp 和 dept 查询 ename,sal,dname,deptno
-- (4)当我们需要指定显示某个表的列是 表.列名
-- 查询emp表
SELECT *
FROM emp;
-- 查询dept表
SELECT *
FROM dept;
-- 尝试两个表一起查询
SELECT *
FROM emp,dept;
-- 将两个表合并的结果进行过滤
SELECT ename,sal,dname,emp.deptno
FROM emp,dept
WHERE emp.deptno = dept.deptno
-- 2. 小技巧:多表查询的条件不能少于 表的个数-1,否则会出现笛卡尔集
-- 3. 如何显示部门号为10的部门名、员工名和工资?
SELECT dname,sal,emp.deptno
FROM emp,dept
WHERE emp.deptno = dept.deptno AND emp.deptno = 10
-- 4. 显示各个员工的姓名,工资,及其工资的级别?
-- 查询工资等级
SELECT * FROM salgrade;
-- 用 WHERE sal BETWEEN losal AND hisal 来过滤
SELECT ename,sal,grade
FROM emp,salgrade
WHERE sal BETWEEN losal AND hisal
-- 5. 显示雇员名,雇员工资及所在部门的名字并按部门排序[降序排].
SELECT ename,sal,dname,emp.deptno
FROM emp,dept
WHERE emp.deptno = dept.deptno
ORDER BY dept.deptno DESC
介绍
自连接是指在同一张表的连接查询 [将同一张表看做两张表]
思考题: 显示公司员工和他的上级的名字
-- 我们现在给工人弄个别名 woker ,上级弄个别名为 boss
SELECT *
FROM emp worker,emp boss -- 169行 13*13
SELECT worker.ename AS '职员',boss.ename AS '上级'
FROM emp worker,emp boss
WHERE worker.mgr = boss.empno;-- 12行
完整的代码如下:
-- 多表查询的 自连接
-- 思考题: 显示公司员工和他的上级的名字
-- 先查询总表
SELECT * FROM emp;
-- 发现员工名字 在emp ,上级的名字 也在emp
-- 员工和上级是通过 emp的mgr列关联
-- 我们现在给工人弄个别名 woker ,上级弄个别名为 boss
SELECT *
FROM emp worker,emp boss -- 169行 13*13
-- 先筛选出员工的名字和上级的名字
-- 然后经过where过滤掉重复的
-- 当员工上级的编号 = 上级的编号
SELECT worker.ename AS '职员',boss.ename AS '上级'
FROM emp worker,emp boss
WHERE worker.mgr = boss.empno;-- 12行
-- 自连接的特点
-- 1.把同一张表当做两张表来使用
-- 2.需要给表取别名,要不然会显示不清晰
-- 别名格式:表名 表别名
-- 3. 列名不明确的话,可以指定列的别名
-- 列的别名: 列名 as 列的别名
子查询是指嵌入在其它 sql 语句中的 select 语句,也叫嵌套查询
单行子查询是指只返回一行数据的子查询语句
请思考:如何显示与SMITH 同一部门的所有员工?
-- 演示子查询
-- 请思考:如何显示与SMITH 同一部门的所有员工?
/*
1. 先查询到 SMITH 的部门号
2. 然后把查询 SMITH 部门号的语句当成一个子查询
*/
SELECT deptno
FROM emp
WHERE ename = 'SMITH'
SELECT *
FROM emp
WHERE deptno = (
SELECT deptno
FROM emp
WHERE ename = 'SMITH'
)
多行子查询指返回多行数据的子查询 使用关键字 in
练习:
如何查询和部门10的工作相同的雇员的名字、岗位、工资、部门号,但是不含10号部门自己的.
思路:
-- 工作可能会有重叠的,用 distinct去掉重叠的信息
SELECT DISTINCT job
FROM emp
WHERE deptno = 10
查询结果如下:
-- 2.把上面的10号部门的job作为多行子查询
SELECT ename,job,sal,deptno
FROM emp
WHERE job IN (
SELECT DISTINCT job
FROM emp
WHERE deptno = 10
) AND deptno != 10
查询结果如下:
练习题 subquery.sql
查询ecshop中各个类别中,价格最高的商品
思路:
第一步:
SELECT * FROM ecs_goods
第二步:
SELECT goods_id,cat_id,goods_name,shop_price
FROM ecs_goods;
第三步:
SELECT cat_id,MAX(shop_price)
FROM ecs_goods
GROUP BY cat_id
然后我们将第三步的子查询表先单独当做一张表,用于跟第二步的表格进行对比查找
第四步:
SELECT goods_id,ecs_goods.cat_id,goods_name,shop_price
FROM (
SELECT cat_id,MAX(shop_price) AS max_price
FROM ecs_goods
GROUP BY cat_id
)temp,ecs_goods
WHERE temp.cat_id = ecs_goods.cat_id
AND temp.max_price = ecs_goods.shop_price;
我们最终需要的查询结果如下:
请思考:显示工资比部门30的所有员工的工资高的员工的姓名、工资和部门号
-- 请思考:显示工资比部门30的所有员工的工资高的员工的姓名、工资和部门号
SELECT ename,sal,deptno
FROM emp
WHERE sal > ALL(
SELECT sal
FROM emp
WHERE deptno = 30)
-- 还有一种写法
SELECT ename,sal,deptno
FROM emp
WHERE sal > ALL(
SELECT MAX(sal)
FROM emp
WHERE deptno = 30)
请思考:如何显示工资比部门30的其中一个员工的工资高的员工的姓名、工资和部门号
-- 请思考:如何显示工资比部门30的其中一个员工的工资高的员工的姓名、工资和部门号
SELECT ename,sal,deptno
FROM emp
WHERE sal > ANY(
SELECT sal
FROM emp
WHERE deptno = 30)
-- 另一种写法
SELECT ename,sal,deptno
FROM emp
WHERE sal > ANY(
SELECT MIN(sal)
FROM emp
WHERE deptno = 30)
多列子查序则是指查询返回多个列数据的子查询语句
请思考:如何查询与allen的部门和岗位完全相同的所有雇员(并且不含allen本人)
(字段1,字段2 …) = (select 字段 1,字段2 from 。。。。)
思路:
-- 先查询Allen的信息
SELECT deptno,job
FROM emp
WHERE ename = 'ALLEN'
-- 然后查询与Allen部门和岗位完全相同的所有雇员
-- 并且排除Allen本人
SELECT *
FROM emp
WHERE (deptno,job)=(
SELECT deptno,job
FROM emp
WHERE ename = 'ALLEN'
)AND ename!='ALLEN'
和宋江数学,英语,语文成绩 完全相同的学生
与上面的写法类似
-- 和赵云数学,英语,语文成绩 完全相同的学生
SELECT *
FROM student
WHERE (math,english,chinese)=(
SELECT math,english,chinese
FROM student
WHERE `name` = '赵云')
请思考: 查找每个部门工资高于本部门平均工资的人的资料
思路:
SELECT deptno,AVG(sal) AS avg_sal
FROM emp
GROUP BY deptno
SELECT ename,temp.avg_sal,sal,emp.deptno
FROM (
SELECT deptno,AVG(sal) AS avg_sal
FROM emp
GROUP BY deptno)temp,emp
WHERE emp.deptno = temp.deptno
AND emp.sal > temp.avg_sal
查询每个部门的信息(包括: 部门名,编号,地址)和人员数量,
思路
SELECT deptno,MAX(sal)AS max_sal
FROM emp
GROUP BY deptno
SELECT ename,temp.max_sal,sal,emp.deptno
FROM (SELECT deptno,MAX(sal)AS max_sal
FROM emp
GROUP BY deptno)temp,emp
WHERE emp.deptno = temp.deptno
AND emp.sal = temp.max_sal
查询每个部门的信息(包括: 部门名,编号,地址)和人员数量。
思路:
SELECT COUNT(*),deptno
FROM emp
GROUP BY deptno
SELECT dname,dept.deptno,loc,tmp.per_num
FROM dept,(
SELECT COUNT(*)AS per_num,deptno
FROM emp
GROUP BY deptno
)tmp
WHERE dept.deptno = tmp.deptno
SELECT tmp.*,dname,loc
FROM dept,(
SELECT COUNT(*)AS per_num,deptno
FROM emp
GROUP BY deptno
)tmp
WHERE dept.deptno = tmp.deptno
有时,为了对某个sql语句进行效率测试,我们需要海量数据时,可以使用此法为表创建海量数据。
简单代码操作:
-- 表的复制
-- 为了对某个sql语句进行效率测试,我们需要海量数据时,可以使用此法为表创建海量数据。
-- 先创建一个表格
CREATE TABLE my_table01
( id INT,
`name` VARCHAR(32),
sal DOUBLE,
job VARCHAR(32),
deptno INT)
DESC my_table01;
SELECT * FROM my_table01;
-- 演示如何自我复制
-- 1. 先把 emp 表记录复制到 my_table01
INSERT INTO my_table01
(id,`name`,sal,job,deptno)
SELECT empno,ename,sal,job,deptno FROM emp
-- 2. 自我复制
INSERT INTO my_table01
SELECT * FROM my_table01;
-- 查询一下
SELECT COUNT(*) FROM my_table01;
思考题:如何删除掉一张表重复记录
思路分析:
先创建一张表 my_table02
这个语句 把emp表的结构(列),复制到my_table02
CREATE TABLE my_table02 LIKE emp;
让 my_table02 有重复记录
INSERT INTO my_table02
SELECT * FROM emp;
考虑去重 my_table02 的记录(这里用的是最原始的步骤,实际上可以不用这么麻烦).
思路:
(1)先创建一张临时表 my_tmp , 该表的结构和 my_tab02 一样
(2)把 my_tmp 的记录 通过 distinct 关键字 处理后 把记录复制到 my_tmp
(3)清除掉 my_tab02 记录
(4)把 my_tmp 表的记录复制到 my_tab02
(5)drop 掉 临时表 my_tmp
代码如下:
-- (1)先创建一张临时表 my_tmp , 该表的结构和 my_tab02 一样
CREATE TABLE my_tmp LIKE my_table02;
-- (2)把 my_tmp 的记录 通过 distinct 关键字 处理后 把记录复制到 my_tmp
INSERT INTO my_tmp
SELECT DISTINCT * FROM my_table02
-- (3)清除掉 my_tab02 记录
DELETE FROM my_table02
-- (4)把 my_tmp 表的记录复制到 my_tab02
INSERT INTO my_table02
SELECT * FROM my_tmp
-- (5)drop 掉 临时表 my_tmp
DROP TABLE my_tmp
有时在实际应用中,为了合并多个select语句的结果,可以使用集合操作符号union , union all
select ename,sal,job from emp where sal>2500 union
select ename,sal,job from emp where job='MANAGER
SELECT ename,sal,job FROM emp WHERE sal>2500
SELECT ename,sal,job FROM emp WHERE job='MANAGER'
-- 现在我们将上面两个查询合并,有两种方法
-- union all 就是将两个查询结果合并,不会去重
SELECT ename,sal,job FROM emp WHERE sal>2500
UNION ALL
SELECT ename,sal,job FROM emp WHERE job='MANAGER'
-- union 就是将两个查询结果合并,会去重
SELECT ename,sal,job FROM emp WHERE sal>2500
UNION
SELECT ename,sal,job FROM emp WHERE job='MANAGER'
-- 比如:列出部门名称和这些部门的员工名称和工作,
-- 同时要求 显示出那些没有员工的部门。
SELECT dname,ename,job
FROM emp,dept
WHERE emp.deptno = dept.deptno
-- 以上操作只能显示第一个要求,
-- 但是显示不了没有员工的部门
-- 我们使用外连接
SELECT dname,ename,job
FROM dept LEFT JOIN emp
ON emp.deptno = dept.deptno
-- 这样我们就能同时满足两个要求了
思路:
-- 创建 stu
/*
id name
1 Jack
2 Tom
3 Kity
4 nono
*/
CREATE TABLE stu
(id INT,
`name` VARCHAR(32)
)
INSERT INTO stu
VALUES(1,'jack'),(2,'tom'),
(3,'kity'),(4,'nono');
SELECT * FROM stu
DELETE FROM stu
-- 创建 exam
/*
id grade
1 56
2 76
11 8
*/
CREATE TABLE exam
(id INT,
grade INT)
INSERT INTO exam
VALUES(1,56),(2,76),
(11,8);
SELECT * FROM exam
使用左外连接 (显示所有人的成绩,如果没有成绩,也要显示该人的姓名和id号,成绩显示为空)
select … from 表1 left join 表2 on条件 [表1:就是左表 表2: 就是右表]
-- 使用左外连接 (显示所有人的成绩,如果没有成绩,也要显示该人的姓名和id号,成绩显示为空)
SELECT stu.id,`name`,grade
FROM stu LEFT JOIN exam
ON stu.id = exam.id
使用右外连接(显示所有成绩如果没有名字匹配,显示空)
select … from 表1 right join 表2 on 条件 [表1: 就是左表 表2:就是右表]
-- 使用右外连接(显示所有成绩如果没有名字匹配,显示空)
SELECT stu.id,`name`,grade
FROM exam RIGHT JOIN stu
ON stu.id = exam.id
约束用于确保数据库的数据满足特定的商业规则。
在mysql中,约束包括: not null、uniqueprimary key,foreign key,和check 五种
用于唯一的标示表行的数据,当定义主键约束后,该列不能重复
案例演示:
-- 主键使用 primary key
CREATE TABLE t16(
id INT PRIMARY KEY,
`name` VARCHAR(32),
sex VARCHAR(6))
-- -- 主键列的值是不可以重复
INSERT INTO t16
VALUES(1,'jack','man')
INSERT INTO t16
VALUES(2,'lucy','woman')
-- id与第一条重复,添加失败
INSERT INTO t16
VALUES(1,'mike','man')
-- primary key(主键)-细节说明
--
-- 1. primary key不能重复而且不能为null。
INSERT INTO t16(NULL,'mike','man') -- 不可以
-- 2. 一张表最多只能有一个主键,但可以是复合主键
-- 3. 主键的指定方式有两种
-- 1. 直接在字段名后指定:字段名 primakry key
-- 2. 在表定义最后写 primary key(列名);
CREATE TABLE t17(
id INT,
`name` VARCHAR(32),
sex VARCHAR(6),
PRIMARY KEY(id,`name`))
-- 这时候的id跟name是复合主键
INSERT INTO t17
VALUES(1,'jack','man')
INSERT INTO t17
VALUES(2,'lily','woman')
INSERT INTO t17
VALUES(1,'jack','man')-- 这里就违反了复合主键
SELECT * FROM t17;
-- 4. 使用desc 表名,可以看到primary key的情况
DESC t16
DESC t17
-- 5. 在实际开发中,每个表往往都会设计一个主键
unique 细节(注意):
简单代码演示:
-- unique(唯一)
CREATE TABLE t18(
id INT UNIQUE,-- 表示 id 列是不可以重复的.
`name` VARCHAR(32),
sex VARCHAR(6))
INSERT INTO t18
VALUES(1,'lisa','woman')
INSERT INTO t18
VALUES(2,'tom','man')
INSERT INTO t18
VALUES(1,'jack','man')-- 添加失败
SELECT * FROM t18
-- unqiue 使用细节
-- 1. 如果没有指定 not null , 则 unique 字段可以有多个 null
-- 如果一个列(字段), 是 unique not null 使用效果类似 primary key
INSERT INTO t18
VALUES(NULL,'smith','man')
INSERT INTO t18
VALUES(NULL,'jack','man')
-- 2. 一张表可以有多个 unique 字段
CREATE TABLE t19(
id INT UNIQUE,-- 表示 id 列是不可以重复的.
`name` VARCHAR(32) UNIQUE,-- 表示name列是不可以重复的
sex VARCHAR(6))
DESC t19
-- foreign key(外键)
-- 创建 主表 my_class
CREATE TABLE my_class(
id INT PRIMARY KEY,
`name` VARCHAR(32)NOT NULL DEFAULT'',
`add` VARCHAR(32)NOT NULL DEFAULT'')
-- 创建 从表 my_stu
CREATE TABLE my_stu(
id INT PRIMARY KEY,
`name` VARCHAR(32)NOT NULL DEFAULT'',
class_id INT,
-- 下面指定外键关系
FOREIGN KEY(class_id) REFERENCES my_class(id))
-- 测试数据
INSERT INTO my_class
VALUES(100, 'java','北京'), (200, 'web','上海');
INSERT INTO my_class
VALUES(300, 'php','深圳');
SELECT * FROM my_class
-- 给 my_stu 填入数据
INSERT INTO my_stu
VALUES(1,'tom', 100);
INSERT INTO my_stu
VALUES(2,'jack', 200);
INSERT INTO my_stu
VALUES(3, 'hsp', 300);
-- 这里会失败...因为 400 班级不存在
INSERT INTO my_stu
VALUES(4, 'mary', 400);
SELECT * FROM my_stu
-- 这里可以, 因为外键没有写 not null
INSERT INTO my_stu
VALUES(5, 'king', NULL);
-- 一旦建立主外键的关系,数据不能随意删除了
DELETE FROM my_class
WHERE id = 100;
-- 演示 check 的使用
-- mysql5.7 目前还不支持 check ,只做语法校验,但不会生效
-- 了解
-- 学习 oracle, sql server, 这两个数据库是真的生效.
-- 测试
CREATE TABLE t20 (
id INT PRIMARY KEY,
`name` VARCHAR(32) ,
sex VARCHAR(6) CHECK (sex IN('man','woman')), -- 性别限制为男或者女
sal DOUBLE CHECK ( sal > 1000 AND sal < 2000) -- 工资限制在1000~2000
);
-- 添加数据
-- mysql5.7 目前还不支持 check ,只做语法校验,但不会生效
INSERT INTO t20
VALUES(1, 'jack', 'mid', 1);
SELECT * FROM t20;
现有一个商店的数据库shop_db,记录客户及其购物情况,
由下面三个表组成:
建表,在定义中要求声明 [进行合理设计]:
(1)每个表的主外键;
(2)客户的姓名不能为空值;
(3)电邮不能够重复;
(4)客户的性别[男|女] check 枚举.
(5)单价unitprice 在 1.0 - 9999.99 之间 check
-- 1. 现有一个商店的数据库shop_db,记录客户及其购物情况,
CREATE DATABASE shop_db;
-- 2. 由下面三个表组成:
-- 1. 商品goods (商品号goods_id,商品名goods_name,单价unitprice,商品类别category,供应商provider);
CREATE TABLE goods
(goods_id INT PRIMARY KEY,
goods_name VARCHAR(64)NOT NULL DEFAULT '',
unitprice DECIMAL(10,2)NOT NULL DEFAULT 0 ,
CHECK(unitprice >= 1.0 AND unitprice <= 9999.99),
category INT NOT NULL DEFAULT 0,
provider VARCHAR(64)UNIQUE)
-- 2. 客户customer (客户号customer_id,姓名name,住址address,电邮email性别sex,身份证card_Id);
CREATE TABLE customer
(customer_id INT PRIMARY KEY,
`name` VARCHAR(64)NOT NULL DEFAULT'',
address VARCHAR(64)NOT NULL DEFAULT'',
email VARCHAR(64) UNIQUE,
-- sex VARCHAR(6)CHECK(sex IN('man','woman')),
-- 这里使用枚举类型
sex ENUM('男','女') NOT NULL,
card_id CHAR(18)
)
-- 3. 购买purchase (购买订单号order_id,客户号customer_id,商品号goods_id,购买数量nums);
CREATE TABLE purchase
(order_id INT UNSIGNED PRIMARY KEY,-- unsigned 正数
customer_id INT NOT NULL DEFAULT 0,
goods_id INT NOT NULL DEFAULT 0,
nums INT NOT NULL DEFAULT 0,
FOREIGN KEY(customer_id)REFERENCES customer(customer_id),
FOREIGN KEY(goods_id)REFERENCES goods(goods_id))
-- 3. 建表,在定义中要求声明 [进行合理设计]:
-- (1)每个表的主外键;
-- (2)客户的姓名不能为空值;
-- (3)电邮不能够重复;
-- (4)客户的性别[男|女] check 枚举.
-- (5)单价unitprice 在 1.0 - 9999.99 之间 check
DESC goods;
DESC customer;
DESC purchase;
我们先看一个问题:
在某张表中,存在一个id列(整数),我们希望在添加记录的时候,该列从1开始,自动的增长,怎么处理?
那么我们就用到了自增长,基本语法如下:
添加 自增长的字段方式
insert into xxx (字段1, 字段2…) values(null,值…);
insert into xxx (字段2…) values(值1,值2’…);
insert into xxx values(null, 值1’…)
-- 自增长
-- 在某张表中,存在一个id列(整数),我们希望在添加记录的时候,该列从1开始,自动的增长,怎么处理?
CREATE TABLE t21(
id INT PRIMARY KEY AUTO_INCREMENT,
email VARCHAR(32) NOT NULL DEFAULT '',
`name` VARCHAR(32) NOT NULL DEFAULT '')
DESC t21
-- 测试自增长的使用
-- 添加 自增长的字段方式
-- insert into xxx (字段1, 字段2.....) values(null,值....);
INSERT INTO t21(id,email,`name`)
VALUES(NULL,'1241955497@qq.com','sys')
-- insert into xxx (字段2.....) values(值1,值2'....);
INSERT INTO t21(email,`name`)
VALUES('jack@qq.com','jack')
-- insert into xxx values(null, 值1'......)
INSERT INTO t21
VALUES(NULL,'lily@qq.com','lily');
SELECT * FROM t21
一般来说自增长是和primary key 配合使用的
自增长也可以单独使用[但是需要配合一个unique]
自增长修饰的字段为整数型的(虽然小数也可以但是非常非常少这样使用)
自增长默认从 1开始,你也可以通过如下命令修改
alter table 表名 auto increment = 新的开始值;
如果你添加数据时,给自增长字段(列)指定的有值,则以指定的值为准;
如果指定了自增长,一般来说,就按照自增长的规则来添加数据.
-- 修改默认的自增长的开始值
-- alter table 表名 auto increment = 新的开始值;
ALTER TABLE t22 AUTO_INCREMENT = 100;
CREATE TABLE t22(
id INT PRIMARY KEY AUTO_INCREMENT,
email VARCHAR(32) NOT NULL DEFAULT '',
`name` VARCHAR(32) NOT NULL DEFAULT '')
INSERT INTO t22
VALUES(NULL,'tom@qq.com','tom')
-- 如果你添加数据时,给自增长字段(列)指定的有值,则以指定的值为准;
-- 如果指定了自增长,一般来说,就按照自增长的规则来添加数据.
INSERT INTO t22
VALUES(666,'jack@qq.com','jack')
SELECT * FROM t22;
-- 索引
-- 创建测试数据库 tmp
CREATE DATABASE tmp;
-- 下面是海量数据表,只为了测试索引用,先不用理解
CREATE TABLE dept( /*部门表*/
deptno MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,
dname VARCHAR(20) NOT NULL DEFAULT "",
loc VARCHAR(13) NOT NULL DEFAULT ""
) ;
#创建表EMP雇员
CREATE TABLE emp
(empno MEDIUMINT UNSIGNED NOT NULL DEFAULT 0, /*编号*/
ename VARCHAR(20) NOT NULL DEFAULT "", /*名字*/
job VARCHAR(9) NOT NULL DEFAULT "",/*工作*/
mgr MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,/*上级编号*/
hiredate DATE NOT NULL,/*入职时间*/
sal DECIMAL(7,2) NOT NULL,/*薪水*/
comm DECIMAL(7,2) NOT NULL,/*红利*/
deptno MEDIUMINT UNSIGNED NOT NULL DEFAULT 0 /*部门编号*/
) ;
#工资级别表
CREATE TABLE salgrade
(
grade MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,
losal DECIMAL(17,2) NOT NULL,
hisal DECIMAL(17,2) NOT NULL
);
#测试数据
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);
DELIMITER $$
#创建一个函数,名字 rand_string,可以随机返回我指定的个数字符串
CREATE FUNCTION rand_string(n INT)
RETURNS VARCHAR(255) #该函数会返回一个字符串
BEGIN
#定义了一个变量 chars_str, 类型 varchar(100)
#默认给 chars_str 初始值 'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ'
DECLARE chars_str VARCHAR(100) DEFAULT
'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ';
DECLARE return_str VARCHAR(255) DEFAULT '';
DECLARE i INT DEFAULT 0;
WHILE i < n DO
# concat 函数 : 连接函数mysql函数
SET return_str =CONCAT(return_str,SUBSTRING(chars_str,FLOOR(1+RAND()*52),1));
SET i = i + 1;
END WHILE;
RETURN return_str;
END $$
#这里我们又自定了一个函数,返回一个随机的部门号
CREATE FUNCTION rand_num( )
RETURNS INT(5)
BEGIN
DECLARE i INT DEFAULT 0;
SET i = FLOOR(10+RAND()*500);
RETURN i;
END $$
#创建一个存储过程, 可以添加雇员
CREATE PROCEDURE insert_emp(IN START INT(10),IN max_num INT(10))
BEGIN
DECLARE i INT DEFAULT 0;
#set autocommit =0 把autocommit设置成0
#autocommit = 0 含义: 不要自动提交
SET autocommit = 0; #默认不提交sql语句
REPEAT
SET i = i + 1;
#通过前面写的函数随机产生字符串和部门编号,然后加入到emp表
INSERT INTO emp VALUES ((START+i) ,rand_string(6),'SALESMAN',0001,CURDATE(),2000,400,rand_num());
UNTIL i = max_num
END REPEAT;
#commit整体提交所有sql语句,提高效率
COMMIT;
END $$
#添加8000000数据
CALL insert_emp(100001,8000000)$$
#命令结束符,再重新设置为;
DELIMITER ;
-- 在没有创建索引时,我们随便查询一条记录
-- 花费了7.380s,效率很低
SELECT *
FROM emp
WHERE empno = 6666666
-- 现在我们使用索引来优化一下,体验一下索引的好处
-- 在没有创建索引前,emp.idb文件大小是512m
-- 创建索引后,emp.idb文件大小是640m
-- 由此可以看出索引本身也会占用空间
-- empno_index是索引名称
-- ON emp(empno):表示在emp表的empno列创建索引
CREATE INDEX empno_index ON emp(empno)
-- 创建索引后,我们还是查询一样的记录
-- 只花费了0.001s,效率提升特别大
SELECT *
FROM emp
WHERE empno = 6666666
但是创建索引后,只对创建了索引的列有效,当我们查询其他列的时候,还是会很慢
所以我们创建一个索引其实对整体是没有很大作用的
没有索引的时候扫描为什么会慢?
因为没有索引的时候,会进行全表扫描,所以查询速度很慢
使用索引为什么会快?
当创建了索引以后形成一个索引的数据结构,比如二又树索引的代价
在我们项目中,select用的比较多[占比90%],update,delete,insert用的比较少[占比10%]
主键索引,主键自动的为主索引(类型Primary key)
create table t1
(id int primary key,-- 主键,同时也是索引,称为主键索引
name varchar(32));
唯一索引(UNIQUE)
create table t2
(id int unique,-- id是唯一的,同时也是索引,称为unique索引
name varchar(32));
普通索引(INDEX)
全文索引(FULLTEXT) [适用于MyISAM]
一般开发,不使用 mysql 自带的全文索引,而是使用:全文搜索 Solr 和 ElasticSearch (ES)
添加索引→主要分为三种:
-- 先创建一个表用于测试 id ,name
CREATE TABLE t23(
id INT,
`name` VARCHAR(32));
-- 查询表是否有索引,没有索引
SHOW INDEXES FROM t23;
-- 添加索引
-- 1. 添加唯一索引
-- create [UNIQUE] index index_name on tbl name (col_name [(length)]
-- [ASC | DESC] , .....);
CREATE UNIQUE INDEX id_index ON t23(id);
-- 查询表是否有索引,查到有索引
-- 注意:这里显示的0代表唯一索引,1代表不是唯一索引
SHOW INDEXES FROM t23;
-- 2. 添加普通索引
-- 添加普通索引方式1
CREATE INDEX id_index ON t23(id);
-- 添加普通索引方式2
-- alter table table_name ADD INDEX [index_name] (index_col_name....)
ALTER TABLE t23 ADD INDEX id_index(id)
-- 如何选择唯一索引和普通索引
-- 如果某列的值,是不会重复的,则优先考虑unique索引,否则使用普通索引
-- 3. 添加主键(索引)
-- ALTER TABLE 表名 ADD PRIMARY KEY(列名...);
-- 先创建一个表用于测试 id ,name
CREATE TABLE t24(
id INT,
`name` VARCHAR(32));
ALTER TABLE t24 ADD PRIMARY KEY(id);
-- 查询表是否有索引,没有索引
SHOW INDEXES FROM t24;
删除索引→主要分为两种:
-- 删除索引
-- DROP INDEX index_name ON tbl_name
-- alter table table_name drop index index_name;
DROP INDEX id_index ON t23
-- 删除主键索引 比较特别:
-- alter table t_b drop primary key;
ALTER TABLE t24 DROP PRIMARY KEY;
修改索引:就是把索引删除,然后再添加索引
查询索引→主要分为三种:
-- 查询索引(三种方式)
-- show index(es) from table_name;
SHOW INDEX FROM t23;
SHOW INDEXES FROM t23;
-- show keys from table_name;
SHOW KEYS FROM t23;
-- desc table_Name;
DESC t23;
要求:
创建一张订单表order (id号,商品名,订购人,数量).
要求id号为主键,请使用2种方式来创建主键.(order1,order2 )
-- 方式一
CREATE TABLE order1(
id INT PRIMARY KEY,
produce_name VARCHAR(32)NOT NULL DEFAULT '',
`order` VARCHAR(32)NOT NULL DEFAULT '',
num INT);
SHOW INDEX FROM order1;
CREATE INDEX id_index ON order1(id);
-- 方式二
CREATE TABLE order2(
id INT NOT NULL DEFAULT 0,
produce_name VARCHAR(32)NOT NULL DEFAULT '',
`order` VARCHAR(32)NOT NULL DEFAULT '',
num INT);
SHOW INDEX FROM order2;
ALTER TABLE order2 ADD PRIMARY KEY(id);
要求:
创建一张特价菜谱表menu (id号,菜谱名,厨师,点餐人身份证,价格).
要求id号为主键,点餐人身份证是unique 请使用两种方式来创建unique.(menu1,menu2)
-- 方式一
CREATE TABLE menu1(
id INT UNIQUE,
menu_name VARCHAR(32)NOT NULL DEFAULT '',
chef_name VARCHAR(32)NOT NULL DEFAULT '',
card_id CHAR(18),
price DOUBLE )
SHOW KEYS FROM menu1;
CREATE INDEX id_index ON menu1(id);
-- 方式二
CREATE TABLE menu2(
id INT ,
menu_name VARCHAR(32)NOT NULL DEFAULT '',
chef_name VARCHAR(32)NOT NULL DEFAULT '',
card_id CHAR(18),
price DOUBLE )
SHOW KEYS FROM menu2;
CREATE UNIQUE INDEX id_index ON menu2(id);
要求:
创建一张运动员表sportman (id号,名字,特长).要求id号为主键名字为普通索引,
请使用2种方式来创建索引( sportman1,sportman2)
-- 方式一
CREATE TABLE sportman1(
id INT PRIMARY KEY,
`name` VARCHAR(32) NOT NULL DEFAULT '',
strong_point VARCHAR(32) NOT NULL DEFAULT '');
SHOW INDEX FROM sportman1;
CREATE INDEX id_index ON sportman1(id);
CREATE INDEX name_index ON sportman1(`name`);
-- 方式二
CREATE TABLE sportman2(
id INT ,
`name` VARCHAR(32) NOT NULL DEFAULT '',
strong_point VARCHAR(32) NOT NULL DEFAULT '');
SHOW INDEX FROM sportman2;
ALTER TABLE sportman2 ADD PRIMARY KEY(id);
CREATE INDEX name_index ON sportman2(`name`);
较频繁的作为查询条件字段应该创建索引
select * from emp where empno = 1
唯一性太差的字段不适合单独创建索引,即使频繁作为查询条件
select * from emp where sex = ‘男’
更新非常频繁的字段不适合创建索引
select * from emp where logincount = 1
不会出现在WHERE子句中字段不该创建索引
事务用于保证数据的一致性,它由一组相关的dml语句组成,该组的dml语句要么全部成功,要么全部失败。
例如:转账就要用事务来处理,用以保证数据的一致性。
当执行事务操作时(dml语句),mysql会在表上加锁,防止其它用户改表的数据这对用户来讲是非常重要的
mysql 数据库控制台事务的几个重要操作:
start transaction – 开始一个事务
savepoint 保存点名 – 设置保存点
rollback to 保存点名 – 回退事务
rollback – 回退全部事务
commit – 提交事务所有的操作生效不能回退
-- 事物的一个重要概念和具体操作
-- mysql 数据库控制台事务的几个重要操作:
-- start transaction -- 开始一个事务
-- savepoint 保存点名 -- 设置保存点
-- rollback to 保存点名 -- 回退事务
-- rollback -- 回退全部事务
-- commit -- 提交事务所有的操作生效不能回退
-- 演示
-- 1. 创建一张测试表
CREATE TABLE t25(
id INT,
`name`VARCHAR(32));
-- 2. 开启事务
START TRANSACTION
-- 3. 设置保存点
SAVEPOINT a
-- 4. 执行dml操作
INSERT INTO t25 VALUES(1,'jack');
-- 5. 设置保存点
SAVEPOINT b
-- 6. 执行dml操作
INSERT INTO t25 VALUES(2,'lucy');
-- 7. 回退到b
ROLLBACK TO b;
-- 8. 回退到a
ROLLBACK TO a;
-- 如果这样操作,那么将会回到事物最初的起点
ROLLBACK
-- 当执行了这个操作以后,之前所有的保存点都会清除
-- 不能再进行回退,所以要谨慎使用
COMMIT
-- 查询表格
SELECT * FROM t25
如果不开始事务,默认情况下,dml操作是自动提交的,不能回滚
如果开始一个事务,你没有创建保存点.你可以执行 rollback,默认就是回退到你事务开始的状态.
你也可以在这个事务中(还没有提交时),创建多个保存点.
比如: savepoint aaa: 执行 dml, savepoint bbb;
你可以在事务没有提交前,选择回退到哪个保存点.
mysql的事务机制需要innodb的存储引擎才可以使用,myisam不好使
开始一个事务 start transaction,set autocommit=off;
-- 讨论事物细节
-- 1. 如果不开始事务,默认情况下,dml操作是自动提交的,不能回滚
INSERT INTO t25 VALUES(3,'lisa')
SELECT * FROM t25
-- 2. 如果开始一个事务,你没有创建保存点.你可以执行 rollback,
-- 默认就是回退到你事务开始的状态.
START TRANSACTION
INSERT INTO t25 VALUES(4,'milan')
INSERT INTO t25 VALUES(5,'duck')
ROLLBACK -- 表示直接回退到事物开始的状态
COMMIT
-- 3. 你也可以在这个事务中(还没有提交时),创建多个保存点.
-- 比如: savepoint aaa: 执行 dml, savepoint bbb;
-- 4. 你可以在事务没有提交前,选择回退到哪个保存点.
-- 5. mysql的事务机制需要innodb的存储引擎才可以使用,myisam不好使
-- InnoDB 存储引擎支持事务,MyISAM不支持
-- 6. 开始一个事务 start transaction,set autocommit=off;
SET autocommit = off;
概念:Mysql隔离级别定义了事务与事务之间的隔离程度。
具体的见下表:
我们举例一个案例来说明mysql的事务隔离级别以对account表进行操作为例。(id,name,money)
我们就以读未提交为例,做一个演示:
先打开两个mysql的控制台
查看当前mysql的隔离级别
操作指令:SELECT @@tx_isolation;
查到我们的事务隔离级别是可重复读
/*
mysql> SELECT @@tx_isolation;
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+
*/
接下来我们把其中一个控制台隔离级别设置 Read uncommitted(读未提交)
操作指令:SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
/*
mysql> SELECT @@tx_isolation;
+------------------+
| @@tx_isolation |
+------------------+
| READ-UNCOMMITTED |
+------------------+
*/
两个控制台都开始事物
START TRANSACTION;
接下来我们在可重复读隔离级别的控制台里面创建表
CREATE TABLE `account`(
id INT,
`name`VARCHAR(32),
money INT);
这时候我们在可重复读隔离级别的控制台里面添加一条数据
INSERT INTO `account`
VALUES(100,'jcak',1000);
然后在读未提交隔离级别的控制台里搜索表account
能看到添加进去的数据,这就是脏读
这时候我们在可重复读隔离级别的控制台里面修改一下数据
UPDATE `account` SET money = 800 WHERE id = 100;
我们再添加一个数据
INSERT INTO `account`
VALUES(200,'tom',2000);
然后我们提交数据
COMMIT;
然后我们在读未提交隔离级别的控制台里搜索表account,能看到新的数据
能看到修改的数据,这就是不可重复读;
也能看到新加入的数据,这就是幻读;
综上所述,其他的事物隔离级别也是相同的道理,具体参照上面那张事务隔离级别表格,这里就不做过多详述;
当另一个窗口是读已提交事物级别的时候,将不会出现脏读,但是还会出现不可重复读和幻读
当另一个窗口是可重复读事物级别的时候,将不会出现脏读,不可重复读和幻读
当另一个窗口是可串行化事物级别的时候,将不会出现脏读,不可重复读和幻读,
而且当操作的那个窗口没提交的时候,可串行化的窗口会被锁在那里,不能进行操作;
查看当前会话隔离级别
select @@tx isolation;
查看系统当前隔离级别
select @@global.tx isolation;
设置当前会话隔离级别
set session transaction isolation level repeatable read;
设置系统当前隔离级别4.
set global transaction isolation level repeatable read;
mysql 默认的事务隔离级别是 repeatable read,
一般情况下,没有特殊要求,没有必要修改 (因为该级别可以满足绝大部分项目需求)
但是如果你非要修改默认的事务隔离级别的话,不用每次都修改,可以修改my.ini配置文件,
在my.ini配置文件最后加上你要设置的事物级别:
READ-UNCOMMITTED,读未提交
READ-COMMITTED,读已提交
REPEATABLEREAD, 可重复读
SERIALIZABLE.可串行化
在里面加入指令即可,指令为:transaction-isolation = [你要设置的事物级别]
原子性 (Atomicity)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
一致性 (Consistency)
事务必须使数据库从一个一致性状态变换到另外一个一致性状态
隔离性 (lsolation)
事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
持久性 (Durability)
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响
解题思路:
先打开三个mysql控制台
进入mysql -> 进入数据库 sys_db02
mysql -u root -p
USE sys_db02
把控制台B设置为 读未提交
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
把控制台C设置为 读已提交
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
A控制台创建表 dog (id,name),开始一个事务,添加两条记录;
创建dog表
CREATE TABLE dog(
id INT,
`name` VARCHAR(32));
三个控制台开始事物
START TRANSACTION;
添加两条记录
INSERT INTO dog VALUES(1,'皮皮');
INSERT INTO dog VALUES(2,'大黄');
在控制台b搜索dog表,发现是加入数据的表
说明 读未提交 会发生 脏读
/*
mysql> select * from dog;
+------+--------+
| id | name |
+------+--------+
| 1 | 皮皮 |
| 2 | 大黄 |
+------+--------+
*/
在控制台a修改大黄的名字
UPDATE dog SET `name` = '小虎' WHERE id = 2;
在控制台c查询表格,发现还是原来的表格
说明 读已提交 不会发生 脏读
/*
mysql> select * from dog;
+------+--------+
| id | name |
+------+--------+
| 1 | 皮皮 |
| 2 | 大黄 |
+------+--------+
*/
如果控制台a提交的话,则会显示修改好的表格
说明 读已提交会发生 不可重复读
/*
mysql> select * from dog;
+------+--------+
| id | name |
+------+--------+
| 1 | 皮皮 |
| 2 | 小虎 |
+------+--------+
*/
这里重点介绍三种: MyISAM、InnoDB、MEMORY
-- 表类型和存储引擎
-- 查看所有存储引擎
SHOW ENGINES
-- innodb 存储引擎,是前面使用过.
-- 1. 支持事务 2. 支持外键 3. 支持行级锁
-- myisam 存储引擎
CREATE TABLE t26 (
id INT,
`name` VARCHAR(32)) ENGINE MYISAM
-- 1. 添加速度快 2. 不支持外键和事务 3. 支持表级锁
-- 开始事物
START TRANSACTION;
-- 添加保存点t1
SAVEPOINT t1;
-- 添加一条数据
INSERT INTO t26 VALUES(1,'jack');
-- 回滚到t1
ROLLBACK TO t1;
-- 通过搜索发现,回滚失败
-- 所以 MYISAM 引擎是不支持事物的
SELECT * FROM t26;
-- memory 存储引擎
-- 1. 数据存储在内存中[关闭了 Mysql 服务,数据丢失, 但是表结构还在]
-- 2. 执行速度很快(没有 IO 读写) 3. 默认支持索引(hash 表)
-- 创建表t27
CREATE TABLE t27(
id INT,
`name` VARCHAR(32)) ENGINE MEMORY
-- 添加三条数据
INSERT INTO t27 VALUES(1,'jack'),(2,'lucy'),(3,'lisa');
-- 搜索表
SELECT * FROM t27;
-- 当我们在控制台重启mysql以后,再次搜索表t27
-- 发现所有数据都不见了,但是表的结构还在
DESC t27;
当我们在控制台重启mysql图例:
如果你的应用不需要事务,处理的只是基本的CRUD操作,那么MyISAM是不二选择,速度快
如果需要支持事务,选择InnoDB.
Memory 存储引擎就是将数据存储在内存中,由于没有磁盘 I/O 的等待速度极快。
但由于是内存存储引擎,所做的任何修改在服务器重启后都将消失。(经典用法 用户的在线状态().)
ALTER TABLE 表名 ENGINE = 储存引擎;
-- 修改t27的存储引擎
ALTER TABLE t27 ENGINE = INNODB;
先看一个需求:
创建一个视图emp_view01,只能查询 emp表的(empno , ename , job 和 deptno)信息
-- 视图的使用
-- 创建一个视图emp_view01,只能查询 emp表的(empno , ename , job 和 deptno)信息
-- 创建视图
CREATE VIEW emp_view01
AS
SELECT empno,ename,job,deptno FROM emp;
-- 查看视图
DESC emp_view01;
SELECT * FROM emp_view01;
SELECT ename FROM emp_view01;
-- 查看创建视图的指令
SHOW CREATE VIEW emp_view01;
-- 1.创建视图
-- create view 视图名 as selecti语句
-- 2.alter view 视图名 as select语句 -- 更新成新的视图
-- 3. SHOW CREATE VIEW 视图名
-- 4. drop view 视图名1,视图名2
DROP VIEW emp_view01;
创建视图后,到数据库去看,对应视图只有一个视图结构文件(形式: 视图名.frm)
视图的数据变化会影响到基表,基表的数据变化也会影响到视图 [insert update delete ]
—·针对前面的雇员管理系统-----
mysql> create view myview as select empno ,ename , job, comm from emp;
mysql> select * from myview;
mysql> update myview set comm=200 where empno=7369;//修改视图,对基表都有变化
mysql> update emp set comm=100 where empno=7369; //修改基表,对视频也有变化
视图中可以再使用视图,数据仍然来自基表…[案例演示]
-- 视图细节的讨论
-- 1. 创建视图后,到数据库去看,对应视图只有一个视图结构文件(形式: 视图名.frm)
-- 2. 视图的数据变化会影响到基表,基表的数据变化也会影响到视图 [insert update delete ]
-- 修改视图,会影响到基表
-- 把编号7369 SMITH的job改为MANAGER
UPDATE emp_view01
SET job = 'MANAGER'
WHERE empno = 7369;
-- 查询基表后发现修改了视图的数据,
-- 基表也跟着修改了
SELECT * FROM emp;
-- 现在我们通过基表把SIMTH的job修改为SALESMAN
UPDATE emp
SET job = 'SALESMAN'
WHERE empno = 7369;
-- 查询视图
SELECT * FROM emp_view01;
-- 视图中可以再使用视图,数据仍然来自基表..[案例演示]
-- 我们从emp_view01的视图中,选出empno和ename作出新的视图
CREATE VIEW emp_view02
AS
SELECT empno,ename FROM emp_view01;
-- 查询视图emp_view02
SELECT * FROM emp_view02;
安全:
一些数据表有着重要的信息。有些字段是保密的,不能让用户直接看到。这时就可以创建一个视图,在这张视图中只保留一部分字段。这样,用户就可以查询自己需要的字段,不能查看保密的字段。
性能:
关系数据库的数据常常会分表存储,使用外键建立这些表的之间关系。这时,数据库查询通常会用到连接 (JOIN)。这样做不但麻烦,效率相对也比较低。如果建立一个视图,将相关的表和字段组合在一起,就可以避免使用JOIN查询数据
灵活:
如果系统中有一张旧的表,这张表由于设计的问题,即将被废弃。然而,很多应用都是基于这张表,不易修改。这时就可以建立一张视图,视图中的数据直接映射到新建的表。这样,就可以少做很多改动,也达到了升级数据表的目的。
针对 emp,dept,和 salgrade 张三表.创建一视图 emp view03,
可以显示雇员编号,雇员名,雇员部门名称和 薪水级别[即使用三张表,构建一个视图]
-- 针对 emp,dept,和 salgrade 张三表.创建一视图 emp view03,
-- 可以显示雇员编号,雇员名,雇员部门名称和 薪水级别[即使用三张表,构建一个视图]
CREATE VIEW emp_view03
AS
SELECT empno,ename,dname,grade
FROM emp,dept,salgrade
WHERE emp.deptno = dept.deptno
AND (sal BETWEEN losal AND hisal);
-- 查询emp_view03视图
SELECT * FROM emp_view03;
其中user表的重要字段说明:
create user ‘用户名’ @ ‘允许登录位置’ identified by ‘密码’
说明: 创建用户,同时指定密码
案例演示:
-- mysql用户的管理
-- 原因:当我们做开发时,可以根据不同的开发人员,赋给他们相应的mysql操作权限
-- 所以,mysql数据库管理人员(root)根据需要创建不同的用户,赋给相应的权限,供开发人员使用
-- 1. 创建新的用户
-- 解读:
-- 'sys_stu'@'localhost' 表示用户的完整信息
-- 'sys_stu' 用户名 'localhost' 登录的ip '123456' 登录的密码
-- 但是注意存放到mysql.user表时,是password('123456')加密后的密码
-- *6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9
CREATE USER 'sys_stu'@'localhost' IDENTIFIED BY '123456'
-- 密码加密
SELECT PASSWORD('123456')
SELECT * FROM mysql.user
SELECT `host`,`user`, authentication_string
FROM mysql.user
这时候我们就可以登录新创的用户 sys_stu
由此可得不同的数据库用户,登录到DBMS后,根据相应的权限,可以操作的数据库和数据对象(表,视图,触发器)都不一样
drop user ‘用户名’ @ ‘允许登录位置’;
-- 删除用户
DROP USER 'sys_stu'@'localhost'
修改自己的密码
set password = password("密码);
修改他人的密码 (需要有修改用户密码权限)
set password for用户名@'登录位置 = password(密码);
sys_stu用户可以修改自己的密码,但是修改不了root用户的密码
但是root用户可以修改任何用户的密码
-- root用户可以修改sys_stu的密码
SET PASSWORD FOR 'sys_stu'@'localhost' = PASSWORD('123456')
基本语法
revoke 权限列表 on 库.对象名 from ‘用户名’@‘登录位置’;
如果权限没有生效,可以执行下面命令
基本语法:FLUSH PRIVILEGES;
用户管理练习题
root用户的代码演示:
-- 1. 创建一个用户(你的名字,拼音),密码 123,并且只可以从本地登录,不让远程登录mysql
CREATE USER 'song'@'localhost' IDENTIFIED BY '123';
-- 2. 创建库和表 testdb下的 news 表,要求:使用root 用户创建
CREATE DATABASE testdb
CREATE TABLE news(
id INT,
content VARCHAR(32));
INSERT INTO news VALUES(1,'木叶')
INSERT INTO news VALUES(2,'杰西')
-- 3. 给用户分配查看 news 表和添加数据的权限
-- grant 权限列表 on 库对象名 to用户名'@’登录位置[identified by密码]
GRANT SELECT,INSERT
ON news
TO 'song'@'localhost'
-- 4. 测试看看用户是否只有这几个权限
-- 增加一个权限
GRANT UPDATE
ON news
TO 'song'@'localhost'
-- 5. 修改密码为 abc,要求: 使用root 用户完成
SET PASSWORD FOR 'song'@'localhost' = PASSWORD('abc')
-- 6. 重新登录
-- 7. 演示 回收权限
-- revoke 权限列表 on 库.对象名 from '用户名'@'登录位置';
REVOKE SELECT,INSERT,UPDATE
ON news
FROM 'song'@'localhost'
-- 8. 使用root 用户删除你的用户
DROP USER 'song'@'localhost'
新创的song 用户的代码演示:
-- 这里在默认情况下,song用户只能看到一个默认的系统数据库
-- 可以查看
SELECT * FROM news
-- 可以插入数据
INSERT INTO news VALUES(3,'大圣')
-- 不能修改,没有权限
UPDATE news SET content = '彼岸'
WHERE id = 1;-- 失败
-- 当root重新赋给我们update权限过后,可以修改了
UPDATE news SET content = '彼岸'
WHERE id = 1;
-- 重新登陆
-- 当root回收我们的权限以后,我们就不能进行操作了
在创建用户的时候,如果不指定Host, 则为%,%表示表示所有IP都有连接权限create user xxx:
你也可以这样指定create user ‘xxx’@‘192.168.1.%’
表示xxx用户在 192.168.1.的ip可以登录mysql
在删除用户的时候,如果 host 不是 %,需要明确指定’用户’@‘host值’
-- 细节说明
-- 1. 在创建用户的时候,如果不指定Host, 则为%,%表示表示所有IP都有连接权限create user xxx:
CREATE USER jack
-- 查询信息
SELECT `host`,`user` FROM mysql.user
-- 2. 你也可以这样指定create user 'xxx'@'192.168.1.%'
-- 表示xxx用户在 192.168.1.的ip可以登录mysql
CREATE USER 'sys'@'192.168.1.%'
-- 3. 在删除用户的时候,如果 host 不是 %,需要明确指定'用户'@'host值'
-- 因为jack的host是%,所以可以直接删除
-- 等价于 DROP USER 'jack'@'%'
DROP USER jack
-- 而sys的host不是%,直接删除会报错
-- Operation DROP USER failed for 'sys'@'%'
DROP USER sys
-- 删除的时候要明确的写明host的值
DROP USER 'sys'@'192.168.1.%'
以下哪条语句是错误的? [D ]
A. SELECT empno,ename name,sal salary FROM emp;
B. SELECT empno,ename name,sal AS salary FROM emp;
C. SELECT ename,sal*12 AS "Annual Salary" FROM emp;*
D. SELECT ename,sal*12 Annual Salary FROM emp;
某用户希望显示补助非空的所有雇员信息,应该使用哪条语句? [B]
A. SELECT ename,sal,comm FROM emp WHERE comm<>null;
B. SELECT ename,sal,comm FROM emp WHERE comm IS NOT null;
C. SELECT ename,sal,comm FROM emp WHERE comm<>0;
以下哪条语句是错误的? [C]
A. SELECT ename,sal salary FROM emp ORDER BY sal:
B. SELECT ename,sal salary FROM emp ORDER BY salary:
C. SELECT ename,sal salary FROM emp ORDER BY 3;
写出 查看DEPT表和EMP表的结构 的sql语句
使用简单查询语句完成:
限制查询数据
排序数据。
编码如下:
-- 1. 写出 查看DEPT表和EMP表的结构 的sql语句 homework02.sql
DESC dept;
DESC emp;
-- 2. 使用简单查询语句完成:
-- 1. 显示所有部门名称。
SELECT dname FROM dept;
-- 2. 显示所有雇员名及其全年收入 13月(工资+补助),并指定列别名”年收入
SELECT ename,(sal+IFNULL (comm,0))*13 AS '年收入'
FROM emp
-- 3. 限制查询数据
-- 1. 显示工资超过2850的雇员姓名和工资
SELECT ename,sal
FROM emp
WHERE sal<2850
-- 2. 显示工资不在1500到2850之间的所有雇员名及工资
SELECT ename,sal
FROM emp
WHERE NOT(sal BETWEEN 1500 AND 2850)
-- 或者可以这样写
-- where sal < 1500 or sal > 2850
-- 3. 显示编号为7566的雇员姓名及所在部门编号。
SELECT ename,deptno
FROM emp
WHERE empno = 7566
-- 4. 显示部门10和30中工资超过1500的雇员名及工资
SELECT ename,sal
FROM emp
WHERE deptno IN(10,30)
-- where (deptno = 10 or deptno = 30)
-- 还可以这样写
AND sal > 1500
-- 5. 显示无管理者的雇员名及岗位。
SELECT ename,job
FROM emp
WHERE mgr IS NULL
-- 4. 排序数据。
-- 1. 显示在1991年2月1日到1991年5月1日之间雇用的雇员名,岗位及雇佣日期,并以雇佣日期进行排序
SELECT ename,job,hiredate
FROM emp
WHERE hiredate BETWEEN '1991-02-01' AND '1991-05-01'
ORDER BY hiredate
-- 2. 显示获得补助的所有雇员名,工资及补助,并以工资降序排序
SELECT ename,sal,comm
FROM emp
WHERE comm IS NOT NULL
ORDER BY sal DESC
根据: emp员工表 写出正确SQL
-- 根据: emp员工表 写出正确SQL
-- 1. 选择部门30中的所有员工.
SELECT *
FROM emp
WHERE deptno = 30
-- 2. 列出所有办事员(CLERK)的姓名,编号和部门编号.
SELECT ename,empno,deptno
FROM emp
WHERE job = 'CLERK'
-- 3. 找出佣金高于薪金的员工.
SELECT *
FROM emp
WHERE IFNULL(comm,0) > sal
-- 4. 找出佣金高于薪金60%的员工
SELECT *
FROM emp
WHERE IFNULL(comm,0) > (sal*0.6)
-- 5. 找出部门10中所有经理(MANAGER)和部门20中所有办事员(CLERK)的详细资料
SELECT *
FROM emp
WHERE (deptno = 10 AND job = 'MANAGER')
OR (deptno = 20 AND job = 'CLERK')
-- 6. 找出部门10中所有经理(MANAGER),部门20中所有办事员(CLERK),
-- 还有既不是经理又不是办事员但其薪金大于或等于2000的所有员工的详细资料.
SELECT *
FROM emp
WHERE (deptno = 10 AND job = 'MANAGER')
OR (deptno = 20 AND job = 'CLERK')
OR (job NOT IN('MANAGER','CLERK')
AND sal>2000)
-- 7. 找出收取佣金的员工的不同工作.
SELECT DISTINCT job
FROM emp
WHERE comm IS NOT NULL
-- 8. 找出不收取佣金或收取的佣金低于100的员工
SELECT ename,job
FROM emp
WHERE comm IS NULL
OR IFNULL(comm,0) < 100
-- 9. 找出各月倒数第3天受雇的所有员工.
-- LAST_DAY() 表示日期的月份的最后一天
SELECT ename,hiredate
FROM emp
WHERE LAST_DAY(hiredate)-hiredate = 2
-- 10. 找出早于12年前受雇的员工.
SELECT ename,hiredate
FROM emp
WHERE DATE_ADD(hiredate,INTERVAL 12 YEAR) - NOW() < 0
-- 11. 以首字母小写的方式显示所有员工的姓名
SELECT CONCAT(LCASE(LEFT(ename,1)),UCASE(SUBSTRING(ename,2)))AS'姓名'
FROM emp
-- 12.显示正好为5个字符的员工的姓名.
SELECT ename
FROM emp
WHERE LENGTH(ename)=5
-- 13. 显示不带有"R"的员工的姓名
SELECT ename
FROM emp
WHERE ename NOT LIKE '%R%'
-- 14. 显示所有员工姓名的前三个字符.
SELECT LEFT(ename,3)
FROM emp
-- 15. 显示所有员工的姓名,用a替换所有"A"
SELECT REPLACE(ename, 'A','a')
FROM emp
-- 16. 显示满10年服务年限的员工的姓名和受雇日期.
SELECT ename,hiredate
FROM emp
WHERE DATE_ADD(hiredate,INTERVAL 10 YEAR)<=NOW()
-- 17. 显示员工的详细资料,按姓名排序.
SELECT *
FROM emp
ORDER BY ename
-- 18. 显示员工的姓名和受雇日期,根据其服务年限,将最老的员工排在最前面
SELECT ename,hiredate
FROM emp
ORDER BY hiredate
-- 19. 显示所有员工的姓名、工作和薪金,按工作降序排序,若工作相同则按薪金排序.
SELECT ename,job,sal
FROM emp
ORDER BY job DESC,sal
-- 20. 显示所有员工的姓名、加入公司的年份和月份,按受雇日期所在月排序,
-- 若月份相同则将最早年份的员工排在最前面.
SELECT ename,CONCAT(YEAR(hiredate),'-',MONTH(hiredate))
FROM emp
ORDER BY MONTH(hiredate),YEAR(hiredate)
-- 21. 显示在一个月为30天的情况所有员工的日薪金,忽略余数
-- floor 向下取整
SELECT ename,FLOOR(sal/30) ,sal/30
FROM emp
-- 22. 找出在(任何年份的)2月受聘的所有员工
SELECT *
FROM emp
WHERE MONTH(hiredate) = 2
-- 23. 对于每个员工,显示其加入公司的天数
SELECT ename,DATEDIFF(NOW(),hiredate)AS'入职天数'
FROM emp
-- 24. 显示姓名字段的任何位置包含"A”的所有员工的姓名
SELECT ename
FROM emp
WHERE ename LIKE '%A%'
-- 25. 以年月日的方式显示所有员工的服务年限.(大概)
-- 思路:大概估算的,平均一个月算30.5天
-- 1. 先求出工作了多少天
SELECT DATEDIFF(NOW(),hiredate)FROM emp;
-- 2.算出工作了多少年
FLOOR(DATEDIFF(NOW(),hiredate)/365)AS '年'
-- 3.算出工作了多少月
FLOOR(DATEDIFF(NOW(),hiredate)%365/30.5)AS '月'
-- 4.算出工作了多少天
FLOOR(DATEDIFF(NOW(),hiredate)%30.5) AS '工作天'
-- 5.然后把他们汇总到一起
SELECT ename, FLOOR(DATEDIFF(NOW(),hiredate)/365)AS '年',
FLOOR(DATEDIFF(NOW(),hiredate)%365/30.5)AS '月',
FLOOR(DATEDIFF(NOW(),hiredate)%30.5) AS '工作天'
FROM emp;
根据:emp员工表,dept部门表,工资 = 薪金sal + 佣金 comm 写出正确SQL
-- 根据:emp员工表,dept部门表,工资 = 薪金sal + 佣金 comm 写出正确SQL
SELECT * FROM emp
SELECT * FROM dept
-- 1. 列出至少有一个员工的所有部门
/*
1. 先查出各个部门有多少人
2. 使用having子句过滤
*/
SELECT COUNT(*)AS num,deptno
FROM emp
GROUP BY deptno
HAVING num >= 1
-- 2. 列出薪金比“SMITH”多的所有员工。
/*
1. 先算出SMITH的sal --> 然后作为子查询
2.然后算出其他员工sal大于SMITH的即可
*/
SELECT sal
FROM emp
WHERE ename = 'SMITH'
SELECT ename,sal
FROM emp
WHERE sal >(
SELECT sal
FROM emp
WHERE ename = 'SMITH'
)
-- 3. 列出受雇日期晚于其直接上级的所有员工。
/*
1.先把emp表当做两张表woker,leader
2.然后满足两个条件
1. woker.hiredate > leader.hiredate
2. woker.mgr = leader.empno
*/
SELECT woker.ename AS '员工名',woker.hiredate AS '员工入职时间',
leader.ename AS '上级名',leader.hiredate AS '上级入职时间'
FROM emp woker,emp leader
WHERE woker.hiredate > leader.hiredate
AND woker.mgr = leader.empno
-- 4. 列出部门名称和这些部门的员工信息,同时列出那些没有员工的部门
/*
因为这里要显示所有的部门
所以我们将用到外连接
*/
SELECT dname,emp.*
FROM dept LEFT JOIN emp
ON emp.deptno = dept.deptno
-- 5. 列出所有“CLERK” (办事员)的姓名及其部门名称。
SELECT ename,dname
FROM emp,dept
WHERE emp.deptno = dept.deptno
AND job = 'CLERK'
-- 6. 列出最低薪金大于1500的各种工作。
/*
1. 先算出各个部门的最低薪金
2. 然后用having子句过滤
*/
SELECT job,MIN(sal)AS min_sal
FROM emp
GROUP BY job
HAVING min_sal > 1500
-- 7. 列出在部门“SALES” (销售部) 工作的员工的姓名
SELECT ename,dname
FROM emp,dept
WHERE emp.deptno = dept.deptno
AND dname = 'SALES'
-- 8. 列出薪金高于公司平均薪金的所有员工。
/*
1. 先算出公司的平均工资 --> 作为子查询
2. 然后算出薪金高于平均工资的员工
*/
SELECT AVG(sal)
FROM emp
SELECT ename,sal
FROM emp
WHERE sal > (
SELECT AVG(sal)
FROM emp)
-- 9. 列出与“SCOTT”从事相同工作的所有员工
/*
1. 先查出'SCOTT'从事的工作 --> 作为子查询
2. 然后查出跟“SCOTT”从事相同工作的所有员工
*/
SELECT job
FROM emp
WHERE ename = 'SCOTT'
SELECT *
FROM emp
WHERE job = (
SELECT job
FROM emp
WHERE ename = 'SCOTT')
-- 10. 列出薪金高于所在部门30工作的所有员工的薪金的员工姓名和薪金
/*
1.先算出在部门30工作的最高工资 --> 作为子查询
2.然后算出薪金高于这个工资的所有员工
*/
SELECT MAX(sal)
FROM emp
WHERE deptno = 30
SELECT ename,sal
FROM emp
WHERE sal >(
SELECT MAX(sal)
FROM emp
WHERE deptno = 30)
-- 11. 列出在每个部门工作的员工数量、平均工资和平均服务期限。
SELECT dname,COUNT(dname),AVG(sal)AS '部门员工数量',
FORMAT(AVG(DATEDIFF(NOW(),hiredate)/365),2)AS '工作年限'
FROM emp,dept
WHERE emp.deptno = dept.deptno
GROUP BY dname
-- 12. 列出所有员工的姓名、部门名称和工资
SELECT ename,dname,sal
FROM emp,dept
WHERE emp.deptno = dept.deptno
-- 13. 列出所有部门的详细信息和部门人数。
-- 1.先算出部门人数
SELECT COUNT(*)AS num ,deptno
FROM emp
GROUP BY deptno
-- 2.然后算出所有部门的详细信息
SELECT dept.*,tem.num AS'部门人数'
FROM dept, (
SELECT COUNT(*)AS num ,deptno
FROM emp
GROUP BY deptno
)tem
WHERE tem.deptno = dept.deptno
-- 14. 列出各种工作的最低工资。
SELECT dname,MIN(sal+IFNULL(comm,0))
FROM emp,dept
WHERE dept.deptno = emp.deptno
GROUP BY dname
-- 15. 列出MANAGER (经理)的最低薪金
SELECT MIN(sal+IFNULL(comm,0))
FROM emp
WHERE job = 'MANAGER'
-- 16. 列出所有员工的年工资,按年薪从低到高排序
SELECT ename,(sal+IFNULL(comm,0))*12 AS year_sal
FROM emp
ORDER BY year_sal
设学校环境如下:一个系有若干个专业,每个专业一年只招一个班,每个班有若干个学生。
现要建立关于系、学生、班级的数据库,关系模式为:
班CLASS (班号classid,专业名subject, 系名deptname,入学年份enrolltime,人数num)
学生STUDENT (学号studentid,姓名name,年龄age,班号classid)
系 DEPARTMENT (系号departmentid,系名deptname)
试用SQL语言完成以下功能:
建表,在定义中要求声明:
(1) 每个表的主外码
(2) deptname是唯一约束
(3)学生姓名不能为空
插入如下数据
CLASS (
101,软件,计算机,1995,20;
102,微电子,计算机,1996,30;
111,无机化学,化学,1995,29;
112,高分子化学,化学,1996,25;
121,统计数学,数学,1995,20;
131,现代语言,中文,1996,20;
141,国际贸易,经济,1997,30;
142,国际金融,经济,1996,14)
STUDENTS (
8101,张三,18,101;
8102,钱四,16,121;
8103,王玲,17,131;
8105,李飞,19,102;
8109,赵四,18,141;
8110,李可,20,142;
8201,张飞,18,111;
8302,周瑜,16,112;
8203,王亮,17,111;
8305,董庆,19,102;
8409,赵龙,18,101;
8510,李丽,20,142 )
DEPARTMENT (
001数学;
002计算机:
003化学;
004中文;
005经济;)
完成以下查询功能
3.1 找出所有姓李的学生
3.2 列出所有开设超过1个专业的系的名字
3.3 列出人数大于等于30的系的编号和名字
学校又新增加了一个物理系,编号为006
学生张三退学,请更新相关的表
-- 设学校环境如下:一个系有若干个专业,每个专业一年只招一个班,每个班有若干个学生。
-- 现要建立关于系、学生、班级的数据库,关系模式为:
-- 班CLASS (班号classid,专业名subject, 系名deptname,入学年份enrolltime,人数num)
-- 学生STUDENT (学号studentid,姓名name,年龄age,班号classid)
-- 系 DEPARTMENT (系号departmentid,系名deptname)
-- 试用SQL语言完成以下功能:
-- 1. 建表,在定义中要求声明:
-- (1) 每个表的主外码
-- (2) deptname是唯一约束
-- (3) 学生姓名不能为空
-- 第一步,先创建 系 DEPARTMENT (系号departmentid,系名deptname)
CREATE TABLE department(
departmentid VARCHAR(32) PRIMARY KEY,
deptname VARCHAR(8)UNIQUE NOT NULL);
SELECT * FROM department;
DROP TABLE department
-- 第二步,创建 班CLASS (班号classid,专业名subject, 系名deptname,入学年份enrolltime,人数num)
CREATE TABLE class(
classid INT PRIMARY KEY,
`subject` VARCHAR(32)NOT NULL DEFAULT '',
deptname VARCHAR(32),-- 外键字段,在表后面定义
enrolltime INT NOT NULL DEFAULT 2000,
num INT NOT NULL DEFAULT 0,
FOREIGN KEY (deptname) REFERENCES department(deptname));
SELECT * FROM class;
DROP TABLE class
-- 第三步,创建 学生STUDENT (学号studentid,姓名name,年龄age,班号classid)
CREATE TABLE students(
studentid INT PRIMARY KEY,
`name` VARCHAR(32)NOT NULL DEFAULT '',
age INT NOT NULL DEFAULT 0,
classid INT,-- 外键
FOREIGN KEY(classid) REFERENCES class(classid));
SELECT * FROM students;
DROP TABLE students
-- 2. 插入如下数据
-- 系DEPARTMENT 的信息
/*
DEPARTMENT (
001数学;
002计算机:
003化学;
004中文;
005经济;)
*/
INSERT INTO department
VALUES('001','数学'),
('002','计算机'),
('003','化学'),
('004','中文'),
('005','经济');
DELETE FROM department
-- 班级 CLASS 的信息
/*
CLASS (
101,软件,计算机,1995,20;
102,微电子,计算机,1996,30;
111,无机化学,化学,1995,29;
112,高分子化学,化学,1996,25;
121,统计数学,数学,1995,20;
131,现代语言,中文,1996,20;
141,国际贸易,经济,1997,30;
142,国际金融,经济,1996,14)
*/
INSERT INTO class
VALUES(101,'软件','计算机',1995,20),
(102,'微电子','计算机',1996,30),
(111,'无机化学','化学',1995,29),
(112,'高分子化学','化学',1996,25),
(121,'统计数学','数学',1995,20),
(131,'现代语言','中文',1996,20),
(141,'国际贸易','经济',1997,30),
(142,'国际金融','经济',1996,14);
-- 学生 STUDENTS 的信息
/*
STUDENTS (
8101,张三,18,101;
8102,钱四,16,121;
8103,王玲,17,131;
8105,李飞,19,102;
8109,赵四,18,141;
8110,李可,20,142;
8201,张飞,18,111;
8302,周瑜,16,112;
8203,王亮,17,111;
8305,董庆,19,102;
8409,赵龙,18,101;
8510,李丽,20,142 )
*/
INSERT INTO students
VALUES(8101,'张三',18,101),
(8102,'钱四',16,121),
(8103,'王玲',17,131),
(8105,'李飞',19,102),
(8109,'赵四',18,141),
(8110,'李可',20,142),
(8201,'张飞',18,111),
(8302,'周瑜',16,112),
(8203,'王亮',17,111),
(8305,'董庆',19,102),
(8409,'赵龙',18,101),
(8510,'李丽',20,142);
-- 3. 完成以下查询功能
-- 3.1 找出所有姓李的学生
SELECT *
FROM students
WHERE `name` LIKE '李%'
-- 3.2 列出所有开设超过1个专业的系的名字
-- 1. 先查询各个系有多少个专业
-- 2.添加having子句过滤
SELECT COUNT(*)AS num,deptname
FROM class
GROUP BY deptname
HAVING num > 1;
-- 3.3 列出人数大于等于30的系的编号和名字
-- 1. 先算出各个系有多少人 --> 临时表
SELECT SUM(num)AS nums,deptname
FROM class
GROUP BY deptname
HAVING nums > 30
-- 2. 把上面那个当做临时表,然后查出人数大于等于30的系的编号和名字
SELECT tmp.*,department.departmentid,department.deptname
FROM department,(
SELECT SUM(num)AS nums,deptname
FROM class
GROUP BY deptname
HAVING nums > 30)tmp
WHERE department.deptname = tmp.deptname
-- 4. 学校又新增加了一个物理系,编号为006
INSERT INTO department
VALUES('006','物理系')
-- 5. 学生张三退学,请更新相关的表
-- 分析:1.张三所在班级的人数-1
-- 2.张三从学生表删除
-- 所以要进行事务控制
-- 1.开启事务
START TRANSACTION;
-- 2.张三所在班级的人数-1
UPDATE class SET num = num -1
WHERE classid = (
SELECT classid
FROM students
WHERE `name` = '张三');
-- 3.删除张三
DELETE
FROM students
WHERE `name` = '张三';
-- 4.提交事务
COMMIT;
-- 5.将上面四步全部选中,直接全部执行操作
-- 将会发现students里的张三被删除了
-- class里面张三的班级号里人数少了一个
SELECT * FROM class;
SELECT * FROM students;