项目:更新 README
This commit is contained in:
parent
2a7f862377
commit
f392022841
@ -25,8 +25,8 @@ Wiki
|
||||
│ └─ 编译原理
|
||||
├─ 职业
|
||||
│ ├─ 求职之路
|
||||
│ ├─ 运维开发工程师
|
||||
│ └─ 信息安全工程师
|
||||
│ ├─ SRE 工程师
|
||||
│ └─ Data 工程师
|
||||
├─ 组织
|
||||
│ ├─ 开源社区
|
||||
│ ├─ 现代企业
|
||||
@ -39,7 +39,8 @@ Wiki
|
||||
|
||||
```markdown
|
||||
Main 主分支
|
||||
├─ dev 开发分支
|
||||
├─ dev 合并分支
|
||||
├─ mac 移动分支
|
||||
└─ phone 移动分支
|
||||
```
|
||||
|
||||
|
@ -1,102 +0,0 @@
|
||||
---
|
||||
title: SQL 函数
|
||||
description: SQL 函数
|
||||
keywords:
|
||||
- 数据库
|
||||
- 函数
|
||||
tags:
|
||||
- SQL
|
||||
sidebar_position: 3
|
||||
author: 7Wate
|
||||
date: 2022-08-31
|
||||
---
|
||||
## 函数
|
||||
|
||||
函数是指一段可以直接被另一段程序调用的程序或代码。
|
||||
|
||||
1. 在企业的 OA 或其他的人力系统中,经常会提供的有这样一个功能,每一个员工登录上来之后都能 够看到当前员工入职的天数。 而在数据库中,存储的都是入职日期,如 2000-11-12,**那如果快速计 算出天数呢?**
|
||||
2. 在做报表这类的业务需求中, 我们要展示出学员的分数等级分布。而在数据库中,存储的是学生的 分数值,如 98/75,**如何快速判定分数的等级呢?**
|
||||
|
||||
以上这些需求都可以在 MySQL 的函数中得到方便解决。
|
||||
|
||||
### 字符串函数
|
||||
|
||||
常用的字符串函数
|
||||
|
||||
| 函数 | 功能 |
|
||||
| :------------------------- | :----------------------------------------------------------- |
|
||||
| CONCAT(s1, s2, …, sn) | 字符串拼接,将 s1, s2, …, sn 拼接成一个字符串 |
|
||||
| LOWER(str) | 将字符串全部转为小写 |
|
||||
| UPPER(str) | 将字符串全部转为大写 |
|
||||
| LPAD(str, n, pad) | 左填充,用字符串 pad 对 str 的左边进行填充,达到 n 个字符串长度 |
|
||||
| RPAD(str, n, pad) | 右填充,用字符串 pad 对 str 的右边进行填充,达到 n 个字符串长度 |
|
||||
| TRIM(str) | 去掉字符串头部和尾部的空格 |
|
||||
| SUBSTRING(str, start, len) | 返回从字符串 str 从 start 位置起的 len 个长度的字符串,起始索引为 1 |
|
||||
|
||||
案例:由于业务需求变更,企业员工的工号,统一为 5 位数,目前不足 5 位数的全部在前面补 0。比如: 1 号员 工的工号应该为 00001。
|
||||
|
||||
```sql
|
||||
update emp set workno = LPAD(workno,5,'0');
|
||||
```
|
||||
|
||||
### 数值函数
|
||||
|
||||
常用的数值函数
|
||||
|
||||
| 函数 | 功能 |
|
||||
| :---------- | :----------------------------------- |
|
||||
| CEIL(x) | 向上取整 |
|
||||
| FLOOR(x) | 向下取整 |
|
||||
| MOD(x, y) | 返回 x/y 的模 |
|
||||
| RAND() | 返回 0~1 内的随机数 |
|
||||
| ROUND(x, y) | 求参数 x 的四舍五入值,保留 y 位小数 |
|
||||
|
||||
案例:通过数据库的函数,生成一个六位数的随机验证码。
|
||||
|
||||
```sql
|
||||
-- 通过rand()获取0~1,乘以1000000,通过四舍五入即可。如果未满6位,则补0
|
||||
select lpad(round(rand()*1000000, 0), 6, '0');
|
||||
```
|
||||
|
||||
### 日期函数
|
||||
|
||||
常用日期函数
|
||||
|
||||
| 函数 | 功能 |
|
||||
| :--------------------------------- | :---------------------------------------------------- |
|
||||
| CURDATE() | 返回当前日期 |
|
||||
| CURTIME() | 返回当前时间 |
|
||||
| NOW() | 返回当前日期和时间 |
|
||||
| YEAR(date) | 获取指定 date 的年份 |
|
||||
| MONTH(date) | 获取指定 date 的月份 |
|
||||
| DAY(date) | 获取指定 date 的日期 |
|
||||
| DATE_ADD(date, INTERVAL expr type) | 返回一个日期 / 时间值加上一个时间间隔 expr 后的时间值 |
|
||||
| DATEDIFF(date1, date2) | 返回起始时间 date1 和结束时间 date2 之间的天数 |
|
||||
|
||||
案例:查询所有员工的入职天数,并根据入职时间倒叙排序
|
||||
|
||||
```sql
|
||||
select name, datediff(curdate(), entrydate) as dates from emp order by dates desc;
|
||||
```
|
||||
|
||||
### 流程函数
|
||||
|
||||
常用流程函数
|
||||
|
||||
| 函数 | 功能 |
|
||||
| :----------------------------------------------------------- | :----------------------------------------------------------- |
|
||||
| IF(value, t, f) | 如果 value 为 true,则返回 t,否则返回 f |
|
||||
| IFNULL(value1, value2) | 如果 value1 不为空,返回 value1,否则返回 value2 |
|
||||
| CASE WHEN [val1] THEN [ res1 ] … ELSE [ default ] END | 如果 val1 为 true,返回 res1,… 否则返回 default 默认值 |
|
||||
| CASE [expr] WHEN [ val1 ] THEN [ res1 ] … ELSE [ default ] END | 如果 expr 的值等于 val1,返回 res1,… 否则返回 default 默认值 |
|
||||
|
||||
```sql
|
||||
select
|
||||
name,
|
||||
if(age between 14 and 28, '青年','其他')
|
||||
from emp;
|
||||
select
|
||||
name,
|
||||
(case workaddress when '北京市' then '一线城市' when '上海市' then '一线城市' else '二线城市' end) as '工作地址'
|
||||
from employee;
|
||||
```
|
@ -1,507 +0,0 @@
|
||||
---
|
||||
title: SQL 语句
|
||||
description: SQL 语句
|
||||
keywords:
|
||||
- SQL
|
||||
- 语句
|
||||
tags:
|
||||
- SQL
|
||||
sidebar_position: 2
|
||||
author: 7Wate
|
||||
date: 2022-08-31
|
||||
---
|
||||
## SQL
|
||||
|
||||
全称 Structured Query Language,结构化查询语言。操作关系型数据库的编程语言,定义了一套操作关系型数据库统一标准。
|
||||
|
||||
![img](https://static.7wate.com/img/2022/05/11/9459b0983c451.png)
|
||||
|
||||
1. SQL 语句可以单行或多行书写,以分号结尾。
|
||||
2. SQL 语句可以使用空格 / 缩进来增强语句的可读性。
|
||||
3. MySQL 数据库的 SQL 语句不区分大小写,**关键字建议使用大写**。
|
||||
4. 注释:
|
||||
1. 单行注释:-- 注释内容 或 # 注释内容
|
||||
2. 多行注释:/ *注释内容* /
|
||||
|
||||
SQL 语句,根据其功能,主要分为四类:DDL、DML、DQL、DCL。
|
||||
|
||||
| 分类 | 说明 |
|
||||
| :--- | :----------------------------------------------------- |
|
||||
| DDL | 数据定义语言,用来定义数据库对象 (数据库,表,字段) |
|
||||
| DML | 数据操作语言, 用来对数据库表中的数据进行增删改 |
|
||||
| DQL | 数据查询语言,用来查询数据库中表的记录 |
|
||||
| DCL | 数据控制语言,用来创建数据库用户、控制数据库的访问权限 |
|
||||
|
||||
## DDL
|
||||
|
||||
数据定义语言,定义数据库对象 (数据库,表,字段)
|
||||
|
||||
### 数据库操作
|
||||
|
||||
查询所有数据库
|
||||
|
||||
```sql
|
||||
SHOW DATABASES
|
||||
```
|
||||
|
||||
使用某个数据库
|
||||
|
||||
```sql
|
||||
USE 数据库名;
|
||||
```
|
||||
|
||||
查询当前数据库
|
||||
|
||||
```sql
|
||||
SELECT DATABASE();
|
||||
```
|
||||
|
||||
创建数据库
|
||||
|
||||
UTF8 字符集长度为 3 字节,有些符号占 4 字节,所以创建数据库时推荐用 utf8mb4 字符集
|
||||
|
||||
```sql
|
||||
CREATE DATABASE [ IF NOT EXISTS ] 数据库名 [ DEFAULT CHARSET 字符集] [COLLATE 排序规则 ];
|
||||
```
|
||||
|
||||
删除数据库
|
||||
|
||||
```sql
|
||||
DROP DATABASE [ IF EXISTS ] 数据库名;
|
||||
```
|
||||
|
||||
### 数据表操作
|
||||
|
||||
创建表
|
||||
|
||||
最后一个字段后面是没有逗号的。
|
||||
|
||||
```sql
|
||||
CREATE TABLE 表名(
|
||||
字段1 字段1类型 [COMMENT 字段1注释],
|
||||
字段2 字段2类型 [COMMENT 字段2注释],
|
||||
字段3 字段3类型 [COMMENT 字段3注释],
|
||||
...
|
||||
字段n 字段n类型 [COMMENT 字段n注释]
|
||||
)[ COMMENT 表注释 ];
|
||||
```
|
||||
|
||||
查询表结构
|
||||
|
||||
```sql
|
||||
DESC 表名;
|
||||
```
|
||||
|
||||
查询当前数据库所有表,**必须处在数据库中**
|
||||
|
||||
```sql
|
||||
SHOW TABLES;
|
||||
```
|
||||
|
||||
查询某表的建表语句
|
||||
|
||||
```sql
|
||||
SHOW CREATE TABLE 表名;
|
||||
```
|
||||
|
||||
表中添加字段
|
||||
|
||||
```sql
|
||||
ALTER TABLE 表名 ADD 字段名 类型(长度) [COMMENT 注释] [约束];
|
||||
-- 例如
|
||||
ALTER TABLE emp ADD nickname varchar(20) COMMENT '用户昵称';
|
||||
```
|
||||
|
||||
修改数据类型
|
||||
|
||||
```sql
|
||||
ALTER TABLE 表名 MODIFY 字段名 新数据类型(长度);
|
||||
-- 例如
|
||||
ALTER TABLE emp MODIFY nickname varchar(30);
|
||||
```
|
||||
|
||||
修改字段名和字段类型
|
||||
|
||||
```sql
|
||||
ALTER TABLE 表名 CHANGE 旧字段名 新字段名 类型(长度) [COMMENT 注释] [约束];
|
||||
-- 例如
|
||||
ALTER TABLE emp CHANGE nickname name varchar(40);
|
||||
```
|
||||
|
||||
删除字段
|
||||
|
||||
```sql
|
||||
ALTER TABLE 表名 DROP 字段名;
|
||||
-- 例如
|
||||
ALTER TABLE emp drop nickname;
|
||||
```
|
||||
|
||||
修改表名
|
||||
|
||||
```sql
|
||||
ALTER TABLE 表名 RENAME TO 新表名;
|
||||
-- 例如
|
||||
ALTER TABLE emp RENAME TO empNew;
|
||||
```
|
||||
|
||||
删除表
|
||||
|
||||
```sql
|
||||
DROP TABLE [IF EXISTS] 表名;
|
||||
-- 例如
|
||||
DROP TABLE [IF EXISTS] emp;
|
||||
```
|
||||
|
||||
删除表,并重新创建该表
|
||||
|
||||
```sql
|
||||
TRUNCATE TABLE 表名;
|
||||
-- 例如
|
||||
TRUNCATE TABLE emp;
|
||||
```
|
||||
|
||||
## DML
|
||||
|
||||
数据操作语言,用来对数据库表中的数据进行增删改
|
||||
|
||||
字符串和日期类型数据应该包含在引号中
|
||||
|
||||
插入的数据大小应该在**字段的规定范围**内
|
||||
|
||||
指定字段添加数据
|
||||
|
||||
```sql
|
||||
INSERT INTO 表名 (字段名1, 字段名2, ...) VALUES (值1, 值2, ...);
|
||||
```
|
||||
|
||||
全部字段添加数据
|
||||
|
||||
```sql
|
||||
INSERT INTO 表名 VALUES (值1, 值2, ...);
|
||||
```
|
||||
|
||||
批量添加数据
|
||||
|
||||
```sql
|
||||
--指定字段
|
||||
INSERT INTO 表名 (字段名1, 字段名2, ...) VALUES (值1, 值2, ...), (值1, 值2, ...), (值1, 值2, ...);
|
||||
--全部字段
|
||||
INSERT INTO 表名 VALUES (值1, 值2, ...), (值1, 值2, ...), (值1, 值2, ...);
|
||||
```
|
||||
|
||||
修改数据
|
||||
|
||||
```sql
|
||||
UPDATE 表名 SET 字段名1 = 值1, 字段名2 = 值2, ... [ WHERE 条件 ];
|
||||
-- 例如
|
||||
UPDATE emp SET nickname = '乐心湖' WHERE id = 1;
|
||||
```
|
||||
|
||||
删除数据
|
||||
|
||||
```sql
|
||||
DELETE FROM 表名 [ WHERE 条件 ];
|
||||
```
|
||||
|
||||
## DQL
|
||||
|
||||
数据查询语言,用来查询数据库中表的记录
|
||||
|
||||
### 基础查询
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
字段列表
|
||||
FROM
|
||||
表名字段
|
||||
WHERE
|
||||
条件列表
|
||||
GROUP BY
|
||||
分组字段列表
|
||||
HAVING
|
||||
分组后的条件列表
|
||||
ORDER BY
|
||||
排序字段列表
|
||||
LIMIT
|
||||
分页参数
|
||||
```
|
||||
|
||||
查询所有字段
|
||||
|
||||
```sql
|
||||
SELECT * FROM 表名;
|
||||
-- 实际开发中尽量不要写 * 而是建议把每个字段都写出来
|
||||
SELECT id,name,age... FROM emp;
|
||||
```
|
||||
|
||||
查询结果字段带别名
|
||||
|
||||
```sql
|
||||
SELECT 字段1 [ AS 别名1 ], 字段2 [ AS 别名2 ], 字段3 [ AS 别名3 ], ... FROM 表名;
|
||||
SELECT 字段1 [ 别名1 ], 字段2 [ 别名2 ], 字段3 [ 别名3 ], ... FROM 表名;
|
||||
```
|
||||
|
||||
去除重复记录
|
||||
|
||||
```sql
|
||||
SELECT DISTINCT 字段列表 FROM 表名;
|
||||
```
|
||||
|
||||
### 条件查询
|
||||
|
||||
```sql
|
||||
SELECT 字段列表 FROM 表名 WHERE 条件列表;
|
||||
```
|
||||
|
||||
条件列表
|
||||
|
||||
| 比较运算符 | 功能 |
|
||||
| :-------------- | :------------------------------------------ |
|
||||
| > | 大于 |
|
||||
| >= | 大于等于 |
|
||||
| < | 小于 |
|
||||
| <= | 小于等于 |
|
||||
| = | 等于 |
|
||||
| `<>` 或 != | 不等于 |
|
||||
| BETWEEN … AND … | 在某个范围内(含最小、最大值) |
|
||||
| IN(…) | 在 in 之后的列表中的值,多选一,或的意思 |
|
||||
| LIKE 占位符 | 模糊匹配(_匹配单个字符,% 匹配任意个字符) |
|
||||
| IS NULL | 是 NULL |
|
||||
|
||||
| 逻辑运算符 | 功能 | | |
|
||||
| :--------- | :----------------------- | ---- | ---------------------------- |
|
||||
| AND 或 && | 并且(多个条件同时成立) | | |
|
||||
| OR 或 \ | \ | | 或者(多个条件任意一个成立) |
|
||||
| NOT 或 ! | 非,不是 | | |
|
||||
|
||||
```sql
|
||||
-- 年龄等于30
|
||||
select * from emp where age = 30;
|
||||
-- 没有身份证
|
||||
select * from emp where idcard is null or idcard = '';
|
||||
-- 有身份证
|
||||
select * from emp where idcard;
|
||||
select * from emp where idcard is not null;
|
||||
-- 不等于
|
||||
select * from emp where age != 30;
|
||||
-- 年龄在20到30之间
|
||||
select * from emp where age between 20 and 30;
|
||||
select * from emp where age >= 20 and age <= 30;
|
||||
-- 下面语句不报错,但查不到任何信息,因此一定要从先写小再写大
|
||||
select * from emp where age between 30 and 20;
|
||||
-- 性别为女且年龄小于30
|
||||
select * from emp where age < 30 and gender = '女';
|
||||
-- 年龄等于25或30或35
|
||||
select * from emp where age = 25 or age = 30 or age = 35;
|
||||
select * from emp where age in (25, 30, 35);
|
||||
-- 姓名为两个字
|
||||
select * from emp where name like '__';
|
||||
-- 姓名第一个字为钟
|
||||
select * from emp where name like '钟%';
|
||||
-- 姓名最后第一个字为雪
|
||||
select * from emp where name like '%雪';
|
||||
-- 身份证最后为X
|
||||
select * from emp where idcard like '%X';
|
||||
```
|
||||
|
||||
### 聚合查询
|
||||
|
||||
将一列数据作为一个整体,进行纵向计算。直接作用于字段。
|
||||
|
||||
```sql
|
||||
SELECT 聚合函数(字段列表) FROM 表名;
|
||||
```
|
||||
|
||||
常见聚合函数如下,注意:所有 null 值不参与聚合运算。
|
||||
|
||||
| 函数 | 功能 |
|
||||
| :---- | :------- |
|
||||
| count | 统计数量 |
|
||||
| max | 最大值 |
|
||||
| min | 最小值 |
|
||||
| avg | 平均值 |
|
||||
| sum | 求和 |
|
||||
|
||||
```sql
|
||||
-- 统计企业员工数量
|
||||
select count(id) from emp;
|
||||
-- 统计企业平均年龄
|
||||
select avg(age) from emp;
|
||||
-- 统计西安地区员工的年龄之和
|
||||
select sum(age) from emp where workaddr = '西安';
|
||||
```
|
||||
|
||||
### 分组查询
|
||||
|
||||
```sql
|
||||
SELECT 字段列表 FROM 表名 [ WHERE 条件 ] GROUP BY 分组字段名 [ HAVING 分组后的过滤条件 ];
|
||||
```
|
||||
|
||||
分组往往伴随着聚合
|
||||
|
||||
where 和 having 的区别:
|
||||
|
||||
- 执行时机不同:where 是分组之前进行过滤,不满足 where 条件不参与分组;having 是分组后对结果进行过滤。
|
||||
- 判断条件不同:where 不能对聚合函数进行判断,而 having 可以。
|
||||
|
||||
注意:
|
||||
|
||||
- 执行顺序:where > 聚合函数 > having
|
||||
- 分组之后,**查询的字段一般为聚合函数和分组字段**,查询其他字段无任何意义
|
||||
|
||||
```sql
|
||||
-- 根据性别分组,统计男性和女性数量(只显示分组数量,不显示哪个是男哪个是女)
|
||||
select count(*) from emp group by gender;
|
||||
-- 根据性别分组,统计男性和女性数量
|
||||
select gender, count(*) from emp group by gender;
|
||||
-- 根据性别分组,统计男性和女性的平均年龄
|
||||
select gender, avg(age) from emp group by gender;
|
||||
-- 年龄小于45,并根据工作地址分组,获取员工数量
|
||||
select workaddr, count(*) from emp where age < 45 group by workaddr;
|
||||
-- 年龄小于45,并根据工作地址分组,获取员工数量大于等于3的工作地址
|
||||
select workaddr, count(*) address_count from emp where age < 45 group by workaddr having address_count >= 3;
|
||||
```
|
||||
|
||||
### 排序查询
|
||||
|
||||
如果是多字段排序,当第一个字段值相同时,才会根据第二个字段进行排序
|
||||
|
||||
```sql
|
||||
SELECT 字段列表 FROM 表名 ORDER BY 字段1 排序方式1, 字段2 排序方式2;
|
||||
```
|
||||
|
||||
排序方式
|
||||
|
||||
- ASC:升序(默认)
|
||||
- DESC:降序
|
||||
|
||||
```sql
|
||||
-- 根据年龄升序排序
|
||||
select * from emp order by age ASC;
|
||||
select * from emp order by age;
|
||||
-- 根据年龄对公司的员工进行升序排序,年龄相同,再按照入职时间进行降序排序
|
||||
select * from emp order by age ASC, entrydate DESC;
|
||||
```
|
||||
|
||||
### 分页查询
|
||||
|
||||
```sql
|
||||
SELECT 字段列表 FROM 表名 LIMIT 起始索引, 查询记录数;
|
||||
```
|
||||
|
||||
- 起始索引从 0 开始,所以这里有个公式,起始索引 = (查询页码 - 1) * 每页显示记录数
|
||||
- **分页查询是数据库的方言,不同数据库有不同实现,MySQL 是 LIMIT**
|
||||
- 如果查询的是第一页数据,起始索引可以省略,直接简写 LIMIT 10
|
||||
|
||||
```sql
|
||||
-- 查询第一页数据,展示10条
|
||||
select * from emp limit 0, 10;
|
||||
-- 查询第二页,每页展示10条
|
||||
select * from emp limit 10, 10;
|
||||
```
|
||||
|
||||
### DQL 练习
|
||||
|
||||
DQL 编写顺序:
|
||||
|
||||
```sql
|
||||
SELECT -> FROM -> WHERE -> GROUP BY -> HAVING -> ORDER BY -> LIMIT
|
||||
```
|
||||
|
||||
DQL 执行顺序:
|
||||
|
||||
```sql
|
||||
FROM -> WHERE -> GROUP BY -> HAVING -> SELECT -> ORDER BY -> LIMIT
|
||||
```
|
||||
|
||||
![img](https://static.7wate.com/img/2022/05/11/e526304cef38f.png)
|
||||
|
||||
按照需求完成如下 DQL 语句编写
|
||||
|
||||
```sql
|
||||
-- 1.查询年龄为20,21,22,23岁的员工信息。
|
||||
select * from emp where age in(20,21,22,23);
|
||||
-- 2.查询性别为男,并且年龄在20-40岁(含)以内的姓名为三个字的员工。
|
||||
select * from emp where gender = '男' and age between 20 and 40 and name like '___';
|
||||
-- 3.统计员工表中,年龄小于60岁的,男性员工和女性员工的人数。
|
||||
select gender, count(*) from emp where age < 60 group by gender;
|
||||
-- 4.查询所有年龄小于等于35岁员工的姓名和年龄,并对查询结果按年龄升序排序,如果年龄相同按入职时间降序排序。
|
||||
select name, age from emp where age < 35 order by age ASC, entrydata DESC;
|
||||
-- 5、查询性别为男,且年龄在20-40岁(含)以内的前5个员工信息,对查询的结果按年龄升序排序,年龄相同按入职时间升序排序。
|
||||
select * from emp where (gender = '男') and (age >= 20 and age <=40) order by age ASC,entrydata DESC limit 5;
|
||||
```
|
||||
|
||||
## DCL
|
||||
|
||||
数据控制语言,用来创建数据库用户、控制数据库的访问权限
|
||||
|
||||
### 用户管理
|
||||
|
||||
查询用户
|
||||
|
||||
```sql
|
||||
use mysql;
|
||||
select * from user;
|
||||
```
|
||||
|
||||
创建用户
|
||||
|
||||
```sql
|
||||
CREATE USER '用户名'@'主机名' IDENTIFIED BY '密码';
|
||||
```
|
||||
|
||||
修改用户密码
|
||||
|
||||
```sql
|
||||
ALTER USER '用户名'@'主机名' IDENTIFIED WITH mysql_native_password BY '新密码';
|
||||
```
|
||||
|
||||
删除用
|
||||
|
||||
```sql
|
||||
DROP USER '用户名'@'主机名';
|
||||
```
|
||||
|
||||
例子
|
||||
|
||||
```sql
|
||||
-- 创建用户lxh,只能在当前主机localhost访问
|
||||
create user 'lxh'@'localhost' identified by '123456';
|
||||
-- 创建用户test,能在任意主机访问,使用 % 通配符号
|
||||
create user 'lxh'@'%' identified by '123456';
|
||||
create user 'lxh' identified by '123456';
|
||||
-- 修改密码
|
||||
alter user 'lxh'@'localhost' identified with mysql_native_password by '123';
|
||||
-- 删除用户
|
||||
drop user 'lxh'@'localhost';
|
||||
```
|
||||
|
||||
### 权限管理
|
||||
|
||||
常见的权限如下,更具体需要百度。
|
||||
|
||||
| 权限 | 说明 |
|
||||
| :------------------ | :--------------------- |
|
||||
| ALL, ALL PRIVILEGES | 所有权限 |
|
||||
| SELECT | 查询数据 |
|
||||
| INSERT | 插入数据 |
|
||||
| UPDATE | 修改数据 |
|
||||
| DELETE | 删除数据 |
|
||||
| ALTER | 修改表 |
|
||||
| DROP | 删除数据库 / 表 / 视图 |
|
||||
| CREATE | 创建数据库 / 表 |
|
||||
|
||||
```sql
|
||||
-- 查询权限:
|
||||
SHOW GRANTS FOR '用户名'@'主机名';
|
||||
|
||||
-- 授予权限:
|
||||
GRANT 权限列表 ON 数据库名.表名 TO '用户名'@'主机名';
|
||||
|
||||
-- 撤销权限:
|
||||
REVOKE 权限列表 ON 数据库名.表名 FROM '用户名'@'主机名';
|
||||
|
||||
-- 注意:
|
||||
-- 多个权限用逗号分隔
|
||||
-- 授权时,数据库名和表名可以用 * 进行通配,代表所有
|
||||
```
|
@ -1,127 +0,0 @@
|
||||
---
|
||||
title: 数据库事务
|
||||
description: 数据库事务
|
||||
keywords:
|
||||
- 数据库
|
||||
- 事务
|
||||
tags:
|
||||
- SQL
|
||||
sidebar_position: 6
|
||||
author: 7Wate
|
||||
date: 2022-08-31
|
||||
---
|
||||
事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求
|
||||
|
||||
**这些操作要么同时成功,要么同时失败。**
|
||||
|
||||
默认 MysQL 的事务是自动提交的,当执行一条 DML 语句,MySQL 会立即隐式地提交事务。
|
||||
|
||||
## 事务操作
|
||||
|
||||
事务演示如下,如果 2-3 步骤之间报错终端,那么张三就丢了 1000;而李四没收到 1000;
|
||||
|
||||
```sql
|
||||
-- 1. 查询张三账户余额
|
||||
select * from account where name = '张三';
|
||||
-- 2. 将张三账户余额 -1000
|
||||
update account set money = money - 1000 where name = '张三';
|
||||
-- 3. 将李四账户余额 +1000
|
||||
update account set money = money + 1000 where name = '李四';
|
||||
```
|
||||
|
||||
因此我们需要把上述代码整合成一个事务,完全执行后才提交。
|
||||
|
||||
```sql
|
||||
-- 查看事务提交方式
|
||||
SELECT @@AUTOCOMMIT;
|
||||
-- 设置事务提交方式,1 为自动提交,0 为手动提交,该设置只对当前会话有效
|
||||
SET @@AUTOCOMMIT = 0;
|
||||
-- 提交事务
|
||||
COMMIT;
|
||||
-- 回滚事务
|
||||
ROLLBACK;
|
||||
```
|
||||
|
||||
第二种方式
|
||||
|
||||
```sql
|
||||
-- 开启事务:
|
||||
START TRANSACTION 或 BEGIN TRANSACTION;
|
||||
-- 提交事务:
|
||||
COMMIT;
|
||||
-- 回滚事务:
|
||||
ROLLBACK;
|
||||
```
|
||||
|
||||
调整事务之后,重新运行 sql
|
||||
|
||||
```sql
|
||||
-- 1. 查询张三账户余额
|
||||
select * from account where name = '张三';
|
||||
-- 2. 将张三账户余额 -1000
|
||||
update account set money = money - 1000 where name = '张三';
|
||||
-- 3. 将李四账户余额 +1000
|
||||
update account set money = money + 1000 where name = '李四';
|
||||
commit;
|
||||
```
|
||||
|
||||
## 事务四大特性
|
||||
|
||||
四大特性 ACID:
|
||||
|
||||
- 原子性 (Atomicity):事务是不可分割的最小操作但愿,要么全部成功,要么全部失败。
|
||||
- 一致性 (Consistency):事务完成时,必须使所有数据都保持一致状态。
|
||||
- 隔离性 (Isolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行。
|
||||
- 持久性 (Durability):事务一旦提交或回滚,它对数据库中的数据的改变就是永久的。
|
||||
|
||||
## 并发事务问题
|
||||
|
||||
| 问题 | 描述 |
|
||||
| :--------- | :----------------------------------------------------------- |
|
||||
| 脏读 | 一个事务读到另一个事务还没提交的数据 |
|
||||
| 不可重复读 | 一个事务先后读取同一条记录,但两次读取的数据不同 |
|
||||
| 幻读 | 一个事务按照条件查询数据时,没有对应的数据行,但是再插入数据时,又发现这行数据已经存在 |
|
||||
|
||||
### 脏读
|
||||
|
||||
如下图,事务 A 更新 1,但还未提交,此时被事务 B 查去了,这就导致可能事务 A 最终决定不提交,但是事务 B 拿出来后当真了,所以这种现象叫做脏读。
|
||||
|
||||
![img](https://static.7wate.com/img/2022/05/11/85c311e17a51d.png)
|
||||
|
||||
### 不可重复读
|
||||
|
||||
如下图,事务 A 查询第一次后,事务 B 更新了这条数据,事务 A 查询第二次时发现跟第一次查询的结果不一样,这种现象叫做不可重复读。
|
||||
|
||||
![img](https://static.7wate.com/img/2022/05/11/0d298382a22d4.png)
|
||||
|
||||
### 幻读
|
||||
|
||||
幻读是在解决 [不可重复读] 的基础上产生的新问题,如下图,事务 A 读取 id 为 1 的数据为空,事务 B 插入 id 为 1 的数据,之后事务 A 想要插入这条数据发现插入不了(比如被主键约束了),然后事务 A 重新查询还是找不到 id 为 1 的数据(因为我们解决了[不可重复读],所以查询出来的结果跟第一次查是一致的)。
|
||||
|
||||
![img](https://static.7wate.com/img/2022/05/11/b5bdbc0d6b4e0.png)
|
||||
|
||||
## 事务隔离级别
|
||||
|
||||
MySQL 默认的事务隔离级别是 Repeatable Read
|
||||
|
||||
Oracle 默认的事务隔离级别是 Read committed
|
||||
|
||||
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|
||||
| :-------------------- | :--- | :--------- | :--- |
|
||||
| Read uncommitted | √ | √ | √ |
|
||||
| Read committed | × | √ | √ |
|
||||
| Repeatable Read(默认) | × | × | √ |
|
||||
| Serializable | × | × | × |
|
||||
|
||||
√ 表示在当前隔离级别下该问题会出现,
|
||||
|
||||
Serializable 性能最低;Read uncommitted 性能最高,数据安全性最差。
|
||||
|
||||
```sql
|
||||
-- 查看事务隔离级别:
|
||||
SELECT @@TRANSACTION_ISOLATION;
|
||||
-- 设置事务隔离级别:
|
||||
SET [ SESSION | GLOBAL ] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE };
|
||||
-- SESSION 是会话级别,表示只针对当前会话有效
|
||||
-- GLOBAL 表示对所有会话有效
|
||||
```
|
@ -1,350 +0,0 @@
|
||||
---
|
||||
title: 数据库查询
|
||||
description: 数据表数据查询
|
||||
keywords:
|
||||
- 数据库
|
||||
- 查询
|
||||
tags:
|
||||
- SQL
|
||||
sidebar_position: 5
|
||||
author: 7Wate
|
||||
date: 2022-08-31
|
||||
---
|
||||
## 多表关系
|
||||
|
||||
项目开发中,在进行数据库表结构设计时,会根据业务需求及业务模块之间的关系,分析并设计表结构,由于业务之间相互关联,所以各个表结构之间也存在着各种联系,基本上分为三种:一对一、一对多、多对多
|
||||
|
||||
### 一对一
|
||||
|
||||
案例:用户与用户详情
|
||||
|
||||
关系:一对一关系,多用于单表拆分,将一张表的基础字段放在一张表中,其他详情字段放在另一张表中,以提升操作效率
|
||||
|
||||
实现:在任意一方加入外键,关联另外一方的主键,并且设置外键为唯一的(UNIQUE)
|
||||
|
||||
![img](https://static.7wate.com/img/2022/05/11/ae2c3633fde99.png)
|
||||
|
||||
### 一对多
|
||||
|
||||
案例:部门与员工
|
||||
|
||||
关系:一个部门对应多个员工,一个员工对应一个部门
|
||||
|
||||
实现:在多的一方建立外键,指向一的一方的主键
|
||||
|
||||
![img](https://static.7wate.com/img/2022/05/11/0f32732cb6a00.png)
|
||||
|
||||
### 多对多
|
||||
|
||||
案例:学生与课程
|
||||
|
||||
关系:一个学生可以选多门课程,一门课程也可以供多个学生选修
|
||||
|
||||
实现:建立第三张中间表,中间表至少包含两个外键,分别关联两方主键
|
||||
|
||||
![img](https://static.7wate.com/img/2022/05/11/c029ca35af10d.png)
|
||||
|
||||
## 多表查询
|
||||
|
||||
指的是从多表中查询出想要的数据。
|
||||
|
||||
**笛卡尔积**:笛卡尔乘积是指在数学中,两个集合 A 集合和 B 集合的所有组合情况。(在多表查询时,需要消除无效的笛
|
||||
卡尔积)
|
||||
|
||||
例如:使用 `select * from employee, dept;` 查询出来的结果是两个表的乘积。
|
||||
|
||||
**消除笛卡尔积**:`select * from employee, dept where employee.dept = dept.id;`
|
||||
|
||||
![img](https://static.7wate.com/img/2022/05/11/eaa68daf10655.png)
|
||||
|
||||
在进行多表查询测试之前,我们先准备好数据表。
|
||||
|
||||
```sql
|
||||
-- 准备数据
|
||||
create table dept(
|
||||
id int auto_increment comment 'ID' primary key,
|
||||
name varchar(50) not null comment '部门名称'
|
||||
)comment '部门表';
|
||||
|
||||
create table emp(
|
||||
id int auto_increment comment 'ID' primary key,
|
||||
name varchar(50) not null comment '姓名',
|
||||
age int comment '年龄',
|
||||
job varchar(20) comment '职位',
|
||||
salary int comment '薪资',
|
||||
entrydate date comment '入职时间',
|
||||
managerid int comment '直属领导ID',
|
||||
dept_id int comment '部门ID'
|
||||
)comment '员工表';
|
||||
|
||||
-- 添加外键
|
||||
alter table emp add constraint fk_emp_dept_id foreign key (dept_id) references dept(id);
|
||||
|
||||
INSERT INTO dept (id, name) VALUES (1, '研发部'), (2, '市场部'),(3, '财务部'), (4, '销售部'), (5, '总经办'), (6, '人事部');
|
||||
INSERT INTO emp (id, name, age, job, salary, entrydate, managerid, dept_id) VALUES
|
||||
(1, '金庸', 66, '总裁',20000, '2000-01-01', null,5),
|
||||
|
||||
(2, '张无忌', 20, '项目经理',12500, '2005-12-05', 1,1),
|
||||
(3, '杨逍', 33, '开发', 8400,'2000-11-03', 2,1),
|
||||
(4, '韦一笑', 48, '开发',11000, '2002-02-05', 2,1),
|
||||
(5, '常遇春', 43, '开发',10500, '2004-09-07', 3,1),
|
||||
(6, '小昭', 19, '程序员鼓励师',6600, '2004-10-12', 2,1),
|
||||
|
||||
(7, '灭绝', 60, '财务总监',8500, '2002-09-12', 1,3),
|
||||
(8, '周芷若', 19, '会计',48000, '2006-06-02', 7,3),
|
||||
(9, '丁敏君', 23, '出纳',5250, '2009-05-13', 7,3),
|
||||
|
||||
(10, '赵敏', 20, '市场部总监',12500, '2004-10-12', 1,2),
|
||||
(11, '鹿杖客', 56, '职员',3750, '2006-10-03', 10,2),
|
||||
(12, '鹤笔翁', 19, '职员',3750, '2007-05-09', 10,2),
|
||||
(13, '方东白', 19, '职员',5500, '2009-02-12', 10,2),
|
||||
|
||||
(14, '张三丰', 88, '销售总监',14000, '2004-10-12', 1,4),
|
||||
(15, '俞莲舟', 38, '销售',4600, '2004-10-12', 14,4),
|
||||
(16, '宋远桥', 40, '销售',4600, '2004-10-12', 14,4),
|
||||
(17, '陈友谅', 42, null,2000, '2011-10-12', 1,null);
|
||||
```
|
||||
|
||||
### 内连接查询
|
||||
|
||||
内连接查询的是两张表交集的部分。
|
||||
|
||||
![img](https://static.7wate.com/img/2022/05/11/8d8fa0226c444.png)
|
||||
|
||||
隐式内连接:
|
||||
`SELECT 字段列表 FROM 表1, 表2 WHERE 条件 ...;`
|
||||
|
||||
显式内连接:
|
||||
`SELECT 字段列表 FROM 表1 [ INNER ] JOIN 表2 ON 连接条件 ...;`
|
||||
|
||||
SQL**显式性能比隐式高**
|
||||
|
||||
```sql
|
||||
-- 查询每一个员工的姓名,及关联的部门的名称(隐式内连接查询)
|
||||
select emp.name, dept.name from emp, dep where emp.dept_id = dept.id;
|
||||
select e.name, d.name from emp e, dep d where e.dept_id = d.id;
|
||||
-- 查询每一个员工的姓名,及关联的部门的名称(显式内连接)
|
||||
select emp.name, dept.name from emp inner join dept on emp.dept_id = dept.id;
|
||||
select e.name, d.name from emp e inner join dept d on e.dept_id = d.id;
|
||||
```
|
||||
|
||||
### 外连接查询
|
||||
|
||||
```sql
|
||||
-- 查询emp表中的所有数据和对应的部门信息(左外连接)
|
||||
select e.*, d.* from emp e left join dept d on e.dept_id = d.id;
|
||||
-- 查询dept表中的所有数据和对应的员工信息(右外连接)
|
||||
select d.name, e.* from emp e right join dept d on e.dept_id = d.id;
|
||||
-- 以上可以看到,左连接可以查询到没有dept的employee,右连接可以查询到没有employee的dept
|
||||
```
|
||||
|
||||
### 自连接查询
|
||||
|
||||
自连接查询指的是于自身的连接查询,把自身当作另一个表,所以要求**必须使用表别名**,**自连接查询可以是内连接查询或者外连接查询。**
|
||||
|
||||
```sql
|
||||
-- 查询员工及其领导
|
||||
select a.name '员工', b.name '领导' from emp a, emp b where a.managerid = b.id;
|
||||
-- 查询所有员工emp及其领导的名字,如果员工没有领导,也需要查询出来
|
||||
select a.*, b.name '领导' from emp a left join emp b on a.managerid = b.id;
|
||||
```
|
||||
|
||||
### 联合查询 union
|
||||
|
||||
联合查询就是把多次查询的结果合并,形成一个新的查询集。**联合查询比使用 or 效率高,不会使索引失效。**对于联合查询,多张表的列数必须保持一致,字段类型也必须保持一段。union all 会将全部的数据直接合并在一起,union 会对合并之后的数据去重。
|
||||
|
||||
```sql
|
||||
SELECT 字段列表 FROM 表A ...
|
||||
UNION [ALL]
|
||||
SELECT 字段列表 FROM 表B ...
|
||||
-- 将薪资低于5000的员工,和年龄大于50岁的员工全部查询出来.
|
||||
select * from emp where salary < 5000
|
||||
union
|
||||
select * from emp where age > 50
|
||||
-- 使用 UNION ALL 会有重复结果,UNION 则不会。
|
||||
```
|
||||
|
||||
## 子查询
|
||||
|
||||
SQL 语句中嵌套 SELECT 语句,称谓嵌套查询,又称子查询。
|
||||
|
||||
```sql
|
||||
SELECT * FROM t1 WHERE column1 = ( SELECT column1 FROM t2);
|
||||
```
|
||||
|
||||
子查询外部的语句可以是 **INSERT / UPDATE / DELETE / SELECT 的任何一个**
|
||||
|
||||
根据子查询结果可以分为:
|
||||
|
||||
- 标量子查询(子查询结果为单个值)
|
||||
- 列子查询(子查询结果为一列)
|
||||
- 行子查询(子查询结果为一行)
|
||||
- 表子查询(子查询结果为多行多列)
|
||||
|
||||
根据子查询位置可分为:
|
||||
|
||||
- WHERE 之后
|
||||
- FROM 之后
|
||||
- SELECT 之后
|
||||
|
||||
### 标量子查询
|
||||
|
||||
子查询返回的结果是单个值(数字、字符串、日期等)。
|
||||
|
||||
常用操作符:`- < > > >= < <=`
|
||||
|
||||
```sql
|
||||
-- 查询销售部所有员工
|
||||
select id from dept where name = '销售部';
|
||||
-- 根据销售部部门ID,查询员工信息
|
||||
select * from emp where dept = 4;
|
||||
-- 合并成标量子查询
|
||||
select * from emp where dept = (select id from dept where name = '销售部');
|
||||
-- 查询李白入职以后的新入职员工信息
|
||||
select * from emp where entrydate > (select entrydate from employee where name = '李白');
|
||||
```
|
||||
|
||||
### 列子查询
|
||||
|
||||
返回的结果是一列(可以是多行)。
|
||||
|
||||
常用操作符:
|
||||
|
||||
| 操作符 | 描述 |
|
||||
| :----- | :------------------------------------------ |
|
||||
| IN | 在指定的集合范围内,多选一 |
|
||||
| NOT IN | 不在指定的集合范围内 |
|
||||
| ANY | 子查询返回列表中,有任意一个满足即可 |
|
||||
| SOME | 与 ANY 等同,使用 SOME 的地方都可以使用 ANY |
|
||||
| ALL | 子查询返回列表的所有值都必须满足 |
|
||||
|
||||
```sql
|
||||
-- 查询销售部和市场部的所有员工信息
|
||||
select * from emp where dept_id in(select id from dept where name = '销售部' or name = '市场部');
|
||||
-- 查询比财务部所有人工资都高的员工信息
|
||||
select * from emp where salary > all(select salary from emp where dept_id = (select id from dept where name = '财务部'));
|
||||
-- 查询比研发部任意一人工资高的员工信息
|
||||
select * from emp where salary > any(select salary from emp where dept_id = (select id from dept where name = '研发部'));
|
||||
```
|
||||
|
||||
### 行子查询
|
||||
|
||||
返回的结果是一行(可以是多列)。
|
||||
|
||||
常用操作符:`=, <, >, IN, NOT IN`
|
||||
|
||||
```sql
|
||||
-- 查询与李白的薪资及直属领导相同的员工信息
|
||||
select * from emp where (salary, manager) = (12500, 1);
|
||||
select * from emp where (salary, manager) = (select salary, manager from emp where name = '李白');
|
||||
```
|
||||
|
||||
### 表子查询
|
||||
|
||||
返回的结果是多行多列
|
||||
常用操作符:IN
|
||||
|
||||
```sql
|
||||
-- 查询与xxx1,xxx2的职位和薪资相同的员工
|
||||
select * from emp where (job, salary) in (select job, salary from emp where name = 'xxx1' or name = 'xxx2');
|
||||
-- 查询入职日期是2022-01-01之后的员工,及其部门信息
|
||||
select e.*, d.* from (select * from emp where entrydate > '2022-01-01') as e left join dept as d on e.dept_id = d.id;
|
||||
```
|
||||
|
||||
## 多表练习
|
||||
|
||||
数据准备:
|
||||
|
||||
```sql
|
||||
-- 准备数据
|
||||
create table dept(
|
||||
id int auto_increment comment 'ID' primary key,
|
||||
name varchar(50) not null comment '部门名称'
|
||||
)comment '部门表';
|
||||
|
||||
create table emp(
|
||||
id int auto_increment comment 'ID' primary key,
|
||||
name varchar(50) not null comment '姓名',
|
||||
age int comment '年龄',
|
||||
job varchar(20) comment '职位',
|
||||
salary int comment '薪资',
|
||||
entrydate date comment '入职时间',
|
||||
managerid int comment '直属领导ID',
|
||||
dept_id int comment '部门ID'
|
||||
)comment '员工表';
|
||||
|
||||
-- 添加外键
|
||||
alter table emp add constraint fk_emp_dept_id foreign key (dept_id) references dept(id);
|
||||
|
||||
INSERT INTO dept (id, name) VALUES (1, '研发部'), (2, '市场部'),(3, '财务部'), (4, '销售部'), (5, '总经办'), (6, '人事部');
|
||||
INSERT INTO emp (id, name, age, job, salary, entrydate, managerid, dept_id) VALUES
|
||||
(1, '金庸', 66, '总裁',20000, '2000-01-01', null,5),
|
||||
|
||||
(2, '张无忌', 20, '项目经理',12500, '2005-12-05', 1,1),
|
||||
(3, '杨逍', 33, '开发', 8400,'2000-11-03', 2,1),
|
||||
(4, '韦一笑', 48, '开发',11000, '2002-02-05', 2,1),
|
||||
(5, '常遇春', 43, '开发',10500, '2004-09-07', 3,1),
|
||||
(6, '小昭', 19, '程序员鼓励师',6600, '2004-10-12', 2,1),
|
||||
|
||||
(7, '灭绝', 60, '财务总监',8500, '2002-09-12', 1,3),
|
||||
(8, '周芷若', 19, '会计',48000, '2006-06-02', 7,3),
|
||||
(9, '丁敏君', 23, '出纳',5250, '2009-05-13', 7,3),
|
||||
|
||||
(10, '赵敏', 20, '市场部总监',12500, '2004-10-12', 1,2),
|
||||
(11, '鹿杖客', 56, '职员',3750, '2006-10-03', 10,2),
|
||||
(12, '鹤笔翁', 19, '职员',3750, '2007-05-09', 10,2),
|
||||
(13, '方东白', 19, '职员',5500, '2009-02-12', 10,2),
|
||||
|
||||
(14, '张三丰', 88, '销售总监',14000, '2004-10-12', 1,4),
|
||||
(15, '俞莲舟', 38, '销售',4600, '2004-10-12', 14,4),
|
||||
(16, '宋远桥', 40, '销售',4600, '2004-10-12', 14,4),
|
||||
(17, '陈友谅', 42, null,2000, '2011-10-12', 1,null);
|
||||
|
||||
create table salgrade(
|
||||
grade int,
|
||||
losal int,
|
||||
hisal int
|
||||
) comment '薪资等级表';
|
||||
|
||||
insert into salgrade values (1,0,3000);
|
||||
insert into salgrade values (2,3001,5000);
|
||||
insert into salgrade values (3,5001,8000);
|
||||
insert into salgrade values (4,8001,10000);
|
||||
insert into salgrade values (5,10001,15000);
|
||||
insert into salgrade values (6,15001,20000);
|
||||
insert into salgrade values (7,20001,25000);
|
||||
insert into salgrade values (8,25001,30000);
|
||||
```
|
||||
|
||||
sql 练习
|
||||
|
||||
以显式内连接为主
|
||||
|
||||
```sql
|
||||
-- 1. 查询员工的姓名、年龄、职位、部门信息 (隐式内连接)
|
||||
select e.name, e.age, e.job, d.name from emp e, dept d where e.dept_id = d.id;
|
||||
-- 2. 查询年龄小于30岁的员工的姓名、年龄、职位、部门信息(显式内连接)
|
||||
select e.name, e.age, e.job, d.name from emp e inner join dept d on e.dept_id = d.id where e.age < 30;
|
||||
-- 3. 查询拥有员工的部门ID、部门名称
|
||||
-- 使用内连接可以让空的数据过滤
|
||||
-- 使用 distinct 可以过滤相同数据
|
||||
select distinct d.id, d.name from dept d inner join emp e on d.id = e.dept_id;
|
||||
-- 4. 查询所有年龄大于40岁的员工, 及其归属的部门名称; 如果员工没有分配部门, 也需要展示出来
|
||||
select e.*, d.name from emp e left join dept d on e.dept_id = d.id where e.age > 40;
|
||||
-- 5. 查询所有员工的工资等级
|
||||
select e.name, e.salary, s.grade from emp e inner join salgrade s on e.salary between s.losal and s.hisal;
|
||||
-- 6. 查询 "研发部" 所有员工的信息及工资等级
|
||||
select d.name '部门', e.*, s.grade from emp e inner join salgrade s on e.salary between s.losal and s.hisal inner join dept d on e.dept_id = d.id where d.name = '研发部';
|
||||
-- 7. 查询 "研发部" 员工的平均工资
|
||||
select d.name '部门', avg(e.salary) from emp e inner join dept d on e.dept_id = d.id where d.name = '研发部';
|
||||
-- 8. 查询工资比 "灭绝" 高的员工信息。
|
||||
select * from emp where salary > (select salary from emp where name = '灭绝');
|
||||
-- 9. 查询比平均薪资高的员工信息
|
||||
select * from emp where salary > (select avg(emp.salary) from emp);
|
||||
-- 10. 查询低于本部门平均工资的员工信息
|
||||
select * from emp e2 where e2.salary < (select avg(e1.salary) from emp e1 where e1.dept_id = e2.dept_id );
|
||||
-- 11. 查询所有的部门信息, 并统计部门的员工人数
|
||||
select d.*, (select count(*) from emp e where e.dept_id = d.id) '人数' from dept d;
|
||||
```
|
||||
|
||||
![img](https://static.7wate.com/img/2022/05/11/7ee27cf278dd9.png)
|
@ -1,38 +0,0 @@
|
||||
---
|
||||
title: 数据库概述
|
||||
description: 数据库概述
|
||||
keywords:
|
||||
- 数据库
|
||||
- 概述
|
||||
tags:
|
||||
- 数据库
|
||||
sidebar_position: 1
|
||||
author: 7Wate
|
||||
date: 2022-08-31
|
||||
---
|
||||
## 数据库
|
||||
|
||||
| 名称 | 全称 | 简称 |
|
||||
| :------------- | :----------------------------------------------------------- | :-------------------------------- |
|
||||
| 数据库 | 存储数据的仓库,数据是有组织的进行存储 | DataBase(DB) |
|
||||
| 数据库管理系统 | 操纵和管理数据库的大型软件 | DataBase Management System (DBMS) |
|
||||
| SQL | 操作关系型数据库的编程语言,定义了一套操作 关系型数据库统一标准 | Structured Query Language (SQL) |
|
||||
|
||||
## 数据模型
|
||||
|
||||
关系型数据库(RDBMS):建立在关系模型基础上,由多张相互连接的二维表组成的数据库。
|
||||
而所谓二维表,指的是由行和列组成的表,可以通过一列关联另外一个表格中的某一列数据,如下图。
|
||||
|
||||
特点:
|
||||
|
||||
1. 使用表存储数据,格式统一,便于维护。
|
||||
2. 使用 SQL 语言操作,标准统一,使用方便。
|
||||
|
||||
![img](https://static.7wate.com/img/2022/05/11/6bbfdea71bff2.png)
|
||||
|
||||
常见的 MySQL、Oracle、DB2、SQLServer 这些都是属于关系型数据库,里面都是基于二维表存储数据的。
|
||||
|
||||
无论我们使用哪一个关系型数据库,最终在操作时都是使用 SQL 语言来进行统一操作,
|
||||
因为我们接下来学习的 SQL 语言,是操作关系型数据库的统一标准。
|
||||
|
||||
![服务器](https://static.7wate.com/img/2022/05/11/b7d5d1651689a.png)
|
@ -1,256 +0,0 @@
|
||||
---
|
||||
title: 数据库规范
|
||||
description: 数据库规范
|
||||
keywords:
|
||||
- 数据库
|
||||
- 规范
|
||||
tags:
|
||||
- SQL
|
||||
sidebar_position: 7
|
||||
author: 7Wate
|
||||
date: 2022-08-31
|
||||
---
|
||||
|
||||
## 命名
|
||||
|
||||
建表时统一规范**表**、**字段**和**索引**的命名,能够提高沟通效率和降低维护成本。
|
||||
|
||||
### 见名知意
|
||||
|
||||
#### 反例
|
||||
|
||||
```sql
|
||||
-- 用户名字段
|
||||
yong_hu_ming
|
||||
用户_name
|
||||
name
|
||||
user_name_123456789
|
||||
```
|
||||
|
||||
#### 正例
|
||||
|
||||
```sql
|
||||
-- 用户名字段
|
||||
user_name
|
||||
```
|
||||
|
||||
***字段不宜过长,尽量控制在 30 个字符以内。***
|
||||
|
||||
### 大小写
|
||||
|
||||
**统一采用小写字母;**因为从视觉上小写字母更容易让人读懂,所以采用小写字母。
|
||||
|
||||
**反例:**
|
||||
|
||||
```sql
|
||||
-- 字段名
|
||||
PRODUCT_NAME
|
||||
PRODUCT_name
|
||||
```
|
||||
|
||||
**正例:**
|
||||
|
||||
```sql
|
||||
-- 字段名
|
||||
product_name
|
||||
```
|
||||
|
||||
### 分隔符
|
||||
|
||||
多个单词之间使用**半角下划线作为分隔符**。
|
||||
|
||||
#### 反例
|
||||
|
||||
```sql
|
||||
-- 字段名
|
||||
productname
|
||||
productName
|
||||
product name
|
||||
product@name
|
||||
```
|
||||
|
||||
**正例:**
|
||||
|
||||
```sql
|
||||
-- 字段名
|
||||
product_name
|
||||
```
|
||||
|
||||
### 表名
|
||||
|
||||
对于表名,在见名知意的基础之上带上**前缀**;以便于归类区分,同时避免出现同名表。
|
||||
|
||||
```sql
|
||||
-- 业务表
|
||||
order_pay
|
||||
order_pay_detail
|
||||
-- 商品表
|
||||
product_spu
|
||||
product_sku
|
||||
```
|
||||
|
||||
### 字段名称
|
||||
|
||||
数据表内常用**公共字段采用统一命名**。
|
||||
|
||||
```sql
|
||||
-- 状态字段
|
||||
status
|
||||
-- 创建时间、修改时间
|
||||
create_time
|
||||
update_time
|
||||
-- 外键
|
||||
表名_字段
|
||||
```
|
||||
|
||||
### 索引名
|
||||
|
||||
在数据库中,索引有主键、普通索引、唯一索引、联合索引等。
|
||||
|
||||
- 表主键:一般使用 **id** 命名。
|
||||
|
||||
- 普通索引和联合索引: **ix_** 前缀。
|
||||
|
||||
- 唯一索引: **ux_** 前缀。
|
||||
|
||||
## 字段类型
|
||||
|
||||
- 尽可能选择占用存储空间小的字段类型,在满足正常业务需求的情况下,从小到大,往上选。
|
||||
- 如果字符串长度固定,或者差别不大,可以选择 char 类型;如果字符串长度差别较大,可以选择varchar类型。
|
||||
- 是否字段:建议选择 bit 类型。
|
||||
- 枚举字段:建议选择 tinyint 类型。
|
||||
- 主键字段:建议选择 bigint 类型。
|
||||
- 金额字段:建议选择 decimal 类型。
|
||||
- 时间字段:建议选择 timestamp 或 datetime 类型。
|
||||
|
||||
## 字段长度
|
||||
|
||||
在 MySQL 中除了 **varchar 和 char 是字符长度**之外,**其余的类型都是字节长度**。
|
||||
|
||||
例如 bigint(4)实际长度是 8 个字节。
|
||||
|
||||
## 字段个数
|
||||
|
||||
数据表的字段**尽量不要超过 20 个。**
|
||||
|
||||
特殊情况可以将**一张表拆成多张表**形成组合表,其主键相同。
|
||||
|
||||
## 主键
|
||||
|
||||
**数据表必须拥有主键**,因为主键拥有自带索引并且不需要回表,所以相比于其他索引查询效率最高。
|
||||
|
||||
数据表**主键建议保存跟业务无关的值**,减少业务耦合性方便扩展。
|
||||
|
||||
单体数据库中,主键可以设置 **AUTO_INCREMENT(自动增长)**。
|
||||
|
||||
分布式数据库中,尤其是分库分表的业务库中,主键最好由外部算法(比如:雪花算法)生成,并且保证生成的 id 是全局唯一的。
|
||||
|
||||
## 存储引擎
|
||||
|
||||
建议在使用 MySQL8 以后的版本时,使用默认的 innodb 存储引擎,无需修改存储引擎。
|
||||
|
||||
## NOT NULL
|
||||
|
||||
在定义字段时,应该尽可能明确该字段 NOT NULL。
|
||||
|
||||
主要有以下原因:
|
||||
|
||||
1. 在 innodb 中,需要额外的空间存储 null 值,需要占用更多的空间。
|
||||
2. null 值可能会导致索引失效。
|
||||
3. null 值只能用 is null 或者 is not null 判断,用等于号判断永远返回 False。
|
||||
|
||||
## 外键
|
||||
|
||||
外键主要作用是保证数据的**一致性**和**完整性**。
|
||||
|
||||
```sql
|
||||
create table class (
|
||||
id int(10) primary key auto_increment,
|
||||
cname varchar(15)
|
||||
);
|
||||
```
|
||||
|
||||
```sql
|
||||
create table student(
|
||||
id int(10) primary key auto_increment,
|
||||
name varchar(15) not null,
|
||||
gender varchar(10) not null,
|
||||
cid int,
|
||||
foreign key(cid) references class(id)
|
||||
);
|
||||
```
|
||||
|
||||
上述两个数据表 class 和 student 中,如果直接删除 student 表的 行数据,因为存在外键关联,所以就会报异常。必须要先删除 class 表对应的 student 表中 cid 那行数据,再删除 student 表的数据才能够**保证数据的一致性和完整性。**
|
||||
|
||||
只有存储引擎是innodb时,才能使用外键。如果**注重性能的场景下,建议不使用外键、存储过程和触发键**。
|
||||
|
||||
## 索引
|
||||
|
||||
创建数据表时,不仅可以指定主键索引,还**可以创建普通索引**;建议单个数据表的普通索引不要超过 5 个。
|
||||
|
||||
```sql
|
||||
create table product_sku (
|
||||
id int(10) primary key auto_increment,
|
||||
spu_id int(10) not null,
|
||||
brand_id int(10) not null,
|
||||
name varchar(15) not null,
|
||||
KEY `ix_spu_id` (`spu_id`) USING BTREE,
|
||||
KEY `ix_brand_id` (`brand_id`) USING BTREE
|
||||
);
|
||||
```
|
||||
|
||||
数据表创建时,添加 ix_spu_id、ix_brand_id 普通索引,可以提高检索数据效率。
|
||||
|
||||
## 时间字段
|
||||
|
||||
目前 MySQL 支持 date、datetime、timestamp、varchar 等时间类型,**建议使用 datetime**。
|
||||
|
||||
- varchar:存储时间的类型是 String,无法创建索引。
|
||||
|
||||
- date:适用于保存日期,比如:2020-08-20 12:12:20。
|
||||
|
||||
- timestamp:4 个字节,取值范围为 1970-01-01 00:00:01 UTC ~ 2038-01-19 03:14:07,受时区影响。
|
||||
- datetime:8个字节,取值范围为 1000-01-01 00:00:00 ~ 9999-12-31 23:59:59,不受时区影响。
|
||||
|
||||
## 金额字段
|
||||
|
||||
MySQL 中 float、double、decimal 等支持浮点数。
|
||||
|
||||
因为 float 和 double 可能会丢失精度,所以建议使用 decimal 类型保存金额数值。
|
||||
|
||||
定义浮点数 decimal(m,n),n 是小数的长度,m 是整数和小数的总长度。
|
||||
|
||||
## 唯一索引
|
||||
|
||||
唯一索引支持单个字段、多个字段联合。
|
||||
|
||||
如果多个字段联合的唯一索引字段值出现 null,那么唯一性约束可能会失效。
|
||||
|
||||
## 字符集
|
||||
|
||||
MySQL 中常用字符集的有 latin1、gbk 、utf-8、utf8mb4 等,**建议使用 utf-8 字符集**。
|
||||
|
||||
| 字符集 | 长度 | 功能 | 备注 |
|
||||
| ------- | ---- | ------------------------------ | --------------------------------------- |
|
||||
| latin1 | 1 | 默认字符集 | 容易出现乱码,使用较少 |
|
||||
| gbk | 2 | 支持中文,非国际通用字符 | 不支持国际通用字符,使用不多 |
|
||||
| utf-8 | 3 | 支持中英文混合,国际通用字符集 | 相对 utf8mb4 占用存储更少,不支持 emoji |
|
||||
| utf8mb4 | 4 | 兼容 UTF-8 | 4 个字节支持存储更多字符,支持 emoji |
|
||||
|
||||
## 排序规则
|
||||
|
||||
数据表创建时 COLLATE 参数可以设置排序规则,排序规则与字符集相关。
|
||||
|
||||
数据表使用 utf8mb4 时字符排序规则以 utf8mb4_ 开头的,常用排序规则 utf8mb4_general_ci 、utf8mb4_bin 等。
|
||||
|
||||
- utf8mb4_general_ci:**不区分字母大小**。
|
||||
|
||||
- utf8mb4_bin:**区分字母大小**。
|
||||
|
||||
建议字符排序规则务必根据实际的业务场景选择,否则容易出现不可预测的问题。
|
||||
|
||||
## 大字段
|
||||
|
||||
大字段即占用较多存储空间的字段,**建议大字段定义成 varchar 类型**。
|
||||
|
||||
例如用户的评论就属于大字段,但是字段长度不唯一,所以一般会限制总长度。
|
@ -1,87 +0,0 @@
|
||||
---
|
||||
title: 数据表约束
|
||||
description: 数据表行数据约束
|
||||
keywords:
|
||||
- 数据库
|
||||
- 约束
|
||||
tags:
|
||||
- SQL
|
||||
sidebar_position: 4
|
||||
author: 7Wate
|
||||
date: 2022-08-31
|
||||
---
|
||||
|
||||
## 数据约束
|
||||
|
||||
约束是作用于表中字段上的,用于限制存储在表中的数据。目的是保证数据中数据的正确、有效性和完整性。
|
||||
|
||||
可以在创建表 / 修改表的时候添加约束。
|
||||
|
||||
常见约束:
|
||||
|
||||
| 约束 | 描述 | 关键字 |
|
||||
| :----------------------- | :------------------------------------------------------- | :---------------------------------- |
|
||||
| 非空约束 | 限制该字段的数据不能为 null | NOT NULL |
|
||||
| 唯一约束 | 保证该字段的所有数据都是唯一、不重复的 | UNIQUE |
|
||||
| 主键约束 | 主键是一行数据的唯一标识,要求非空且唯一 | PRIMARY KEY(自增:AUTO_INCREMENT) |
|
||||
| 默认约束 | 保存数据时,如果未指定该字段的值,则采用默认值 | DEFAULT |
|
||||
| 检查约束(8.0.1 版本后) | 保证字段值满足某一个条件 | CHECK |
|
||||
| 外键约束 | 用来让两张图的数据之间建立连接,保证数据的一致性和完整性 | FOREIGN KEY |
|
||||
|
||||
### 常用约束
|
||||
|
||||
![img](https://static.7wate.com/img/2022/05/11/58a6b68ad6471.png)
|
||||
|
||||
```sql
|
||||
create table user(
|
||||
id int primary key auto_increment comment '主键id',
|
||||
name varchar(10) not null unique comment '姓名',
|
||||
age int check(age between 0 and 120) comment '年龄',
|
||||
status char(1) default '1' comment '状态',
|
||||
gender char(1) comment '性别'
|
||||
) comment '用户表';
|
||||
```
|
||||
|
||||
### 外键约束
|
||||
|
||||
外键约束是用来让两个表的数据之间建立连接,从而保证数据的一致性和完整性。
|
||||
|
||||
![img](https://static.7wate.com/img/2022/05/11/110dd22cdc2cf.png)
|
||||
|
||||
```sql
|
||||
-- 在创建表时添加外键
|
||||
CREATE TABLE 表名(
|
||||
字段名 字段类型,
|
||||
...
|
||||
[CONSTRAINT] [外键名称] FOREIGN KEY(外键字段名) REFERENCES 主表(主表列名)
|
||||
);
|
||||
-- 为表补上外键
|
||||
ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段名) REFERENCES 主表(主表列名);
|
||||
-- 删除外键
|
||||
ALTER TABLE 表名 DROP FOREIGN KEY 外键名;
|
||||
```
|
||||
|
||||
案例:为 emp 表补上外键,dept_id 关联 dep 中的 id
|
||||
|
||||
```sql
|
||||
alter table emp add constraint fk_emp_dept_id foreign key dept_id references dep(id);
|
||||
```
|
||||
|
||||
### 删除 / 更新行为
|
||||
|
||||
表添加了外键之后,再删除父表数据时产生的约束行为,我们就称为删除 / 更新行为。有以下常见的几种:
|
||||
|
||||
| 行为 | 说明 |
|
||||
| :---------- | :----------------------------------------------------------- |
|
||||
| NO ACTION | 当在父表中删除 / 更新对应记录时,首先检查该记录是否有对应外键,如果有则不允许删除 / 更新(与 RESTRICT 一致) |
|
||||
| RESTRICT | 当在父表中删除 / 更新对应记录时,首先检查该记录是否有对应外键,如果有则不允许删除 / 更新(与 NO ACTION 一致) |
|
||||
| CASCADE | 当在父表中删除 / 更新对应记录时,首先检查该记录是否有对应外键,如果有则也删除 / 更新外键在子表中的记录 |
|
||||
| SET NULL | 当在父表中删除 / 更新对应记录时,首先检查该记录是否有对应外键,如果有则设置子表中该外键值为 null(要求该外键允许为 null) |
|
||||
| SET DEFAULT | 父表有变更时,子表将外键设为一个默认值(Innodb 不支持) |
|
||||
|
||||
```sql
|
||||
ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段) REFERENCES 主表名(主表字段名) ON UPDATE 行为 ON DELETE 行为;
|
||||
-- 其实就是在添加外键时的多一个定义
|
||||
-- 例如:在父表中删除/更新对应记录时,首先检查该记录是否有对应外键,如果有则也删除/更新外键在子表中的记录
|
||||
alter table emp add constraint fk_emp_dept_id foreign key dept_id references dep(id) on update cascade on delete cascade;
|
||||
```
|
@ -8,20 +8,20 @@ sidebar_position: 1
|
||||
|
||||
**职责与工作内容:**
|
||||
|
||||
1. **数据处理与存储**:设计和实施数据存储解决方案,如关系型数据库、Hadoop、HBase 和 Cassandra。
|
||||
2. **数据流处理与 ETL**:使用工具如 Apache Kafka、Apache Flink 进行实时数据流处理,同时设计和实现 ETL 流程。
|
||||
3. **数据集成**:整合来自不同来源的数据,确保数据的一致性和完整性。
|
||||
4. **性能优化**:优化数据查询和处理的性能,确保系统的高效运行。
|
||||
5. **系统维护与监控**:监控数据系统的健康状况,进行故障排除和性能调优。
|
||||
6. **跨团队协作**:与数据科学家、业务分析师、BI 专家和其他团队合作,确保他们可以轻松访问和使用数据。
|
||||
1. **数据处理与存储**:设计和实施数据存储解决方案,如关系型数据库、Hadoop、HBase 和 Cassandra。
|
||||
2. **数据流处理与 ETL**:使用工具如 Apache Kafka、Apache Flink 进行实时数据流处理,同时设计和实现 ETL 流程。
|
||||
3. **数据集成**:整合来自不同来源的数据,确保数据的一致性和完整性。
|
||||
4. **性能优化**:优化数据查询和处理的性能,确保系统的高效运行。
|
||||
5. **系统维护与监控**:监控数据系统的健康状况,进行故障排除和性能调优。
|
||||
6. **跨团队协作**:与数据科学家、业务分析师、BI 专家和其他团队合作,确保他们可以轻松访问和使用数据。
|
||||
|
||||
**必备技能与知识:**
|
||||
|
||||
1. **数据技术栈**:熟悉数据库技术(如MySQL、PostgreSQL)、Hadoop 生态系统(如HDFS、MapReduce、Hive)和 Spark。
|
||||
2. **分布式系统**:理解分布式系统的原理和挑战,如数据一致性、分区容错等。
|
||||
3. **编程与脚本**:如 Java、Scala、Python 等,用于数据处理和自动化任务。
|
||||
4. **数据建模与设计**:能够设计高效、可扩展的数据模型和架构。
|
||||
5. **系统监控与维护**:使用工具如 Grafana、Prometheus 进行系统监控和告警。
|
||||
6. **沟通与团队合作**:与各团队沟通协作,理解业务需求,为其提供所需的数据支持。
|
||||
1. **数据技术栈**:熟悉数据库技术(如MySQL、PostgreSQL)、Hadoop 生态系统(如HDFS、MapReduce、Hive)和 Spark。
|
||||
2. **分布式系统**:理解分布式系统的原理和挑战,如数据一致性、分区容错等。
|
||||
3. **编程与脚本**:如 Java、Scala、Python 等,用于数据处理和自动化任务。
|
||||
4. **数据建模与设计**:能够设计高效、可扩展的数据模型和架构。
|
||||
5. **系统监控与维护**:使用工具如 Grafana、Prometheus 进行系统监控和告警。
|
||||
6. **沟通与团队合作**:与各团队沟通协作,理解业务需求,为其提供所需的数据支持。
|
||||
|
||||
总的来说,数据工程师确保数据系统的稳定、高效和可扩展,从而支持企业做出数据驱动的决策。他们的专业知识和技能,使得企业能够充分利用其数据资产,为客户和业务提供价值。
|
@ -9,20 +9,20 @@ sidebar_position: 1
|
||||
|
||||
**职责与工作内容:**
|
||||
|
||||
1. **监控与故障排除**:SRE 工程师持续对系统进行实时监控,确保其性能和可用性。一旦出现故障,他们会迅速定位并解决问题。
|
||||
2. **自动化与工具开发**:通过编程和自动化技术,SRE 工程师简化和优化系统管理,提高效率并增强系统的可靠性。
|
||||
3. **容量规划**:他们评估系统资源的需求,确保系统能够应对预期的负载和流量。
|
||||
4. **故障分析与预防**:对系统故障进行深入分析,识别并解决潜在问题,从而提高系统的稳定性。
|
||||
5. **紧急响应**:在系统出现紧急故障时,SRE 工程师会迅速采取行动,最大程度地减少停机时间和业务影响。
|
||||
6. **跨团队协作**:与软件开发、产品团队等进行合作,共同解决系统和运维问题。
|
||||
1. **监控与故障排除**:SRE 工程师持续对系统进行实时监控,确保其性能和可用性。一旦出现故障,他们会迅速定位并解决问题。
|
||||
2. **自动化与工具开发**:通过编程和自动化技术,SRE 工程师简化和优化系统管理,提高效率并增强系统的可靠性。
|
||||
3. **容量规划**:他们评估系统资源的需求,确保系统能够应对预期的负载和流量。
|
||||
4. **故障分析与预防**:对系统故障进行深入分析,识别并解决潜在问题,从而提高系统的稳定性。
|
||||
5. **紧急响应**:在系统出现紧急故障时,SRE 工程师会迅速采取行动,最大程度地减少停机时间和业务影响。
|
||||
6. **跨团队协作**:与软件开发、产品团队等进行合作,共同解决系统和运维问题。
|
||||
|
||||
**必备技能与知识:**
|
||||
|
||||
1. **系统与网络基础**:这是 SRE 的基础,包括算法、数据结构、网络编程等。
|
||||
2. **编程与脚本**:如 Python、Go 等,用于自动化和工具开发。
|
||||
3. **容器化与云计算**:如 Docker、Kubernetes,这些技术在现代互联网企业中被广泛采用。
|
||||
4. **运维工具与技术**:如监控系统、配置管理和自动化部署,帮助 SRE 更高效地管理系统。
|
||||
5. **故障排除与分析**:能够迅速定位问题并找出解决方案。
|
||||
6. **沟通与团队合作**:与各团队沟通协作,共同解决问题。
|
||||
1. **系统与网络基础**:这是 SRE 的基础,包括算法、数据结构、网络编程等。
|
||||
2. **编程与脚本**:如 Python、Go 等,用于自动化和工具开发。
|
||||
3. **容器化与云计算**:如 Docker、Kubernetes,这些技术在现代互联网企业中被广泛采用。
|
||||
4. **运维工具与技术**:如监控系统、配置管理和自动化部署,帮助 SRE 更高效地管理系统。
|
||||
5. **故障排除与分析**:能够迅速定位问题并找出解决方案。
|
||||
6. **沟通与团队合作**:与各团队沟通协作,共同解决问题。
|
||||
|
||||
综上所述,SRE 工程师是一个综合性极强的角色,他们既需要软件工程技能,又要具备系统运维知识。他们的存在,确保了在线服务的高可靠性,为用户提供了卓越的服务体验。
|
||||
|
Loading…
Reference in New Issue
Block a user