Datawhale-mysql-08

文章目录

MySQL任务1 - 3天

负责人:XXX

【任务一】

#任务时间#

请于8月7日21:00前完成,在https://shimo.im/sheets/8tytcJVqY68tDjJv/ 《My_SQL第八期打卡统计》,可复制链接后用石墨文档 App 打开处打卡,逾期未打卡会被清退。

1.1 MySQL 软件安装及数据库基础

#学习内容#

1.软件安装及服务器设置。

教程 http://www.runoob.com/mysql/mysql-install.html

2.(选做,但是强烈建议) 使用图形界面软件 Navicat for SQL

知识星球提供破解版Navicat for SQL,请大家自行查找。

简易步骤:

​ 解压缩文件,复制key

​ 打开文件夹中的navicat.e

​ 用户名随意,输入key,然后连接数据库

​ 输入密码,连接名改成自己喜欢的

​ 剩下的自己探索,怎么在navicat中创建数据库、表等等

3.数据库基础知识

数据库定义

关系型数据库

二维表

主键

外键

4.MySQL数据库管理系统

数据库

数据表

视图

存储过程

1.2 MySQL 基础 (一)- 查询语句

#学习内容#

\1. 导入示例数据库,教程 https://www.yiibai.com/mysql/how-to-load-sample-database-into-mysql-database-server.html

\2. SQL是什么?MySQL是什么?

\3. 查询语句 SELECT FROM

​ 语句解释

​ 去重语句

​ 前N个语句

​ CASE…END判断语句

\4. 筛选语句 WHERE

​ 语句解释

​ 运算符/通配符/操作符

\5. 分组语句 GROUP BY

​ 聚集函数

​ 语句解释

​ HAVING子句

\6. 排序语句 ORDER BY

​ 语句解释

​ 正序、逆序

\7. 函数

​ 时间函数

​ 数值函数

​ 字符串函数

\8. SQL注释

\9. SQL代码规范

​ [SQL编程格式的优化建议] https://zhuanlan.zhihu.com/p/27466166

​ [SQL Style Guide] https://www.sqlstyle.guide/

#作业#

项目一:查找重复的电子邮箱(难度:简单)

创建 email表,并插入如下三行数据

±—±--------+

| Id | Email |

±—±--------+

| 1 | a@b.com |

| 2 | c@d.com |

| 3 | a@b.com |

±—±--------+

编写一个 SQL 查询,查找 Email 表中所有重复的电子邮箱。

根据以上输入,你的查询应返回以下结果:

±--------+

| Email |

±--------+

| a@b.com |

±--------+

说明:所有电子邮箱都是小写字母。

项目二:查找大国(难度:简单)

创建如下 World 表

±----------------±-----------±-----------±-------------±--------------+

| name | continent | area | population | gdp |

±----------------±-----------±-----------±-------------±--------------+

| Afghanistan | Asia | 652230 | 25500100 | 20343000 |

| Albania | Europe | 28748 | 2831741 | 12960000 |

| Algeria | Africa | 2381741 | 37100000 | 188681000 |

| Andorra | Europe | 468 | 78115 | 3712000 |

| Angola | Africa | 1246700 | 20609294 | 100990000 |

±----------------±-----------±-----------±-------------±--------------+

如果一个国家的面积超过300万平方公里,或者(人口超过2500万并且gdp超过2000万),那么这个国家就是大国家。

编写一个SQL查询,输出表中所有大国家的名称、人口和面积。

例如,根据上表,我们应该输出:

±-------------±------------±-------------+

| name | population | area |

±-------------±------------±-------------+

| Afghanistan | 25500100 | 652230 |

| Algeria | 37100000 | 2381741 |

±-------------±------------±-------------+

#彩蛋#

考虑到本次集训有很多新手,本次作业赠送建表代码,意不意外,开不开心。

直接将附件code内容复制到cmd或者navicat运行就行。

项目一

– 创建表

CREATE TABLE IF NOT EXISTS email (

ID INT NOT NULL PRIMARY KEY,

Email VARCHAR(255)

);

– 插入数据

INSERT INTO email VALUES(‘1’,‘a@b.com’);

INSERT INTO email VALUES(‘2’,‘c@d.com’);

INSERT INTO email VALUES(‘3’,‘a@b.com’);

项目二

– 创建表

CREATE TABLE IF NOT EXISTS World (

name VARCHAR(50) NOT NULL,

continent VARCHAR(50) NOT NULL,

area INT NOT NULL,

population INT NOT NULL,

gdp INT NOT NULL

);

– 插入数据

INSERT INTO World

VALUES(‘Afghanistan’,‘Asia’,652230,25500100,20343000);

INSERT INTO World

VALUES(‘Albania’,‘Europe’,28748,2831741,12960000);

INSERT INTO World

VALUES(‘Algeria’,‘Africa’,2381741,37100000,188681000);

INSERT INTO World

VALUES(‘Andorra’,‘Europe’,468,78115,3712000);

INSERT INTO World

VALUES(‘Angola’,‘Africa’,1246700,20609294,100990000);

#打卡规则#

学员微信群编号+姓名+CSDN博客或Github链接。

  • 比如我是0号,打卡内容就是 000+言念+https://github.com/

【任务说明】

1.1 是软件安装和配置,以及一些数据库理论知识储备。

1.2 是最最基础的查询语句,可以说学完本次课程,SQL语句就掌握了30%了。

语言规范非常重要,请大家认真仔细阅读。请记住,你写SQL需要考虑别人review时的心情。写的过于杂乱会分分钟造成暴力事件。

学习内容中函数部分,是让大家了解下MySQL可以怎样处理一些数据。了解些常用的,等实际中遇到了再回头查找详细就行。

学习内容是指需要在博客文章中总结的知识点,包括但不仅限于这些知识点。比如一些安装过程中的报错及解决办法也可以写。

祝大家学习开心。😃

六十二 阅

目录

  • 【任务一】
  • 1.1 MySQL 软件安装及数据库基础
  • 1.2 MySQL 基础 (一)- 查询语句
  • #作业#
  • 项目一:查找重复的电子邮箱(难度:简单)
  • 项目二:查找大国(难度:简单)
  • 【任务说明】

项目一作业:

# 连接数据库
C:\WINDOWS\system32>mysql -uroot -p
Enter password: ******
# create创建数据库
mysql> CREATE DATABASE IF NOT EXISTS datawhaledb DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
Query OK, 1 row affected (0.01 sec)

# show展示数据库
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| datawhaledb        |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
5 rows in set (0.00 sec)

mysql>
# use选中数据库
mysql> use datawhaledb
Database changed
# create table 表名(字段名 类型(长度));
mysql> CREATE TABLE IF NOT EXISTS email (ID INT NOT NULL PRIMARY KEY,Email VARCHAR(255));
Query OK, 0 rows affected (0.03 sec)
# desc查看表结构
mysql> desc email;
+-------+--------------+------+-----+---------+-------+
| Field | Type         | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| ID    | int(11)      | NO   | PRI | NULL    |       |
| Email | varchar(255) | YES  |     | NULL    |       |
+-------+--------------+------+-----+---------+-------+
2 rows in set (0.00 sec)

# 增删改查
## 增 insert
1.insert into 表名 values(); 这个必须严格按照字段名 及顺序 对应写
2. insert into 表名(字段1,字段2) values(值1,值2); 有些字段加默认值 我们在插入数据的过程中可以
忽略这些 只要按照 字段1 字段2。。。对应来写

mysql> INSERT INTO email VALUES('1','a@b.com');INSERT INTO email VALUES('2','c@d.com');INSERT INTO email VALUES('3','a@b.com');
Query OK, 1 row affected (0.00 sec)

Query OK, 1 row affected (0.01 sec)

Query OK, 1 row affected (0.00 sec)
## 查 select 
select * from 表名;
* 展示所有的字段 
mysql> select * from email;
+----+---------+
| ID | Email   |
+----+---------+
|  1 | a@b.com |
|  2 | c@d.com |
|  3 | a@b.com |
+----+---------+
3 rows in set (0.00 sec)

编写一个 SQL 查询,查找 Email 表中所有重复的电子邮箱。
从含义上讲,count(1) 与 count(*) 都表示对全部数据行的查询。count(*) 包括了所有的列,相当于行数,在统计结果的时候,不会忽略列值为NULL ;count(1) 用1代表代码行,在统计结果的时候,不会忽略列值为NULL 。
https://blog.csdn.net/lingduo24/article/details/87621363
mysql> select Email,count(1) from email group by Email having count(1)>1;
+---------+----------+
| Email   | count(1) |
+---------+----------+
| a@b.com |        2 |
+---------+----------+
1 row in set (0.01 sec)

mysql>
mysql> select Email from email group by Email having count(1)>1;
+---------+
| Email   |
+---------+
| a@b.com |
+---------+
1 row in set (0.00 sec)

mysql>

项目二作业:

mysql> CREATE TABLE IF NOT EXISTS World (name VARCHAR(50) NOT NULL,continent VARCHAR(50) NOT NULL,area INT NOT NULL,population INT NOT NULL,gdp INT NOT NULL);
Query OK, 0 rows affected (0.02 sec)

mysql> INSERT INTO World  VALUES('Afghanistan','Asia',652230,25500100,20343000);INSERT INTO World   VALUES('Albania','Europe',28748,2831741,12960000);INSERT INTO World   VALUES('Algeria','Africa',2381741,37100000,188681000);INSERT INTO World  VALUES('Andorra','Europe',468,78115,3712000);INSERT INTO World  VALUES('Angola','Africa',1246700,20609294,100990000);
Query OK, 1 row affected (0.02 sec)

Query OK, 1 row affected (0.00 sec)

Query OK, 1 row affected (0.02 sec)

Query OK, 1 row affected (0.00 sec)

Query OK, 1 row affected (0.00 sec)

mysql>



mysql> select * from World;
+-------------+-----------+---------+------------+-----------+
| name        | continent | area    | population | gdp       |
+-------------+-----------+---------+------------+-----------+
| Afghanistan | Asia      |  652230 |   25500100 |  20343000 |
| Albania     | Europe    |   28748 |    2831741 |  12960000 |
| Algeria     | Africa    | 2381741 |   37100000 | 188681000 |
| Andorra     | Europe    |     468 |      78115 |   3712000 |
| Angola      | Africa    | 1246700 |   20609294 | 100990000 |
+-------------+-----------+---------+------------+-----------+
5 rows in set (0.00 sec)
如果一个国家的面积超过300万平方公里,或者(人口超过2500万并且gdp超过2000万),那么这个国家就是大国家。
编写一个SQL查询,输出表中所有大国家的名称、人口和面积。
例如,根据上表,我们应该输出:

mysql>
mysql> select name,population,area from World where area > 300000 or (population > 2500000 and gdp > 20000000);
+-------------+------------+---------+
| name        | population | area    |
+-------------+------------+---------+
| Afghanistan |   25500100 |  652230 |
| Algeria     |   37100000 | 2381741 |
| Angola      |   20609294 | 1246700 |
+-------------+------------+---------+
3 rows in set (0.00 sec)

mysql>

MySQL任务2 - 4天

负责人:XXX

【任务二】

#任务时间#

请于8月11日21:00前完成,在https://shimo.im/sheets/8tytcJVqY68tDjJv/z8q2D处打卡。逾期未打卡会被清退。

2.1 MySQL 基础 (二)- 表操作

#学习内容#

\1. MySQL表数据类型

\2. 用SQL语句创建表

​ 语句解释

​ 设定列类型 、大小、约束

​ 设定主键

\3. 用SQL语句向表中添加数据

​ 语句解释

​ 多种添加方式(指定列名;不指定列名)

\4. 用SQL语句删除表

​ 语句解释

​ DELETE

​ DROP

​ TRUNCATE

​ 不同方式的区别

\5. 用SQL语句修改表

​ 修改列名

​ 修改表中数据

​ 删除行

​ 删除列

​ 新建列

​ 新建行

#作业#

项目三:超过5名学生的课(难度:简单)

创建如下所示的courses 表 ,有: student (学生) 和 class (课程)。

例如,表:

±--------±-----------+

| student | class |

±--------±-----------+

| A | Math |

| B | English |

| C | Math |

| D | Biology |

| E | Math |

| F | Computer |

| G | Math |

| H | Math |

| I | Math |

| A | Math |

±--------±-----------+

编写一个 SQL 查询,列出所有超过或等于5名学生的课。

应该输出:

±--------+

| class |

±--------+

| Math |

±--------+

Note:

学生在每个课中不应被重复计算。

解答项目三

mysql> use datawhaledb
Database changed
mysql> CREATE TABLE IF NOT EXISTS courses(
    -> student VARCHAR(64) NOT NULL,
    -> class VARCHAR(20)
    -> );
Query OK, 0 rows affected (0.06 sec)

mysql>
mysql> INSERT INTO courses VALUES('A','Math');
Query OK, 1 row affected (0.01 sec)

mysql> INSERT INTO courses VALUES('B','English');
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO courses VALUES('C','Math');
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO courses VALUES('D','Biology');
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO courses VALUES('E','Math');
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO courses VALUES('F','Computer');
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO courses VALUES('G','Math');
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO courses VALUES('H','Math');
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO courses VALUES('I','Math');
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO courses VALUES('A','Math');
Query OK, 1 row affected (0.00 sec)

mysql>mysql> select class 
			  from courses 
			  group by class 
			  having count(distinct student)>=5;
+-------+
| class |
+-------+
| Math  |
+-------+
1 row in set (0.00 sec)

mysql>

项目四:交换工资(难度:简单)

创建一个 salary表,如下所示,有m=男性 和 f=女性的值 。

例如:

| id | name | sex | salary |

|----|------|-----|--------|

| 1 | A | m | 2500 |

| 2 | B | f | 1500 |

| 3 | C | m | 5500 |

| 4 | D | f | 500 |

交换所有的 f 和 m 值(例如,将所有 f 值更改为 m,反之亦然)。要求使用一个更新查询,并且没有中间临时表。

运行你所编写的查询语句之后,将会得到以下表:

| id | name | sex | salary |

|----|------|-----|--------|

| 1 | A | f | 2500 |

| 2 | B | m | 1500 |

| 3 | C | f | 5500 |

| 4 | D | m | 500 |

解答项目四

mysql> CREATE TABLE IF NOT EXISTS salary(
    -> id INT NOT NULL PRIMARY KEY,
    -> name VARCHAR(20) NOT NULL,
    -> sex CHAR NOT NULL,
    -> salary int
    -> );
Query OK, 0 rows affected (0.03 sec)

mysql>

mysql> INSERT INTO salary VALUES(1,'A','m',2500);
Query OK, 1 row affected (0.02 sec)

mysql> INSERT INTO salary VALUES(2,'B','f',5500);
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO salary VALUES(3,'C','m',5000);
Query OK, 1 row affected (0.01 sec)

mysql> INSERT INTO salary VALUES(4,'D','f',1500);
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO salary VALUES(5,'E','f',2000);
Query OK, 1 row affected (0.01 sec)

mysql>

mysql> 
UPDATE salary
SET sex = CASE 
	WHEN sex = 'f' THEN 'm'
	WHEN sex = 'm' THEN 'f'
	ELSE sex
END;
Query OK, 5 rows affected (0.04 sec)
Rows matched: 5  Changed: 5  Warnings: 0

mysql> select * from salary;
+----+------+-----+--------+
| id | name | sex | salary |
+----+------+-----+--------+
|  1 | A    | m   |   2500 |
|  2 | B    | f   |   5500 |
|  3 | C    | m   |   5000 |
|  4 | D    | f   |   1500 |
|  5 | E    | f   |   2000 |
+----+------+-----+--------+
5 rows in set (0.00 sec)

mysql>

项目五:有趣的电影 (难度:简单)

某城市开了一家新的电影院,吸引了很多人过来看电影。该电影院特别注意用户体验,专门有个 LED显示板做电影推荐,上面公布着影评和相关电影描述。

作为该电影院的信息部主管,您需要编写一个 SQL查询,找出所有影片描述为非 boring (不无聊) 的并且 id 为奇数 的影片,结果请按等级 rating 排列。

例如,下表 cinema:

±--------±----------±-------------±----------+

| id | movie | description | rating |

±--------±----------±-------------±----------+

| 1 | War | great 3D | 8.9 |

| 2 | Science | fiction | 8.5 |

| 3 | irish | boring | 6.2 |

| 4 | Ice song | Fantacy | 8.6 |

| 5 | House card| Interesting| 9.1 |

±--------±----------±-------------±----------+

对于上面的例子,则正确的输出是为:

±--------±----------±-------------±----------+

| id | movie | description | rating |

±--------±----------±-------------±----------+

| 5 | House card| Interesting| 9.1 |

| 1 | War | great 3D | 8.9 |

±--------±----------±-------------±----------+

解答项目五

# 创建
mysql> CREATE TABLE IF NOT EXISTS cinema (
    -> id INT NOT NULL PRIMARY KEY,
    -> movie VARCHAR(20) NOT NULL,
    -> description VARCHAR(20) NOT NULL,
    -> rating float
    -> );
# 插入    
mysql> INSERT INTO cinema
    -> VALUES ('1', 'Wargreat', '3D', '8.9');
Query OK, 1 row affected (0.01 sec)

mysql>
mysql> INSERT INTO cinema
    -> VALUES ('2', 'Science', 'fiction', '8.5');
Query OK, 1 row affected (0.00 sec)

mysql>
mysql> INSERT INTO cinema
    -> VALUES ('3', 'irish', 'boring', '6.2');
Query OK, 1 row affected (0.00 sec)

mysql>
mysql> INSERT INTO cinema
    -> VALUES ('4', 'Ice song', ' Fantacy', '8.6');
Query OK, 1 row affected (0.00 sec)

mysql>
mysql> INSERT INTO cinema
    -> VALUES ('5', 'House card', 'Interesting', '9.1');
Query OK, 1 row affected (0.01 sec)   

# 查询
mysql> 
SELECT id, movie, description, rating
FROM cinema
WHERE description != 'boring'
	AND id % 2 = 1
ORDER BY rating DESC;

+----+------------+-------------+--------+
| id | movie      | description | rating |
+----+------------+-------------+--------+
|  5 | House card | Interesting |    9.1 |
|  1 | Wargreat   | 3D          |    8.9 |
+----+------------+-------------+--------+
2 rows in set (0.00 sec)

mysql>

2.2 MySQL 基础 (三)- 表联结

#学习内容#

  • MySQL别名

  • INNER JOIN

  • LEFT JOIN

  • CROSS JOIN

  • 自连接

  • UNION

  • 以上几种方式的区别和联系

#作业#

项目六:组合两张表 (难度:简单)

在数据库中创建表1和表2,并各插入三行数据(自己造)

表1: Person

±------------±--------+

| 列名 | 类型 |

±------------±--------+

| PersonId | int |

| FirstName | varchar |

| LastName | varchar |

±------------±--------+

PersonId 是上表主键

表2: Address

±------------±--------+

| 列名 | 类型 |

±------------±--------+

| AddressId | int |

| PersonId | int |

| City | varchar |

| State | varchar |

±------------±--------+

AddressId 是上表主键

编写一个 SQL 查询,满足条件:无论 person 是否有地址信息,都需要基于上述两表提供 person 的以下信息:FirstName, LastName, City, State

解答项目六

-- 创建表
CREATE TABLE IF NOT EXISTS person(
PersonID int PRIMARY KEY,
FirstName varchar(20),
SecondName varchar(20)
);

INSERT INTO person VALUES(1,'Gali','Ma');
INSERT INTO person VALUES(2,'Yao','Xiao');
INSERT INTO person VALUES(3,'Tao','Baiwan');


CREATE TABLE IF NOT EXISTS address(
AddressId int PRIMARY KEY,
PersonId int,
City varchar(20),
State varchar(20)
);

INSERT INTO address VALUES(1,2,'Huangpu','Shanghai');
INSERT INTO address VALUES(2,1,'Huangpu','Shanghai');
INSERT INTO address VALUES(3,3,'Changsha','Hunan');
INSERT INTO address VALUES(4,4,'Haidian','Beijing');

mysql> select Person.FirstName, Person.SecondName, Address.City, Address.State
    -> from Person
    -> left join Address on Person.PersonID = Address.PersonID;
+-----------+------------+----------+----------+
| FirstName | SecondName | City     | State    |
+-----------+------------+----------+----------+
| Yao       | Xiao       | Huangpu  | Shanghai |
| Gali      | Ma         | Huangpu  | Shanghai |
| Tao       | Baiwan     | Changsha | Hunan    |
+-----------+------------+----------+----------+
3 rows in set (0.02 sec)

mysql>

项目七:删除重复的邮箱(难度:简单)

编写一个 SQL 查询,来删除 email 表中所有重复的电子邮箱,重复的邮箱里只保留 Id 最小 的那个。

±—±--------+

| Id | Email |

±—±--------+

| 1 | a@b.com |

| 2 | c@d.com |

| 3 | a@b.com |

±—±--------+

Id 是这个表的主键。

例如,在运行你的查询语句之后,上面的 Person表应返回以下几行:

±—±-----------------+

| Id | Email |

±—±-----------------+

| 1 | a@b.com |

| 2 | c@d.com |

±—±-----------------+

解答项目七

mysql> select * from email;
+----+---------+
| ID | Email   |
+----+---------+
|  1 | a@b.com |
|  2 | c@d.com |
|  3 | a@b.com |
+----+---------+
3 rows in set (0.00 sec)

# 编写一个 SQL 查询,来删除 email 表中所有重复的电子邮箱,重复的邮箱里只保留 **Id** *最小* 的那个。
思路解析: 重复的邮箱用group by 分组,再用min 函数选出最小的邮箱,然后组成一个信的table ,
 		删除重复的邮箱,通过id查询上面生成的临时表,如果当前id不在临时表里,就说明次id 是重复的邮箱,删除即可

# 重复的邮箱里只保留 **Id** *最小* 的那个
mysql> select min(Id) as minId from email group by Email;
+-------+
| minId |
+-------+
|     1 |
|     2 |
+-------+
2 rows in set (0.01 sec)

# 所有重复的邮箱里 **Id** *最小* 的集合, 作为一个临时表table_tmp
SELECT minId
		FROM (
			SELECT MIN(Id) AS minId
			FROM email
			GROUP BY Email
		) table_tmp
		
# 判断Id是否在临时表里,若不在则删除
DELETE FROM email
WHERE Id NOT IN (
		SELECT minId
		FROM (
			SELECT MIN(Id) AS minId
			FROM email
			GROUP BY Email
		) table_tmp
	);
	
mysql> select * from email;
+----+---------+
| ID | Email   |
+----+---------+
|  1 | a@b.com |
|  2 | c@d.com |
+----+---------+
2 rows in set (0.01 sec)
	

项目八:从不订购的客户 (难度:简单)

某网站包含两个表,Customers 表和 Orders 表。编写一个 SQL 查询,找出所有从不订购任何东西的客户。

Customers 表:

±—±------+

| Id | Name |

±—±------+

| 1 | Joe |

| 2 | Henry |

| 3 | Sam |

| 4 | Max |

±—±------+

Orders 表:

±—±-----------+

| Id | CustomerId |

±—±-----------+

| 1 | 3 |

| 2 | 1 |

±—±-----------+

例如给定上述表格,你的查询应返回:

±----------+

| Customers |

±----------+

| Henry |

| Max |

±----------+

解答项目八:从不订购的客户

# 创建表,插入数据
CREATE TABLE IF NOT EXISTS Customers(
Id int PRIMARY KEY,
Name varchar(20)
);

INSERT INTO Customers VALUES(1,'Joe');
INSERT INTO Customers VALUES(2,'Henry');
INSERT INTO Customers VALUES(3,'Sam');
INSERT INTO Customers VALUES(4,'Max');

mysql> select * from Customers;
+----+-------+
| Id | Name  |
+----+-------+
|  1 | Joe   |
|  2 | Henry |
|  3 | Sam   |
|  4 | Max   |
+----+-------+
4 rows in set (0.00 sec)

mysql>


## 创建Orders 表及插入数据
CREATE TABLE IF NOT EXISTS Orders(
Id int PRIMARY KEY,
CustomerId varchar(20)
);

INSERT INTO Orders VALUES(1,'3');
INSERT INTO Orders VALUES(2,'1');

mysql> select * from Orders;
+----+------------+
| Id | CustomerId |
+----+------------+
|  1 | 3          |
|  2 | 1          |
+----+------------+
2 rows in set (0.00 sec)

mysql>

某网站包含两个表,Customers 表和 Orders 表。编写一个 SQL 查询,找出所有从不订购任何东西的客户。
思路一:
两个表外连接,然后判断id是否相等,取出哪些id不等的就可以
LEFT OUTER JOIN        左外连接 一个表满足条件的行,和另一个表的所有行  
left join 是left outer join的简写,left join默认是outer属性的。

# 查询
mysql> SELECT Customers.Name AS 'Customers'
    -> FROM Customers
    -> LEFT JOIN Orders ON Orders.CustomerId = Customers.id
    -> WHERE Orders.CustomerId IS NULL;
+-----------+
| Customers |
+-----------+
| Henry     |
| Max       |
+-----------+
2 rows in set (0.00 sec)

mysql>

# 思路二: 直接查询,CustomerId 不在order中的,然后取出即可
mysql> SELECT Customers.Name AS 'Customers'
    -> FROM Customers
    -> WHERE Customers.id NOT IN (
    -> SELECT CustomerId
    -> FROM Orders
    -> );
+-----------+
| Customers |
+-----------+
| Henry     |
| Max       |
+-----------+
2 rows in set (0.00 sec)

mysql>

项目九:超过经理收入的员工(难度:简单)

Employee 表包含所有员工,他们的经理也属于员工。每个员工都有一个 Id,此外还有一列对应员工的经理的 Id。

±—±------±-------±----------+

| Id | Name | Salary | ManagerId |

±—±------±-------±----------+

| 1 | Joe | 70000 | 3 |

| 2 | Henry | 80000 | 4 |

| 3 | Sam | 60000 | NULL |

| 4 | Max | 90000 | NULL |

±—±------±-------±----------+

给定 Employee 表,编写一个 SQL 查询,该查询可以获取收入超过他们经理的员工的姓名。在上面的表格中,Joe 是唯一一个收入超过他的经理的员工。

±---------+

| Employee |

±---------+

| Joe |

±---------+

【任务说明】

本次作业以及之后的都需要小伙伴自己创建表和插入数据啦。就当表操作的练习。请注意语句规范,可参考上次作业的参考答案。

表联结是SQL语句核心部分。因为在正式业务中必然会涉及到多表之间的数据调用。所以大家务必完全理解吃透这部分的内容。

祝大家学习开心。😃

四十七 阅https://www.cnblogs.com/sunchaothu/p/10459012.html#参考链接)

解答项目九:超过经理收入的员工(难度:简单)

# 创建表,插入数据
CREATE TABLE IF NOT EXISTS Employee (
Id int PRIMARY KEY,
Name varchar(20),
Salary varchar(20),
ManagerId varchar(20)
);

INSERT INTO Employee VALUES('1', 'Joe', '70000', '3');
INSERT INTO Employee VALUES('2', 'Henry', '80000', '4');
INSERT INTO Employee VALUES('3', 'Sam', '60000','NUll');
INSERT INTO Employee VALUES('4', 'Max', '90000','NULL');
mysql> select * from  Employee;
+----+-------+--------+-----------+
| Id | Name  | Salary | ManagerId |
+----+-------+--------+-----------+
|  1 | Joe   | 70000  | 3         |
|  2 | Henry | 80000  | 4         |
|  3 | Sam   | 60000  | NUll      |
|  4 | Max   | 90000  | NULL      |
+----+-------+--------+-----------+
4 rows in set (0.00 sec)


# 思路一: 先比较ManagerId 与Id 是否相等,再比较薪资
mysql> SELECT e1.name AS Employee
    -> FROM Employee e1, Employee e2
    -> WHERE e1.ManagerId = e2.Id
    -> AND e1.Salary > e2.Salary;
+----------+
| Employee |
+----------+
| Joe      |
+----------+
1 row in set, 8 warnings (0.00 sec)

mysql>
 
# 思路二: join函数
mysql> SELECT e1.name AS Employee
    -> FROM Employee e1
    -> JOIN Employee e2
    -> ON e1.ManagerId = e2.Id
    -> AND e1.Salary > e2.Salary;
+----------+
| Employee |
+----------+
| Joe      |
+----------+
1 row in set, 8 warnings (0.00 sec)

mysql>

END

https://www.yiibai.com/mysql/

MySQL教程

https://www.cnblogs.com/sunchaothu/p/10447982.html

这里有个sql美化工具,https://tool.lu/sql/,sql语句比较长的时候可以使用一下。

另外 https://zh.sqlzoo.net/ 这个是个练习 SQL 语句的网站,有需要的话可以使用一下,

https://jiaoqiyuan.cn/2018/10/22/sqlzoo-practice/ 这是我之前练习时的笔记,

现在有点生疏了,分享给大家,希望能对你们有所帮助。

MySQL任务3 - 2天

负责人:某某某

【任务三】

#任务时间#

请于8月13日21:00前完成,在https://shimo.im/sheets/8tytcJVqY68tDjJv/z8q2D处打卡。逾期尚未打卡的会被清退。

#学习内容#

数据导入导出

  • 将之前创建的任意一张MySQL表导出,需要是CSV格式

  • 再将CSV表导入数据库

#作业#

项目十: 各部门工资最高的员工(难度:中等)

创建Employee 表,包含所有员工信息,每个员工有其对应的 Id, salary 和 department Id。

±—±------±-------±-------------+

| Id | Name | Salary | DepartmentId |

±—±------±-------±-------------+

| 1 | Joe | 70000 | 1 |

| 2 | Henry | 80000 | 2 |

| 3 | Sam | 60000 | 2 |

| 4 | Max | 90000 | 1 |

±—±------±-------±-------------+

创建Department 表,包含公司所有部门的信息。

±—±---------+

| Id | Name |

±—±---------+

| 1 | IT |

| 2 | Sales |

±—±---------+

编写一个 SQL 查询,找出每个部门工资最高的员工。例如,根据上述给定的表格,Max 在 IT 部门有最高工资,Henry 在 Sales 部门有最高工资。

±-----------±---------±-------+

| Department | Employee | Salary |

±-----------±---------±-------+

| IT | Max | 90000 |

| Sales | Henry | 80000 |

±-----------±---------±-------+

解答项目十: 各部门工资最高的员工(难度:中等)

https://ask.csdn.net/questions/324299

mysql添加字段,同时插入数据

alter table 表名 add column 字段名 类型(长度);

mysql> alter table employee add column DepartmentId int(11) not null;
Query OK, 0 rows affected (0.10 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> desc employee;
+--------------+-------------+------+-----+---------+-------+
| Field        | Type        | Null | Key | Default | Extra |
+--------------+-------------+------+-----+---------+-------+
| Id           | int(11)     | NO   | PRI | NULL    |       |
| Name         | varchar(20) | YES  |     | NULL    |       |
| Salary       | varchar(20) | YES  |     | NULL    |       |
| ManagerId    | varchar(20) | YES  |     | NULL    |       |
| DepartmentId | int(11)     | NO   |     | NULL    |       |
+--------------+-------------+------+-----+---------+-------+
5 rows in set (0.01 sec)

update 表名 set 字段1=值1,字段2=值2 where 条件 更新数据行

UPDATE employee
SET DepartmentId = 1
WHERE id = 1;

UPDATE employee
SET DepartmentId = 2
WHERE id = 2;

UPDATE employee
SET DepartmentId = 2
WHERE id = 3;

UPDATE employee
SET DepartmentId = 1
WHERE id = 4;

创建表

CREATE TABLE IF NOT EXISTS Department(
Id int PRIMARY KEY,
Name varchar(20)
);

INSERT INTO Department VALUES(1,'IT');
INSERT INTO Department VALUES(2,'Sales');

mysql> select * from Department;
+----+-------+
| Id | Name  |
+----+-------+
|  1 | IT    |
|  2 | Sales |
+----+-------+
2 rows in set (0.00 sec)

mysql>


## 找出每个部门工资最高的员工 
| Department | Employee | Salary |
| IT         | Max      | 90000  |
1.各部门 分组 group by Department
2.最高的工资, 过滤 having max(Salary)
3. 显示的字段 Department(来源于Department.Name 用as改别名,)
			Employee  (来源于Department.employee.Name 用as 改别名 Employee)
			Salary (来源于Department.Salary 用as 改别名 Salary)
4.涉及两个表用join			
join等价于inner join内连接,是返回两个表中都有的符合条件的行。
left join左连接,是返回左表中所有的行及右表中符合条件的行。
right join右连接,是返回右表中所有的行及左表中符合条件的行。
full join全连接,是返回左表中所有的行及右表中所有的行,并按条件连接。
通常情况下,left join肯定比inner join返回的行数多。


mysql> SELECT Department.Name AS Department, employee.Name AS Employee, employee.Salary AS Salary
    -> FROM Department
    -> JOIN employee
    -> WHERE Department.Id = employee.DepartmentId
    -> GROUP BY Department.name
    -> HAVING MAX(Salary);
+------------+----------+--------+
| Department | Employee | Salary |
+------------+----------+--------+
| IT         | Joe      | 70000  |
| Sales      | Henry    | 80000  |
+------------+----------+--------+
2 rows in set (0.02 sec)

mysql>


项目十一: 换座位(难度:中等)

小美是一所中学的信息科技老师,她有一张 seat 座位表,平时用来储存学生名字和与他们相对应的座位 id。

其中纵列的 id 是连续递增的

小美想改变相邻俩学生的座位。

你能不能帮她写一个 SQL query 来输出小美想要的结果呢?

请创建如下所示seat表:

示例:

±--------±--------+

| id | student |

±--------±--------+

| 1 | Abbot |

| 2 | Doris |

| 3 | Emerson |

| 4 | Green |

| 5 | Jeames |

±--------±--------+

假如数据输入的是上表,则输出结果如下:

±--------±--------+

| id | student |

±--------±--------+

| 1 | Doris |

| 2 | Abbot |

| 3 | Green |

| 4 | Emerson |

| 5 | Jeames |

±--------±--------+

注意:

如果学生人数是奇数,则不需要改变最后一个同学的座位。

解答项目十一: 换座位(难度:中等)

小美想改变相邻俩学生的座位。

若id 为偶: 
	则把当前字段的id-1,values不变,更新到当前,
               
若id为奇:
      先判断id != 最大的id and  id/2 是否为奇数,
      则把当前的字段的id+1,values不变,更新到当前,
若id为奇,且最大:
	不变
    
最后三个select 做 union
UNION 操作符用于合并两个或多个 SELECT 语句的结果集。



请注意,UNION 内部的 SELECT 语句必须拥有相同数量的列。列也必须拥有相似的数据类型。同时,每条 SELECT 语句中的列的顺序必须相同。
https://www.w3school.com.cn/sql/sql_union.asp
## 创建表
CREATE TABLE IF NOT EXISTSv seat(
id INT NOT NULL PRIMARY KEY,
student VARCHAR(20)
);

INSERT INTO seat
VALUES
(1,'Abbot'),
(2,'Doris'),
(3,'Emerson'),
(4,'Green'),
(5,'Jeames');

mysql> select * from seat;
+----+---------+
| id | student |
+----+---------+
|  1 | Abbot   |
|  2 | Doris   |
|  3 | Emerson |
|  4 | Green   |
|  5 | Jeames  |
+----+---------+
5 rows in set (0.00 sec)

mysql>
## 若为偶
mysql> select id-1 as id, student from seat where id % 2 = 0;
+----+---------+
| id | student |
+----+---------+
|  1 | Doris   |
|  3 | Green   |
+----+---------+

# 若为奇 且id不最大
mysql> SELECT id + 1 AS id, student
    -> FROM seat
    -> WHERE id % 2 = 1
    -> AND id != (SELECT max(id) FROM seat );
   
+----+---------+
| id | student |
+----+---------+
|  2 | Abbot   |
|  4 | Emerson |
+----+---------+
2 rows in set (0.00 sec)

# 若为奇 且id 最大
mysql> SELECT id, student
    -> FROM seat
    -> WHERE id % 2 = 1
    -> AND id = (
    -> SELECT MAX(id)
    -> FROM seat
    -> );
+----+---------+
| id | student |
+----+---------+
|  5 | Jeames  |
+----+---------+
1 row in set (0.00 sec)
mysql>

# union

SELECT s.id, s.student
FROM (
	SELECT id - 1 AS id, student
	FROM seat
	WHERE id % 2 = 0
	
	UNION
	SELECT id + 1 AS id, student
	FROM seat
	WHERE id % 2 = 1
	AND id != (SELECT MAX(id) FROM seat)
	
	UNION
	SELECT id, student
	FROM seat
	WHERE id % 2 = 1
	AND id = (SELECT MAX(id)FROM seat)
)s 
ORDER BY s.id


+----+---------+
| id | student |
+----+---------+
|  1 | Doris   |
|  2 | Abbot   |
|  3 | Green   |
|  4 | Emerson |
|  5 | Jeames  |
+----+---------+
5 rows in set (0.00 sec)

mysql>

项目十二: 分数排名(难度:中等)

编写一个 SQL 查询来实现分数排名。如果两个分数相同,则两个分数排名(Rank)相同。请注意,平分后的下一个名次应该是下一个连续的整数值。换句话说,名次之间不应该有“间隔”。

创建以下score表:

±—±------+

| Id | Score |

±—±------+

| 1 | 3.50 |

| 2 | 3.65 |

| 3 | 4.00 |

| 4 | 3.85 |

| 5 | 4.00 |

| 6 | 3.65 |

±—±------+

例如,根据上述给定的 Scores 表,你的查询应该返回(按分数从高到低排列):

±------±-----+

| Score | Rank |

±------±-----+

| 4.00 | 1 |

| 4.00 | 1 |

| 3.85 | 2 |

| 3.65 | 3 |

| 3.65 | 3 |

| 3.50 | 4 |

±------±-----+

解答项目十二: 分数排名(难度:中等)

编写一个 SQL 查询来实现分数排名。如果两个分数相同,则两个分数排名(Rank)相同。请注意,平分后的下一个名次应该是下一个连续的整数值。换句话说,名次之间不应该有“间隔”。

1.分数排名: order by Score
2.判断分数是否相同,
	若相同, 则rank不自增
## 创建表	
CREATE TABLE IF NOT EXISTS score(
id int,
score DECIMAL(3,2)
);

INSERT INTO score
VALUES
(1,3.50),
(2,3.65),
(3,4.00),
(4,3.85),
(5,4.00),
(6,3.65);	
	
mysql> select * from score;
+------+-------+
| id   | score |
+------+-------+
|    1 |  3.50 |
|    2 |  3.65 |
|    3 |  4.00 |
|    4 |  3.85 |
|    5 |  4.00 |
|    6 |  3.65 |
+------+-------+
6 rows in set (0.00 sec)

mysql>	
https://blog.csdn.net/bmw601055/article/details/77596427
 @preScore 这种是变量声明,类似之前的set ,set @a = 1;
 :=  赋值,@preScore := 1,表示给@preScore赋值为1;
 
mysql> select @rank_tmp:=0,@pre_score:=NULL;
+--------------+------------------+
| @rank_tmp:=0 | @pre_score:=NULL |
+--------------+------------------+
|            0 | NULL             |
+--------------+------------------+
1 row in set (0.01 sec) 

SELECT id, score
	, CASE 
		WHEN @pre_score = score THEN @rank_tmp
		WHEN @pre_score := score THEN @rank_tmp := @rank_tmp + 1
	END AS 'rank'
FROM score, (
		SELECT @rank_tmp := 0, @pre_score := NULL
	) tmp
ORDER BY score.score DESC;

+------+-------+------+
| id   | score | rank |
+------+-------+------+
|    3 |  4.00 |    1 |
|    5 |  4.00 |    1 |
|    4 |  3.85 |    2 |
|    2 |  3.65 |    3 |
|    6 |  3.65 |    3 |
|    1 |  3.50 |    4 |
+------+-------+------+
6 rows in set (0.01 sec)

-- 显示的字段
SELECT id, score,
-- case 函数只返回第一个符合条件的值,剩下的case部分,when then 将不执行
-- score一样的情况时,@preScore=score时,@curRank并没有做+1操作,所以就有了并列的情况
case
-- 判断前一个socre 是否和当前score 相等,,相等就执行rank_tmp
when @pre_score = score then @rank_tmp
-- 上面如果不等,则把score 赋值给pre_socre ,@tank_tmp + 1
when @pre_score :=score then @rank_tmp:=@rank_tmp+1
end as 'rank'

-- 两个表,一个score表,一个tmp 表
from score,(select @rank_tmp:=0,@pre_score:=NULL) tmp
order by
score.score desc;

这里用了变量和 CASE WHEN 语句,十分巧妙。
在MySQL 8.x 版本引入了窗口函数,包括 rank()、dense_rank();所以可以这样写
SELECT
    id,
    score,
    dense_rank ( ) over ( ORDER BY score.score DESC ) AS 'rank' 
FROM
    score 
ORDER BY
    score DESC;

项目十三:连续出现的数字(难度:中等)

编写一个 SQL 查询,查找所有至少连续出现三次的数字。

±—±----+

| Id | Num |

±—±----+

| 1 | 1 |

| 2 | 1 |

| 3 | 1 |

| 4 | 2 |

| 5 | 1 |

| 6 | 2 |

| 7 | 2 |

±—±----+

例如,给定上面的 Logs 表, 1 是唯一连续出现至少三次的数字。

±----------------+

| ConsecutiveNums |

±----------------+

| 1 |

±----------------+

解答项目十三:连续出现的数字(难度:中等)

编写一个 SQL 查询,查找所有至少连续出现三次的数字。

1.先分组,再统计
## 创建表,数据

DROP TABLE IF EXISTS logs;
Create table If Not Exists Logs (Id int,Num int);
Truncate table Logs;
insert into Logs (Id, Num) values ('1','1');
insert into Logs (Id, Num) values ('2','1');
insert into Logs (Id, Num) values ('3', '1');
insert into Logs (Id, Num) values ('4','2');
insert into Logs (Id, Num) values ('5','1');
insert into Logs (Id, Num) values ('6','2');
insert into Logs (Id, Num) values ('7','2');
mysql> select * from Logs;
+------+------+
| Id   | Num  |
+------+------+
|    1 |    1 |
|    2 |    1 |
|    3 |    1 |
|    4 |    2 |
|    5 |    1 |
|    6 |    2 |
|    7 |    2 |
+------+------+
7 rows in set (0.00 sec)

mysql>

http://www.mamicode.com/info-detail-2268496.html
[LeetCode]刷题---连续出现的数字
https://www.cnblogs.com/gered/p/9262670.html
查找至少连续出现三次的所有数字/连续3天的日期【LeetCode】
https://blog.csdn.net/wal1314520/article/details/80107916
【LeetCode】180.连续出现的数字
https://www.jianshu.com/p/8f0941271e2a
【LeetCode】180. 连续出现的数字

看到这种题目,博主一开始脑子一片空白,不知如何下手,毕竟以前没遇到过这种类型的sql查询,这题的重点是连续。
其实也很简单,连续,无非就是将三张表连接起来,然后ID是递增,并且Num相等。

解法一:多表查询
mysql> SELECT DISTINCT l1.Num as ConsecutiveNums
    -> FROM Logs l1, Logs l2, Logs l3
    -> WHERE (l1.id = l2.id - 1
    -> AND l2.id = l3.id - 1
    -> AND l1.Num = l2.Num
    -> AND l2.Num = l3.Num);
+-----------------+
| ConsecutiveNums |
+-----------------+
|               1 |
+-----------------+
1 row in set (0.00 sec)

mysql>

解法二:联表查询
mysql> SELECT DISTINCT l1.Num AS ConsecutiveNums
    -> FROM Logs l1
    -> JOIN Logs l2 ON l1.id = l2.id - 1
    -> JOIN Logs l3 ON l2.id = l3.id - 1
    -> WHERE l1.Num = l2.Num
    -> AND l2.Num = l3.Num;
+-----------------+
| ConsecutiveNums |
+-----------------+
|               1 |
+-----------------+
1 row in set (0.00 sec)

mysql>


# 解法三:取巧,因id 递增,而数值不增,那么相邻相减,数值为零,统计此列0的个数大于3的即可(需8.0支持)
# 解法四:用到了变量count和pre
再来看一种画风截然不同的方法,用到了变量count和pre,分别初始化为0和-1,然后需要注意的是用到了IF语句,MySQL里的IF语句和我们所熟知的其他语言的if不太一样,相当于我们所熟悉的三元操作符a?b:c,若a真返回b,否则返回c,具体可看这个帖子。那么我们先来看对于Num列的第一个数字1,pre由于初始化是-1,和当前Num不同,所以此时count赋1,此时给pre赋为1,然后Num列的第二个1进来,此时的pre和Num相同了,count自增1,到Num列的第三个1进来,count增加到了3,此时满足了where条件,t.n >= 3,所以1就被select出来了,以此类推遍历完整个Num就可以得到最终结果:
https://www.cnblogs.com/gered/p/9262670.html

mysql> SELECT DISTINCT num AS ConsecutiveNums
    -> FROM (
    -> SELECT Num
    -> , @count := if(@pre = Num, @count + 1, 1) AS n  --新增一个字段,count
    -> , @pre := Num
    -> FROM Logs, (
    -> SELECT @count := 0, @pre := -1  --初始化变量count,pre
    -> ) init
    -> ) t
    -> WHERE t.n >= 3;  --n,作为count的别名, 统计t.n里大于3的个数
+-----------------+
| ConsecutiveNums |
+-----------------+
|               1 |
+-----------------+
1 row in set (0.01 sec)

mysql>

项目十四:树节点 (难度:中等)

对于 tree 表,id 是树节点的标识,p_id 是其父节点的 id

±—±-----+

| id | p_id |

±—±-----+

| 1 | null |

| 2 | 1 |

| 3 | 1 |

| 4 | 2 |

| 5 | 2 |

±—±-----+

每个节点都是以下三种类型中的一种:

  • Leaf: 如果节点是根节点。

  • Root: 如果节点是叶子节点。

  • Inner: 如果节点既不是根节点也不是叶子节点。

写一条查询语句打印节点id及对应的节点类型。按照节点id排序。上面例子的对应结果为:

±—±-----+

| id | Type |

±—±-----+

| 1 | Root |

| 2 | Inner|

| 3 | Leaf |

| 4 | Leaf |

| 5 | Leaf |

±—±-----+

说明

  • 节点’1’是根节点,因为它的父节点为NULL,有’2’和’3’两个子节点。

  • 节点’2’是内部节点,因为它的父节点是’1’,有子节点’4’和’5’。

  • 节点’3’,‘4’,'5’是叶子节点,因为它们有父节点但没有子节点。

下面是树的图形:

​ 1

​ / \

​ 2 3

/ \

4 5

注意

如果一个树只有一个节点,只需要输出根节点属性。

解答项目十四:树节点 (难度:中等)

https://www.jianshu.com/p/c2812e5c2caa
完结篇:LeetCode数据库最新44题190528Oracle版(3)
https://www.jb51.net/article/87318.htm
MySQL实现树状所有子节点查询的方法
https://zhidao.baidu.com/question/359985270.html
求高手帮忙sql写法:树节点放一个表中,怎么用一条语句查询一个节点及对应的所有父节点信息。

## 创建表,插入数据
CREATE TABLE IF NOT EXISTS tree(
Id int PRIMARY KEY,
p_id varchar(20)
);

INSERT INTO tree VALUES('1', NULL);
INSERT INTO tree VALUES('2', '1');
INSERT INTO tree VALUES('3', '1');
INSERT INTO tree VALUES('4', '2');
INSERT INTO tree VALUES('5', '2');


mysql> select * from tree;
+----+------+
| Id | p_id |
+----+------+
|  1 | NULL |
|  2 | 1    |
|  3 | 1    |
|  4 | 2    |
|  5 | 2    |
+----+------+
5 rows in set (0.00 sec)

mysql>

**说明**

下面是树的图形:

​        1

​      /   \

​    2       3

  /   \

4       5
+----+------+

| id | Type |

+----+------+

| 1  | Root |

| 2  | Inner|

| 3  | Leaf |

| 4  | Leaf |

| 5  | Leaf |

+----+------+

# 分解
分别以其条件查询,再union
- 节点’1’是根节点,因为它的父节点为NULL,有’2’和’3’两个子节点。
    **注意**如果一个树只有一个节点,只需要输出根节点属性。
- 节点’2’是内部节点,因为它的父节点是’1’,有子节点’4’和’5’。
- 节点’3’,‘4’,'5’是叶子节点,因为它们有父节点但没有子节点。

## 根节点 '1' 
只要查询出父节点为null的就是根节点

mysql>
SELECT id, 'Root' AS type
FROM tree
WHERE p_id IS NULL;
mysql> 
+----+------+
| id | type |
+----+------+
|  1 | Root |
+----+------+
1 row in set (0.00 sec)
mysql>

## 节点’2’是内部节点
内部节点条件:  p_id 集合里有自己(排除根节点),且自己的p_id不为null(排除根节点)

mysql>
-- 要显示的字段
SELECT id, 'Inner' AS type 
FROM tree
wHERE id IN (
		SELECT DISTINCT p_id     -- 所有p_id 的集合  
		FROM tree
		WHERE p_id IS NOT NULL   -- 排除根节点
	)
	AND p_id IS NOT NULL;      -- 排除根节点	
mysql> 
+----+-------+
| id | type  |
+----+-------+
|  2 | Inner |
+----+-------+
1 row in set (0.00 sec)

mysql>

## 节点’3’,‘4’,'5’是叶子节点,因为它们有父节点但没有子节点。
叶子节点: 就是说节点’3’,‘4’,'5’在p_id 集合里不存在,
mysql>
SELECT id, 'Leaf' AS type
FROM tree

WHERE id NOT IN (

	SELECT DISTINCT p_id    -- 节点’3’,‘4’,'5’在p_id 集合里不存在,
	FROM tree              
	WHERE p_id IS NOT NULL  -- 排除根节点

)
AND p_id IS NOT NULL;     -- 排除根节点
mysql>
+----+-------+
| id | type  |
+----+-------+
|  3 | Inner |
|  4 | Inner |
|  5 | Inner |
+----+-------+
3 rows in set (0.00 sec)

mysql>

## 最后去除分号,做union
UNION 语句:用于将不同表中相同列中查询的数据展示出来;(不包括重复数据)
UNION ALL 语句:用于将不同表中相同列中查询的数据展示出来;(包括重复数据)

mysql>
SELECT id, 'Root' AS type
FROM tree
WHERE p_id IS NULL

UNION

SELECT id, 'Inner' AS type 
FROM tree
wHERE id IN (
		SELECT DISTINCT p_id     -- 所有p_id 的集合  
		FROM tree
		WHERE p_id IS NOT NULL   -- 排除根节点
	)
	AND p_id IS NOT NULL      -- 排除根节点

UNION

SELECT id, 'Leaf' AS type
FROM tree

WHERE id NOT IN (

	SELECT DISTINCT p_id    -- 节点’3’,‘4’,'5’在p_id 集合里不存在,
	FROM tree              
	WHERE p_id IS NOT NULL  -- 排除根节点

)
AND p_id IS NOT NULL      -- 排除根节点
ORDER BY  id;    

mysql>
+----+-------+
| id | type  |
+----+-------+
|  1 | Root  |
|  2 | Inner |
|  3 | Leaf  |
|  4 | Leaf  |
|  5 | Leaf  |
+----+-------+
5 rows in set (0.01 sec)

mysql>

https://www.jianshu.com/p/c2812e5c2caa
完结篇:LeetCode数据库最新44题190528Oracle版(3)

项目十五:至少有五名直接下属的经理 (难度:中等)

Employee 表包含所有员工及其上级的信息。每位员工都有一个Id,并且还有一个对应主管的Id(ManagerId)。

+------+----------+-----------+----------+

|Id    |Name 	  |Department |ManagerId |

+------+----------+-----------+----------+

|101   |John 	  |A 	      |null      |

|102   |Dan 	  |A 	      |101       |

|103   |James 	  |A 	      |101       |

|104   |Amy 	  |A 	      |101       |

|105   |Anne 	  |A 	      |101       |

|106   |Ron 	  |B 	      |101       |

+------+----------+-----------+----------+

针对 Employee 表,写一条SQL语句找出有5个下属的主管。对于上面的表,结果应输出:

+-------+

| Name  |

+-------+

| John  |

+-------+

注意:

没有人向自己汇报。

解答项目十五:至少有五名直接下属的经理 (难度:中等)

修改表字段排序顺序

alter table 表 名 modify 字段名 类型(长度) 【not null】first ;

添加表字段的时候控制顺序

alter table 表名 add 字段名 类型(长度) after 字段名;

mysql> select * from employee;
+----+-------+--------+-----------+--------------+
| Id | Name  | Salary | ManagerId | DepartmentId |
+----+-------+--------+-----------+--------------+
|  1 | Joe   | 70000  | 3         |            1 |
|  2 | Henry | 80000  | 4         |            2 |
|  3 | Sam   | 60000  | NUll      |            2 |
|  4 | Max   | 90000  | NULL      |            1 |
+----+-------+--------+-----------+--------------+
4 rows in set (0.00 sec)

mysql> desc employee;
+--------------+-------------+------+-----+---------+-------+
| Field        | Type        | Null | Key | Default | Extra |
+--------------+-------------+------+-----+---------+-------+
| Id           | int(11)     | NO   | PRI | NULL    |       |
| Name         | varchar(20) | YES  |     | NULL    |       |
| Salary       | varchar(20) | YES  |     | NULL    |       |
| ManagerId    | varchar(20) | YES  |     | NULL    |       |
| DepartmentId | int(11)     | NO   |     | NULL    |       |
+--------------+-------------+------+-----+---------+-------+
5 rows in set (0.00 sec)


## 修改字段顺序
mysql> ALTER TABLE employee
    -> MODIFY COLUMN ManagerId varchar(20) AFTER DepartmentId;

mysql> select * from employee;
+----+-------+--------+--------------+-----------+
| Id | Name  | Salary | DepartmentId | ManagerId |
+----+-------+--------+--------------+-----------+
|  1 | Joe   | 70000  |            1 | 3         |
|  2 | Henry | 80000  |            2 | 4         |
|  3 | Sam   | 60000  |            2 | NUll      |
|  4 | Max   | 90000  |            1 | NULL      |
+----+-------+--------+--------------+-----------+
4 rows in set (0.00 sec)

mysql>

## 修改表字段的类型
alter table 表名 modify 字段名 类型(长度);经常用

mysql> alter table employee modify DepartmentId varchar(20);
Query OK, 4 rows affected (0.09 sec)
Records: 4  Duplicates: 0  Warnings: 0

mysql> desc employee;
+--------------+-------------+------+-----+---------+-------+
| Field        | Type        | Null | Key | Default | Extra |
+--------------+-------------+------+-----+---------+-------+
| Id           | int(11)     | NO   | PRI | NULL    |       |
| Name         | varchar(20) | YES  |     | NULL    |       |
| Salary       | varchar(20) | YES  |     | NULL    |       |
| DepartmentId | varchar(20) | YES  |     | NULL    |       |
| ManagerId    | varchar(20) | YES  |     | NULL    |       |
+--------------+-------------+------+-----+---------+-------+
5 rows in set (0.00 sec)

## 插入数据
INSERT INTO employee (Id, Name, DepartmentId, ManagerId)
VALUES ('101', 'John', 'A', 'null'),
	('102', 'Dan', 'A', '101 '),
	('103', 'James', 'A', '101'),
	('104', 'Amy', 'A', '101'),
	('105', 'Anne', 'A', '101'),
	('106', 'Ron', 'B', '101');
	
	
mysql> select * from employee;
+-----+-------+--------+--------------+-----------+
| Id  | Name  | Salary | DepartmentId | ManagerId |
+-----+-------+--------+--------------+-----------+
|   1 | Joe   | 70000  | 1            | 3         |
|   2 | Henry | 80000  | 2            | 4         |
|   3 | Sam   | 60000  | 2            | NUll      |
|   4 | Max   | 90000  | 1            | NULL      |
| 101 | John  | NULL   | A            | null      |
| 102 | Dan   | NULL   | A            | 101       |
| 103 | James | NULL   | A            | 101       |
| 104 | Amy   | NULL   | A            | 101       |
| 105 | Anne  | NULL   | A            | 101       |
| 106 | Ron   | NULL   | B            | 101       |
+-----+-------+--------+--------------+-----------+
10 rows in set (0.00 sec)


## 查询 至少有五名直接下属的经理

1# 创建子表,用ManagerId分组,统计总数count(Id) as cou
2# 与Employee表,联查

mysql> SELECT Name
    -> FROM (
    -> SELECT ManagerId, COUNT(Id) AS cou
    -> FROM employee
    -> GROUP BY ManagerId
    -> ) m, employee e
    -> WHERE m.ManagerId = e.Id
    -> AND cou >= 5;
+------+
| Name |
+------+
| John |
+------+
1 row in set (0.01 sec)

mysql>

END

https://www.yiibai.com/mysql/

MySQL教程

https://www.cnblogs.com/sunchaothu/p/10447982.html

https://tool.lu/sql/,

这里有个sql美化工具,sql语句比较长的时候可以使用一下。

https://zh.sqlzoo.net/

这个是个练习 SQL 语句的网站,有需要的话可以使用一下,

https://jiaoqiyuan.cn/2018/10/22/sqlzoo-practice/

这是我之前练习时的笔记,现在有点生疏了,分享给大家,希望能对你们有所帮助。

https://blog.csdn.net/wal1314520/article/details/80107916

【LeetCode】180.连续出现的数字

MySQL任务4 - 2天

负责人:某某某

【任务四】#任务时间#

请于8月15日22:00前完成,在https://shimo.im/sheets/8tytcJVqY68tDjJv/z8q2D处打卡。逾期尚未打卡的会被清退。

** MySQL 实战 - 复杂项目****#作业#**

项目十六 分数排名 (难度:中等)

依然是昨天的分数表,实现排名功能,但是排名需要是非连续的,如下:
±------±-----+
| Score | Rank |
±------±-----+
| 4.00 | 1 |
| 4.00 | 1 |
| 3.85 | 3 |
| 3.65 | 4 |
| 3.65 | 4 |
| 3.50 | 6 |
±------±-----

解答项目十六 分数排名 (难度:中等)

查询来实现分数排名。排名连续

如果两个分数相同,则两个分数排名(Rank)相同

编写一个 SQL 查询来实现分数排名。如果两个分数相同,则两个分数排名(Rank)相同。请注意,平分后的下一个名次应该是下一个连续的整数值。换句话说,名次之间不应该有“间隔”。

1.分数排名: order by Score
2.判断分数是否相同,
	若相同, 则rank不自增
## 创建表	
CREATE TABLE IF NOT EXISTS score(
id int,
score DECIMAL(3,2)
);

INSERT INTO score
VALUES
(1,3.50),
(2,3.65),
(3,4.00),
(4,3.85),
(5,4.00),
(6,3.65);	
	
mysql> select * from score;
+------+-------+
| id   | score |
+------+-------+
|    1 |  3.50 |
|    2 |  3.65 |
|    3 |  4.00 |
|    4 |  3.85 |
|    5 |  4.00 |
|    6 |  3.65 |
+------+-------+
6 rows in set (0.00 sec)

mysql>	
https://blog.csdn.net/bmw601055/article/details/77596427
 @preScore 这种是变量声明,类似之前的set ,set @a = 1;
 :=  赋值,@preScore := 1,表示给@preScore赋值为1;
 
mysql> select @rank_tmp:=0,@pre_score:=NULL;
+--------------+------------------+
| @rank_tmp:=0 | @pre_score:=NULL |
+--------------+------------------+
|            0 | NULL             |
+--------------+------------------+
1 row in set (0.01 sec) 

SELECT id, score
	, CASE 
		WHEN @pre_score = score THEN @rank_tmp
		WHEN @pre_score := score THEN @rank_tmp := @rank_tmp + 1
	END AS 'rank'
FROM score, (
		SELECT @rank_tmp := 0, @pre_score := NULL
	) tmp
ORDER BY score.score DESC;

+------+-------+------+
| id   | score | rank |
+------+-------+------+
|    3 |  4.00 |    1 |
|    5 |  4.00 |    1 |
|    4 |  3.85 |    2 |
|    2 |  3.65 |    3 |
|    6 |  3.65 |    3 |
|    1 |  3.50 |    4 |
+------+-------+------+
6 rows in set (0.01 sec)

-- 显示的字段
SELECT id, score,
-- case 函数只返回第一个符合条件的值,剩下的case部分,when then 将不执行
-- score一样的情况时,@preScore=score时,@curRank并没有做+1操作,所以就有了并列的情况
case
-- 判断前一个socre 是否和当前score 相等,,相等就执行rank_tmp
when @pre_score = score then @rank_tmp
-- 上面如果不等,则把score 赋值给pre_socre ,@tank_tmp + 1
when @pre_score :=score then @rank_tmp:=@rank_tmp+1
end as 'rank'

-- 两个表,一个score表,一个tmp 表
from score,(select @rank_tmp:=0,@pre_score:=NULL) tmp
order by
score.score desc;

这里用了变量和 CASE WHEN 语句,十分巧妙。
在MySQL 8.x 版本引入了窗口函数,包括 rank()、dense_rank();所以可以这样写
SELECT
    id,
    score,
    dense_rank ( ) over ( ORDER BY score.score DESC ) AS 'rank' 
FROM
    score 
ORDER BY
    score DESC;

查询实现分数排名,排名非连续的

https://blog.csdn.net/qq_37155959/article/details/81012030

在MySQL中实现Rank高级排名函数

https://blog.csdn.net/qq_37155959/article/details/81014909

介绍mysql 的三种排名,由简到难

mysql中if()函数使用](https://www.cnblogs.com/zjdxr-up/p/8383609.html)

mysql> select * from score;
+------+-------+
| id   | score |
+------+-------+
|    1 |  3.50 |
|    2 |  3.65 |
|    3 |  4.00 |
|    4 |  3.85 |
|    5 |  4.00 |
|    6 |  3.65 |
+------+-------+
6 rows in set (0.01 sec)


实现排名功能,但是排名需要是非连续的
1.分数排名: order by Score
2.非连续排序
    a.则需要构建一个变量curRank,这个变量可以根据情况的不同,
    	输出不同的值(@curRank(不自增的),@incRank(自增的));
    b.有@curRank as Rank, @incRank, @prevRank 组成一个临时子表;
    c.从临时子表筛选出要显示的字段即可


## 临时子表
mysql>
SELECT Score
		, @curRank := IF(@prevRank = Score, @curRank, @incRank) AS Rank
		, @incRank := @incRank + 1
		, @prevRank := Score
	FROM score, (
			SELECT @curRank := 0, @prevRank := NULL
				, @incRank := 1
		) tmp
	ORDER BY Score DESC
mysql>
+-------+------+--------------------------+--------------------+
| Score | Rank | @incRank := @incRank + 1 | @prevRank := Score |
+-------+------+--------------------------+--------------------+
|  4.00 | 1    |                        2 |               4.00 |
|  4.00 | 1    |                        3 |               4.00 |
|  3.85 | 3    |                        4 |               3.85 |
|  3.65 | 4    |                        5 |               3.65 |
|  3.65 | 4    |                        6 |               3.65 |
|  3.50 | 6    |                        7 |               3.50 |
+-------+------+--------------------------+--------------------+
6 rows in set (0.00 sec)

## 筛选临时子表
mysql>
-- 在mysql中if()函数的用法类似于java中的三目表达式,其用处也比较多,具体语法如下:
-- IF(expr1,expr2,expr3),如果expr1的值为true,则返回expr2的值,如果expr1的值为false,
-- 则返回expr3的值。
SELECT Score, Rank
FROM (
	SELECT Score
		, @curRank := IF(@prevRank = Score, @curRank, @incRank) AS Rank 
		, @incRank := @incRank + 1  -- 自增
		, @prevRank := Score        
	FROM score, (
			SELECT @curRank := 0, @prevRank := NULL
				, @incRank := 1        -- 初始化
		) tmp
	ORDER BY Score DESC 
) tmp1;
mysql>
+-------+------+
| Score | Rank |
+-------+------+
|  4.00 | 1    |
|  4.00 | 1    |
|  3.85 | 3    |
|  3.65 | 4    |
|  3.65 | 4    |
|  3.50 | 6    |
+-------+------+
6 rows in set (0.00 sec)

mysql>

##  最简单的 思路直接就是使用 MySQL 8.x支持的窗口函数
SELECT
    id,
    score,
    rank ( ) over ( ORDER BY score.score DESC ) AS 'rank' 
FROM
    score 
ORDER BY
    score DESC;

项目十七:查询回答率最高的问题 (难度:中等)

求出survey_log表中回答率最高的问题,表格的字段有:uid, action, question_id, answer_id, q_num, timestamp
uid是用户id;action的值为:“show”, “answer”, “skip”;当action是"answer"时,answer_id不为空,相反,当action是"show"和"skip"时为空(null);q_num是问题的数字序号。
写一条sql语句找出回答率最高的问题。
举例:

输入

+------+--------+----------+-----------+-------+-----------+
| uid  | action | question_id | answer_id | q_num | timestamp |
+------+--------+----------+-----------+-------+-----------+
|    5 | show   |      285 |      NULL |     1 | 123       |
|    5 | answer |      285 |    124124 |     1 | 124       |
|    5 | show   |      369 |      NULL |     2 | 125       |
|    5 | skip   |      369 |      NULL |     2 | 126       |
+------+--------+----------+-----------+-------+-----------+

输出

+------------+
| survey_log |
+------------+
| 285        |
+------------+

说明
问题285的回答率为1/1,然而问题369的回答率是0/1,所以输出是285。
注意: 最高回答率的意思是:同一个问题出现的次数中回答的比例。

解答项目十七:查询回答率最高的问题 (难度:中等)

https://blog.csdn.net/weixin_43329319/article/details/96367844

https://blog.csdn.net/albert184/article/details/90576349

求什么比率一般都是:
(sum(case when `action` like 'answer' then 1 else 0 end) / sum(case when `action` like 'show' then 1 else 0 end)) as rate
mysql>
CREATE TABLE IF NOT EXISTS survey_log(
id int PRIMARY KEY,
uid int(11) ,
action varchar(20),
question_id int(11),
answer_id int(11),
q_num int(11),
timestamp varchar(20)
);

INSERT INTO survey_log VALUES('1', '5', 'show', '285', null, '1', '123');
INSERT INTO survey_log VALUES('2', '5', 'answer', '285', '124124', '1', '124');
INSERT INTO survey_log VALUES('3', '5', 'show', '369', null, '2', '125');
INSERT INTO survey_log VALUES('4', '5', 'skip', '369', null, '2', '126');
mysql>

mysql> select * from survey_log;
+----+------+--------+----------+-----------+-------+-----------+
| id | uid  | action | question_id | answer_id | q_num | timestamp |
+----+------+--------+----------+-----------+-------+-----------+
|  1 |    5 | show   |      285 |      NULL |     1 | 123       |
|  2 |    5 | answer |      285 |    124124 |     1 | 124       |
|  3 |    5 | show   |      369 |      NULL |     2 | 125       |
|  4 |    5 | skip   |      369 |      NULL |     2 | 126       |
+----+------+--------+----------+-----------+-------+-----------+
4 rows in set (0.00 sec)

插曲:表字段改名
alter table 表名 change 原来的字段名 字段新名字 类型(长度);
ALTER TABLE survey_log CHANGE question question_id VARCHAR(20) 
mysql>

## 思路:
回答率最高
先统计,再分组,order by queston 
然后 求比率 
最后 排序 取第一个

## 统计
-- SELECT sum(question_id) FROM survey_log WHERE action LIKE 'show';
--对action 为answer的进行统计,like 比=运行开快 
SELECT SUM(CASE 
		WHEN action LIKE 'answer' THEN 1
		ELSE 0
	END)
FROM survey_log;

## 分组,求比率, 排序,
SELECT SUM(CASE 
		WHEN action LIKE 'answer' THEN 1 
		ELSE 0
	END) / SUM(CASE 
		WHEN action LIKE 'show' THEN 1
		ELSE 0
	END) AS rate, question_id
FROM survey_log
GROUP BY question_id
ORDER BY rate DESC;
mysql>
+--------+-------------+
| rate   | question_id |
+--------+-------------+
| 1.0000 | 285         |
| 0.0000 | 369         |
+--------+-------------+
2 rows in set (0.00 sec)

mysql>

## 取第一个,显示字段
mysql>
SELECT question_id AS survey_log
FROM (
	SELECT SUM(CASE 
			WHEN action LIKE 'answer' THEN 1
			ELSE 0
		END) / SUM(CASE 
			WHEN action LIKE 'show' THEN 1
			ELSE 0
		END) AS rate, question_id
	FROM survey_log
	GROUP BY question_id
	ORDER BY rate DESC
	LIMIT 1
) tmp;
+------------+
| survey_log |
+------------+
| 285        |
+------------+
1 row in set (0.00 sec)

mysql>



项目十八:各部门前3高工资的员工(难度:中等)

将项目九,项目十 中的employee表清空,重新插入以下数据(其实是多插入5,6两行):
±—±------±-------±-------------+
| Id | Name | Salary | DepartmentId |
±—±------±-------±-------------+
| 1 | Joe | 70000 | 1 |
| 2 | Henry | 80000 | 2 |
| 3 | Sam | 60000 | 2 |
| 4 | Max | 90000 | 1 |
| 5 | Janet | 69000 | 1 |
| 6 | Randy | 85000 | 1 |
±—±------±-------±-------------+
编写一个 SQL 查询,找出每个部门工资前三高的员工。例如,根据上述给定的表格,查询结果应返回:
±-----------±---------±-------+
| Department | Employee | Salary |
±-----------±---------±-------+
| IT | Max | 90000 |
| IT | Randy | 85000 |
| IT | Joe | 70000 |
| Sales | Henry | 80000 |
| Sales | Sam | 60000 |
±-----------±---------±-------+

此外,请考虑实现各部门前N高工资的员工功能。

解答项目十八:各部门前3高工资的员工(难度:中等)

mysql> select * from employee;
+-----+-------+--------+--------------+-----------+
| Id  | Name  | Salary | DepartmentId | ManagerId |
+-----+-------+--------+--------------+-----------+
|   1 | Joe   | 70000  | 1            | 3         |
|   2 | Henry | 80000  | 2            | 4         |
|   3 | Sam   | 60000  | 2            | NUll      |
|   4 | Max   | 90000  | 1            | NULL      |
| 101 | John  | NULL   | A            | null      |
| 102 | Dan   | NULL   | A            | 101       |
| 103 | James | NULL   | A            | 101       |
| 104 | Amy   | NULL   | A            | 101       |
| 105 | Anne  | NULL   | A            | 101       |
| 106 | Ron   | NULL   | B            | 101       |
+-----+-------+--------+--------------+-----------+
10 rows in set (0.01 sec)
mysql> select * from department;
+----+-------+
| Id | Name  |
+----+-------+
|  1 | IT    |
|  2 | Sales |
+----+-------+
2 rows in set (0.00 sec)

## 插入数据
mysql>
INSERT INTO employee(Id,Name,Salary,DepartmentId) VALUES('5',"Janet",69000,1);
INSERT INTO employee(Id,Name,Salary,DepartmentId) VALUES('6',"Randy",85000,1);

mysql> select * from employee;
+-----+-------+--------+--------------+-----------+
| Id  | Name  | Salary | DepartmentId | ManagerId |
+-----+-------+--------+--------------+-----------+
|   1 | Joe   | 70000  | 1            | 3         |
|   2 | Henry | 80000  | 2            | 4         |
|   3 | Sam   | 60000  | 2            | NUll      |
|   4 | Max   | 90000  | 1            | NULL      |
|   5 | Janet | 69000  | 1            | NULL      |
|   6 | Randy | 85000  | 1            | NULL      |
| 101 | John  | NULL   | A            | null      |
| 102 | Dan   | NULL   | A            | 101       |
| 103 | James | NULL   | A            | 101       |
| 104 | Amy   | NULL   | A            | 101       |
| 105 | Anne  | NULL   | A            | 101       |
| 106 | Ron   | NULL   | B            | 101       |
+-----+-------+--------+--------------+-----------+
12 rows in set (0.00 sec)

mysql>

各部门前3高工资的员工
## 思路
先做join
mysql>	
SELECT d.name AS Department, e.Name AS Employee, Salary
FROM Employee e
	JOIN Department d ON e.DepartmentId = d.Id;
mysql>		
+------------+----------+--------+
| Department | Employee | Salary |
+------------+----------+--------+
| IT         | Joe      | 70000  |
| Sales      | Henry    | 80000  |
| Sales      | Sam      | 60000  |
| IT         | Max      | 90000  |
| IT         | Janet    | 69000  |
| IT         | Randy    | 85000  |
+------------+----------+--------+
6 rows in set, 12 warnings (0.00 sec)

## 再过滤前三
mysql>
SELECT d.name AS Department, e.Name AS Employee, Salary
FROM Employee e
	JOIN Department d ON e.DepartmentId = d.Id
WHERE (
	SELECT COUNT(DISTINCT em.Salary)
	FROM Employee em
	WHERE em.Salary >= e.Salary
		AND em.DepartmentId = e.DepartmentId
) <= 3
GROUP BY Department, Salary DESC;
https://blog.csdn.net/qq_41822173/article/details/80978544

注意:条件语句中必须为 3 > (...),子查询中必须是 e2.Salary>e1.Salary

为什么呢?

不妨假设e1=e2=[6,5,4,3],则子查询的过程如下:

1、e1.Salary=3;则e2.Salary可以取4、5、6;COUNT(DISTINCT e2.Salary)=3

2、e1.Salary=4;则e2.Salary可以取5、6;COUNT(DISTINCT e2.Salary)=2

3、e1.Salary=5;则e2.Salary可以取6;COUNT(DISTINCT e2.Salary)=1

4、e1.Salary=6;则e2.Salary无法取值;COUNT(DISTINCT e2.Salary)=0

则要令COUNT(DISTINCT e2.Salary)  < 3 的情况有上述的4、3、2.

也即是说,这等价于取e1.Salary最大的三个值。
--------------------- 
+------------+----------+--------+
| Department | Employee | Salary |
+------------+----------+--------+
| IT         | Max      | 90000  |
| IT         | Randy    | 85000  |
| IT         | Joe      | 70000  |
| Sales      | Henry    | 80000  |
| Sales      | Sam      | 60000  |
+------------+----------+--------+
5 rows in set, 12 warnings (0.00 sec)

mysql>

## 解法二 多表查询
mysql>
SELECT D1.NAME AS DepartmentId, E1.NAME AS employee, E1.Salary
FROM employee E1, employee E2, department D1
WHERE (E1.DepartmentId = E2.DepartmentId
	AND E2.Salary >= E1.Salary
	AND E1.DepartmentId = D1.Id)
GROUP BY E1.Name
HAVING COUNT(DISTINCT E2.Salary) <= 3
ORDER BY D1.Name, E1.Salary DESC;
mysql>
+--------------+----------+--------+
| DepartmentId | employee | Salary |
+--------------+----------+--------+
| IT           | Max      | 90000  |
| IT           | Randy    | 85000  |
| IT           | Joe      | 70000  |
| Sales        | Henry    | 80000  |
| Sales        | Sam      | 60000  |
+--------------+----------+--------+
5 rows in set, 12 warnings (0.00 sec)

mysql>
https://www.cnblogs.com/MsHibiscus/p/10661312.html

## 解法三 8.0以上
SELECT 
    d.name AS Department, e.Name AS Employee, Salary
FROM
    Employee e
        JOIN
    Department d ON e.DepartmentId = d.Id
WHERE
    (SELECT 
            COUNT(DISTINCT em.Salary)
        FROM
            Employee em
        WHERE
            em.Salary >= e.Salary
                AND em.DepartmentId = e.DepartmentId) <= 3
GROUP BY Department , Salary DESC;

项目十九:平面上最近距离

point_2d 表包含一个平面内一些点(超过两个)的坐标值(x,y)。
写一条查询语句求出这些点中的最短距离并保留2位小数。

xy
-1-1
00
-1-2

最短距离是1,从点(-1,-1)到点(-1,2)。所以输出结果为:

shortest
1.00

注意: 所有点的最大距离小于10000。

解答项目十九:平面上最近距离

## 创建表,插入数据
CREATE TABLE IF NOT EXISTS point_2d (
	x int(11),
	y int(11)
);

INSERT INTO point_2d
VALUES (-1, -1),
	(0, 0),
	(-1, -2);

mysql> select * from point_2d;
+------+------+
| x    | y    |
+------+------+
|   -1 |   -1 |
|    0 |    0 |
|   -1 |   -2 |
+------+------+
3 rows in set (0.00 sec)

mysql>
SELECT MIN(distance) AS shortest
FROM (
	-- POWER(X,Y)这两个函数返回X的Y次方
	SELECT POW(P1.x - p2.x, 2) + POW(p1.y - p2.y, 2) AS distance
	FROM point_2d p1
		INNER JOIN point_2d p2 ON !(p1.x = p2.x
			AND p1.y = p2.y)  -- 剔除和自己比较的情况
) p

mysql>
+----------+
| shortest |
+----------+
|        1 |
+----------+
1 row in set (0.00 sec)

mysql>
https://www.yiibai.com/mysql/mysql_function_pow.html

项目二十:行程和用户(难度:困难)

Trips 表中存所有出租车的行程信息。每段行程有唯一键 Id,Client_Id 和 Driver_Id 是 Users 表中 Users_Id 的外键。Status 是枚举类型,枚举成员为 (‘completed’, ‘cancelled_by_driver’, ‘cancelled_by_client’)。
±—±----------±----------±--------±-------------------±---------+
| Id | Client_Id | Driver_Id | City_Id | Status |Request_at|
±—±----------±----------±--------±-------------------±---------+
| 1 | 1 | 10 | 1 | completed |2013-10-01|
| 2 | 2 | 11 | 1 | cancelled_by_driver|2013-10-01|
| 3 | 3 | 12 | 6 | completed |2013-10-01|
| 4 | 4 | 13 | 6 | cancelled_by_client|2013-10-01|
| 5 | 1 | 10 | 1 | completed |2013-10-02|
| 6 | 2 | 11 | 6 | completed |2013-10-02|
| 7 | 3 | 12 | 6 | completed |2013-10-02|
| 8 | 2 | 12 | 12 | completed |2013-10-03|
| 9 | 3 | 10 | 12 | completed |2013-10-03|
| 10 | 4 | 13 | 12 | cancelled_by_driver|2013-10-03|
±—±----------±----------±--------±-------------------±---------+
Users 表存所有用户。每个用户有唯一键 Users_Id。Banned 表示这个用户是否被禁止,Role 则是一个表示(‘client’, ‘driver’, ‘partner’)的枚举类型。
±---------±-------±-------+
| Users_Id | Banned | Role |
±---------±-------±-------+
| 1 | No | client |
| 2 | Yes | client |
| 3 | No | client |
| 4 | No | client |
| 10 | No | driver |
| 11 | No | driver |
| 12 | No | driver |
| 13 | No | driver |
±---------±-------±-------+
写一段 SQL 语句查出 **2013年10月1日 **至 **2013年10月3日 **期间非禁止用户的取消率。基于上表,你的 SQL 语句应返回如下结果,取消率(Cancellation Rate)保留两位小数。
±-----------±------------------+
| Day | Cancellation Rate |
±-----------±------------------+
| 2013-10-01 | 0.33 |
| 2013-10-02 | 0.00 |
| 2013-10-03 | 0.50 |
±-----------±------------------+

**【任务说明】**这是本次集训最后一次任务啦,大家再坚持一下。之前的任务中项目是不是觉得水水的?本次任务都是较为复杂的实战了。建议有能力的小伙伴可以在项目本身的基础上拓展,看能不能将问题设置的更复杂点或实现方式更灵活点。举个栗子:各部门工资最高,是不是可以实现工资第二高的甚至是第N高的? 自由发挥~
祝大家学习开心。😃

解答项目二十:行程和用户(难度:困难)


## 创建表,插入数据
DROP TABLE if EXISTS Trips;
DROP TABLE if EXISTS Users;

CREATE TABLE IF NOT EXISTS Users(
Users_Id INT UNIQUE,
Banned VARCHAR(5),
Role VARCHAR(10)		
);

CREATE TABLE Trips(
Id INT UNIQUE,
Client_Id INT,
Driver_Id INT,
City_Id INT,
Status VARCHAR(30),
Request_at Date,
CONSTRAINT FOREIGN KEY (Client_Id) REFERENCES Users(Users_Id),
CONSTRAINT FOREIGN KEY(Driver_Id) REFERENCES Users(Users_Id)
);



INSERT INTO Users
VALUES (1, 'No', 'client'),
	(2, 'Yes', 'client'),
	(3, 'No', 'client'),
	(4, 'No', 'client'),
	(10, 'No', 'driver'),
	(11, 'No', 'driver'),
	(12, 'No', 'driver'),
	(13, 'No', 'driver');

INSERT INTO Trips
VALUES (1, 1, 10, 1, 'completed'
		, '2013-10-01'),
	(2, 2, 11, 1, 'cancelled_by_driver'
		, '2013-10-01'),
	(3, 3, 12, 6, 'completed'
		, '2013-10-01'),
	(4, 4, 13, 6, 'cancelled_by_client'
		, '2013-10-01'),
	(5, 1, 10, 1, 'completed'
		, '2013-10-02'),
	(6, 2, 11, 6, 'completed'
		, '2013-10-02'),
	(7, 3, 12, 6, 'completed'
		, '2013-10-02'),
	(8, 2, 11, 12, 'completed'
		, '2013-10-03'),
	(9, 3, 10, 12, 'completed'
		, '2013-10-03'),
	(10, 4, 13, 12, 'completed_by_driver'
		, '2013-10-03');


mysql> select * from users;
+----------+--------+--------+
| Users_Id | Banned | Role   |
+----------+--------+--------+
|        1 | No     | client |
|        2 | Yes    | client |
|        3 | No     | client |
|        4 | No     | client |
|       10 | No     | driver |
|       11 | No     | driver |
|       12 | No     | driver |
|       13 | No     | driver |
+----------+--------+--------+
8 rows in set (0.00 sec)

mysql> select * from trips;
+------+-----------+-----------+---------+---------------------+------------+
| Id   | Client_Id | Driver_Id | City_Id | Status              | Request_at |
+------+-----------+-----------+---------+---------------------+------------+
|    1 |         1 |        10 |       1 | completed           | 2013-10-01 |
|    2 |         2 |        11 |       1 | cancelled_by_driver | 2013-10-01 |
|    3 |         3 |        12 |       6 | completed           | 2013-10-01 |
|    4 |         4 |        13 |       6 | cancelled_by_client | 2013-10-01 |
|    5 |         1 |        10 |       1 | completed           | 2013-10-02 |
|    6 |         2 |        11 |       6 | completed           | 2013-10-02 |
|    7 |         3 |        12 |       6 | completed           | 2013-10-02 |
|    8 |         2 |        11 |      12 | completed           | 2013-10-03 |
|    9 |         3 |        10 |      12 | completed           | 2013-10-03 |
|   10 |         4 |        13 |      12 | completed_by_driver | 2013-10-03 |
+------+-----------+-----------+---------+---------------------+------------+
10 rows in set (0.00 sec)

mysql>


写一段 SQL 语句查出 2013年10月1日至2013年10月3日 期间非禁止用户的取消率。基于上表,你的 SQL 语句应返回如下结果,取消率(Cancellation Rate)保留两位小数。

## 思路
日期 : 分组group by
非禁止用户: not in
取消率 : 统计
显示字段, 日期, 取消率


## 受禁止的用户user_id	
mysql> 
SELECT Users_Id AS userid
FROM Users
WHERE Banned = 'Yes'
AND Role = 'client';
+--------+
| userid |
+--------+
|      2 |
+--------+
1 row in set (0.00 sec)
mysql>
## 排除受禁止的用户user_id
WHERE Client_id NOT IN (     -- 排除受禁止用户的user_id     
	SELECT Users_Id AS userid
	FROM Users
	WHERE Banned = 'Yes'
		AND Role = 'client';  -- 查询受禁止用户的user_id
## 计算取消率
mysql>
SELECT SUM(Status != 'completed') / COUNT(*) -- 未完成的/总的
FROM trips
GROUP BY Request_at   -- 通过日期分组 
mysql>
+---------------------------------------+
| SUM(Status != 'completed') / COUNT(*) |
+---------------------------------------+
|                                0.5000 |
|                                0.0000 |
|                                0.3333 |
+---------------------------------------+
3 rows in set (0.00 sec)

mysql>

## 整合
DATEDIFF(date_expression_1,date_expression_2)
DATEDIFF函数接受两个任何有效日期或日期时间值的参数。如果您传递DATETIME或TIMESTAMP值,则DATEDIFF函数仅将日期部分用于计算,并忽略时间部分。
DATEDIFF函数在许多情况下很有用,例如,您可以计算产品需要发送给客户的间隔时间。原文出自【易百教程】,商业转载请联系作者获得授权,非商业请保留原文链接:https://www.yiibai.com/mysql/datediff.html

mysql>
SELECT Request_at AS Day
	, SUM(Status != 'completed') / COUNT(*) AS 'Cancellation Rate' -- 计算取消率
FROM trips
WHERE Client_Id NOT IN (            -- 排除受禁止的user_id 
		SELECT Users_Id AS userid       -- 查询受禁止的user_id
		FROM Users
		WHERE Banned = 'Yes'
			AND Role = 'client'
	)
	AND DATEDIFF(Request_at, '2013-10-01') >= 0  -- 计算时间间隔
	AND DATEDiFF(Request_at, '2013-10-01') <= 2
GROUP BY Request_at
;
mysql>
+------------+-------------------+
| Day        | Cancellation Rate |
+------------+-------------------+
| 2013-10-01 |            0.3333 |
| 2013-10-02 |            0.0000 |
| 2013-10-03 |            0.5000 |
+------------+-------------------+
3 rows in set (0.01 sec)

mysql>


END

https://www.yiibai.com/mysql/

MySQL教程

https://www.cnblogs.com/Thegonedays/p/10472829.html

https://www.cnblogs.com/MsHibiscus/p/10661312.html

https://www.cnblogs.com/sunchaothu/p/10447982.html

https://blog.csdn.net/linjiawen1996/article/details/99584150

https://tool.lu/sql/,

这里有个sql美化工具,sql语句比较长的时候可以使用一下。

https://zh.sqlzoo.net/

这个是个练习 SQL 语句的网站,有需要的话可以使用一下,

https://jiaoqiyuan.cn/2018/10/22/sqlzoo-practice/

这是我之前练习时的笔记,现在有点生疏了,分享给大家,希望能对你们有所帮助。

https://blog.csdn.net/wal1314520/article/details/80107916

【LeetCode】180.连续出现的数字

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值