Compare commits

...

2 Commits

Author SHA1 Message Date
cb02a70fcf
Merge branch 'main' of git.dr1997.com:blog/article 2023-06-04 15:59:56 +08:00
98b3779e63
fix 2023-06-04 15:59:11 +08:00
7 changed files with 1282 additions and 16 deletions

458
SQL-窗口函数.md Normal file
View File

@ -0,0 +1,458 @@
# SQL窗口函数
### 一. 什么是窗口函数
#### 基本含义
窗口限定一个范围,它可以理解为满足某些条件的记录集合,窗口函数也就是在窗口范围内执行的函数。
#### 基本语法
窗口函数有over关键字指定函数执行的范围可分为三部分分组子句(partition by),排序子句(order by),窗口子句(rows)
```sql
<函数名> over (partition by <分组的列> order by <排序的列> rows between <起始行> and <终止行>)
```
**注意Mysql8才支持窗口函数**
#### 演示表格
| cid班级id) | sname学生姓名 | score分数 |
| ------------ | ----------------- | ------------- |
| 001 | 张三 | 78 |
| 001 | 李四 | 82 |
| 002 | 小明 | 90 |
| 001 | 王五 | 67 |
| 002 | 小红 | 85 |
| 002 | 小刚 | 62 |
#### 演示脚本
```sql
CREATE TABLE SQL_5 (
cid varchar(4),
sname varchar(4),
score int
);
insert into SQL_5 (cid, sname, score) values ('001', '张三', 78);
insert into SQL_5 (cid, sname, score) values ('001', '李四', 82);
insert into SQL_5 (cid, sname, score) values ('002', '小明', 90);
insert into SQL_5 (cid, sname, score) values ('001', '王五', 67);
insert into SQL_5 (cid, sname, score) values ('002', '小红', 85);
insert into SQL_5 (cid, sname, score) values ('002', '小刚', 62);
```
### 二. 窗口的确定
例子:
```sql
select *, sum(score) over (partition by cid order by score rows between unbounded preceding and unbounded following) as '班级总分' from SQL_5;
```
#### 分组子句(partition by)
不分组可以写成partition by null或者直接不写
后面可以跟多个列, 如 partition by cid, sname
**注意 partition by与group by的区别**
1前者不会压缩行数但是后者会
2后者只能选取分组的列和聚合的列
也就是说group by 后生成的结果集与原表的行数和列数都不同
#### 排序子句(order by)
不排序可以写成order by null 或者直接不写
asc或不写表示升序desc表示降序
后面可以跟多个列, 如 order by cid, sname
#### 窗口子句(rows)
窗口子句的描述
1) 起始行: N preceding/unbounded preceding
2) 当前行: current row
3) 终止行: N following/unbounded following
举例:
rows between unbounded preceding and current row 从之前所有的行到当前行
rows between 2 preceding and current row 从前面两行到当前行
rows between current row and unbounded following 从当前行到之后所有的行
rows between current row and 1following 从当前行到后面一行
**注意:**
**排序子句后面缺少窗口子句,窗口规范默认是 rows between unbounded preceding and current row**
**排序子句和窗口子句都缺失,窗口规范默认是 rows between unbounded preceding and unbounded following**
#### 总体流程
1 通过partition by 和 order by 子句确定大窗口( 定义出上界unbounded preceding和下界unbounded following
2 通过row 子句针对每一行数据确定小窗口(滑动窗口)
3 对每行的小窗口内的数据执行函数并生成新的列
### 三. 函数分类
#### 排序类
rank, dense_rank, row_number
```sql
-- 【排序类】
-- 按班级分组后打上序号 不考虑并列
select *, row_number() over (partition by cid order by score desc) as '不可并列排名' from SQL_5;
-- 按班级分组后作跳跃排名 考虑并列
select *, rank() over (partition by cid order by score desc) as '跳跃可并列排名' from SQL_5;
-- 按班级分组后作连续排名 考虑并列
select *, dense_rank() over (partition by cid order by score desc) as '连续可并列排名' from SQL_5;
-- 合并起来对比
select *, row_number() over (partition by cid order by score desc) as '不可并列排名' ,
rank() over (partition by cid order by score desc) as '跳跃可并列排名',
dense_rank() over (partition by cid order by score desc) as '连续可并列排名'
from SQL_5;
```
#### 聚合类
sum. avg, count, max, min
```sql
-- 【聚合类】
-- 让同一班级每个学生都知道班级总分是多少
select *, sum(score) over (partition by cid) as '班级总分' from SQL_5;
-- 或者可以写成
select *, sum(score) over (partition by cid rows between unbounded preceding and unbounded following) as '班级总分' from SQL_5;
-- 计算同一班级,每个同学和比他分数低的同学的累计总分是多少
select *, sum(score) over (partition by cid order by score) '累加分数' from SQL_5;
-- 或者可以写成 其中rows between ... and 是规定窗口大小
select *, sum(score) over (partition by cid order by score rows between unbounded preceding and current row) as '累加分数' from SQL_5;
```
#### 跨行类
lag, lead
```sql
-- 【跨行类】
-- lag/lead 函数 参数1:比较的列 参数2: 偏移量 参数3:找不到的默认值
-- 同一班级内,成绩比自己低一名的分数是多少
select *, lag(score, 1) over (partition by cid order by score) as '低一名的分数' from SQL_5;
-- 或者写成
select *, lag(score, 1, 0) over (partition by cid order by score) as '低一名的分数' from SQL_5;
-- 同一班级内成绩比自己高2名的分数是多少
select *, lead(score, 2) over (partition by cid order by score) as '高两名的分数' from SQL_5;
```
### 四. 相关题目
#### 表格
| cid | sname | course | score |
| ---- | ----- | ------ | ----- |
| 001 | 张三 | 语文 | 78 |
| 002 | 小刚 | 语文 | 71 |
| 001 | 李四 | 数学 | 56 |
| 002 | 小明 | 数学 | 54 |
| ... | ... | ... | ... |
#### 脚本
```sql
CREATE TABLE SQL_6 (
cid varchar(4),
sname varchar(4),
course varchar(10),
score int
);
insert into SQL_6 (cid, sname, course, score) values ('001', '张三', '语文', 78);
insert into SQL_6 (cid, sname, course, score) values ('002', '小刚', '语文', 71);
insert into SQL_6 (cid, sname, course, score) values ('001', '李四', '数学', 56);
insert into SQL_6 (cid, sname, course, score) values ('001', '王五', '数学', 97);
insert into SQL_6 (cid, sname, course, score) values ('002', '小明', '数学', 54);
insert into SQL_6 (cid, sname, course, score) values ('002', '小刚', '数学', 67);
insert into SQL_6 (cid, sname, course, score) values ('002', '小红', '数学', 82);
insert into SQL_6 (cid, sname, course, score) values ('001', '王五', '语文', 80);
insert into SQL_6 (cid, sname, course, score) values ('001', '张三', '数学', 77);
insert into SQL_6 (cid, sname, course, score) values ('002', '小明', '语文', 58);
insert into SQL_6 (cid, sname, course, score) values ('002', '小红', '语文', 87);
insert into SQL_6 (cid, sname, course, score) values ('001', '李四', '语文', 60);
insert into SQL_6 (cid, sname, course, score) values ('001', '张三', '英语', 66);
insert into SQL_6 (cid, sname, course, score) values ('002', '小刚', '英语', 50);
insert into SQL_6 (cid, sname, course, score) values ('001', '李四', '地理', 59);
insert into SQL_6 (cid, sname, course, score) values ('001', '王五', '地理', 88);
insert into SQL_6 (cid, sname, course, score) values ('002', '小明', '地理', 45);
insert into SQL_6 (cid, sname, course, score) values ('002', '小刚', '地理', 66);
insert into SQL_6 (cid, sname, course, score) values ('002', '小红', '地理', 82);
insert into SQL_6 (cid, sname, course, score) values ('001', '王五', '英语', 81);
insert into SQL_6 (cid, sname, course, score) values ('001', '张三', '地理', 77);
insert into SQL_6 (cid, sname, course, score) values ('002', '小明', '英语', 55);
insert into SQL_6 (cid, sname, course, score) values ('002', '小红', '英语', 87);
insert into SQL_6 (cid, sname, course, score) values ('001', '李四', '英语', 61);
```
##### 分组内topN
问题1求出每个学生成绩最高的三条记录
```sql
select * from
(
select *, row_number() over (partition by sname order by score desc) as rn from SQL_6
) temp
where rn <= 3
```
###### 公式:
```sql
select * from
(
select *, row_number() over (partition by 分组列 order by 比较列) as rn from table
) as tmp
where rn <= N;
```
##### 汇总分析
问题2找出每门课程都高于班级课程平均分的学生
可以拆解成以下几个问题:
1求出每个班级每门课程的平均分
```sql
with
-- 1) 求出每个班级,每门课程的平均分
t1 as
(
select *,
avg(score) over (partition by cid, course) as 'avg'
from SQL_6
),
```
2将学生每门课程的成绩与所在班级的对应课程平均分相减结果大于0就说明该学生的这门成绩高于课程平均分
```sql
t2 as (
select *,
score - avg as 'del'
from t1
)
```
3“找出每门课程都高于班级课程平均分的学生”说明对于学生来说最小的“相减结果”都是大于0的
```sql
select sname from t2
group by sname
having min(del) > 0;
```
合并后的SQL语句
```sql
with
t1 as
(
select *,
avg(score) over (partition by cid, course) as 'avg'
from SQL_6
),
t2 as (
select *,
score - avg as 'del'
from t1
)
select sname from t2
group by sname
having min(del) > 0;
-- 或者
select sname from (
select *,
score - avg as 'del'
from (
select *,
avg(score) over (partition by cid, course) as 'avg'
from SQL_6
) t1
) t2
group by sname
having min(del) > 0;
```
#### 表格
| empno | ename | hire_date | salary | dept_no |
| ----- | ----- | ---------- | ------ | ------- |
| 001 | Adam | 2018-03-01 | 1000 | A |
| 002 | Bill | 2021-03-01 | 1200 | A |
| 003 | Cindy | 2016-03-01 | 1500 | A |
| 004 | Danny | 2020-03-01 | 5000 | A |
| 005 | Eason | 2020-03-01 | 4000 | B |
| 006 | Fred | 2018-03-01 | 3500 | B |
| 007 | Gary | 2017-03-01 | 1800 | B |
| 008 | Hugo | 2020-03-01 | 2500 | B |
#### 脚本
```sql
CREATE TABLE SQL_7 (
empno varchar(4),
ename varchar(10),
hire_date varchar(10),
salary int,
dept_no varchar(2)
);
insert into SQL_7 (empno, ename, hire_date, salary, dept_no) values ('001', 'Adam', '2018-03-01', 1000, 'A');
insert into SQL_7 (empno, ename, hire_date, salary, dept_no) values ('002', 'Bill', '2021-03-01', 1200, 'A');
insert into SQL_7 (empno, ename, hire_date, salary, dept_no) values ('003', 'Cindy', '2016-03-01', 1500, 'A');
insert into SQL_7 (empno, ename, hire_date, salary, dept_no) values ('004', 'Danny', '2020-03-01', 5000, 'A');
insert into SQL_7 (empno, ename, hire_date, salary, dept_no) values ('005', 'Eason', '2020-03-01', 4000, 'B');
insert into SQL_7 (empno, ename, hire_date, salary, dept_no) values ('006', 'Fred', '2018-03-01', 3500, 'B');
insert into SQL_7 (empno, ename, hire_date, salary, dept_no) values ('007', 'Gary', '2017-03-01', 1800, 'B');
insert into SQL_7 (empno, ename, hire_date, salary, dept_no) values ('008', 'Hugo', '2020-03-01', 4500, 'B');
select * from SQL_7;
```
##### 分组内topN
问题一:求出每个部门工资最高的前三名员工
```sql
select * from
(
select *, row_number() over (partition by dept_no order by salary desc) as rn from SQL_7
) as tmp
where rn <= 3;
```
##### 汇总分析
问题二:计算这些员工的工资占所属部门总工资的百分比
```sql
with
t1 as (
select * , sum(salary) over (partition by dept_no) as 'sum_sal' from SQL_7
),
t2 as (
select *, round(salary*100/sum_sal,2) as 'percentage' from t1
)
select * from t2;
```
问题三对各部门员工的工资进行从小到大排序排名前30%为低层30%-80%为中层高于80%为高层,并打上标签
```label
with
t1 as (
select * , row_number() over (partition by dept_no order by salary) as cnt,
count(empno) over (partition by dept_no) as 'sum' from SQL_7
),
t2 as (
select *, round(cnt/sum,2) as 'percentage' from t1
),
t3 as (
select *, case when percentage <= 0.3 then '低层'
when percentage <= 0.8 then '中层'
when percentage <= 1 then '高层' end as 'label'
from t2
)
select empno, ename, hire_date, salary, dept_no, label from t3;
```
问题四:统计每年入职总数以及截至本年累计入职总人数(本年总入职人数 + 本年之前所有年的总入职人数之和)
```sql
select year(hire_date) as hire_year, count(empno) as cnt
from SQL_7
group by year(hire_date) order by hire_year;
```
```sql
with t1 as (
select year(hire_date) as hire_year, count(empno) as cnt from SQL_7 group by year(hire_date) order by hire_year
)
select *, sum(cnt) over(partition by null rows between unbounded preceding and current row) as sum from t1;
```
### 五. 技巧
1分组内topN公式
```sql
select * from
(
select *, row_number() over (partition by 分组列 order by 比较列) as rn from table
) as tmp
where rn <= N;
```
2 窗口函数 -> 生成辅助列(相当于高级语言的临时变量)
3) with 语句 -> 生成临时表(相当于高级语言的局部方法)
把复杂的问题拆分成多个子问题并用临时表去表达

View File

@ -0,0 +1,324 @@
# SQL 讲解 —— 行转列 与 列转行
## 行转列
### 题目1
#### 描述
```
name subject score
张三 语文 78
张三 数学 88
张三 英语 98
李四 语文 89
李四 数学 76
李四 英语 90
王五 语文 99
王五 数学 66
王五 英语 91
name 语文 数学 英语
张三 78 88 98
李四 89 76 90
王五 99 66 91
```
#### 脚本
```sql
create table SQL_1
(
name varchar(20),
subject varchar(20),
score float
);
insert into SQL_1 (name, subject, score) values ('张三', '语文', 78);
insert into SQL_1 (name, subject, score) values ('张三', '数学', 88);
insert into SQL_1 (name, subject, score) values ('张三', '英语', 98);
insert into SQL_1 (name, subject, score) values ('李四', '语文', 89);
insert into SQL_1 (name, subject, score) values ('李四', '数学', 76);
insert into SQL_1 (name, subject, score) values ('李四', '英语', 90);
insert into SQL_1 (name, subject, score) values ('王五', '语文', 99);
insert into SQL_1 (name, subject, score) values ('王五', '数学', 66);
insert into SQL_1 (name, subject, score) values ('王五', '英语', 91);
select * from SQL_1;
```
#### 解题步骤
1 确定分组列,转换列,数据列
2 生成伪列
3 做分组查询
4 选择合适的聚合函数
#### 两步法
##### 公式:
```sql
select 分组列,
聚合函数(m1) as 列名1,
聚合函数(m2) as 列名2,
聚合函数(m3) as 列名3
from (select *,
case 转换列 when 转换列值1 then 数据列 else ... end as m1,
case 转换列 when 转换列值2 then 数据列 else ... end as m2,
case 转换列 when 转换列值3 then 数据列 else ... end as m3
from 表名) 临时表名
group by 分组列;
```
##### 解题SQL
```sql
select name,
sum(m1) as 语文,
sum(m2) as 数学,
sum(m3) as 英语
from (select *,
case subject when '语文' then score else 0 end as m1,
case subject when '数学' then score else 0 end as m2,
case subject when '英语' then score else 0 end as m3
from sql_1) tmp
group by name;
```
#### 一步法
##### 公式:
```sql
select 分组列,
聚合函数(case 转换列 when 转换列值1 then 数据列 else ... end) as 列名1,
聚合函数(case 转换列 when 转换列值2 then 数据列 else ... end) as 列名2,
聚合函数(case 转换列 when 转换列值3 then 数据列 else ... end) as 列名3
...
from 表名
group by 分组列;
select 分组列,
聚合函数(case when 转换列=转换列值1 then 数据列 else ... end) as 列名1,
聚合函数(case when 转换列=转换列值2 then 数据列 else ... end) as 列名2,
聚合函数(case when 转换列=转换列值3 then 数据列 else ... end) as 列名3
...
from 表名
group by 分组列;
```
##### 解题SQL
```sql
select name,
sum(case subject when '语文' then score else 0 end) as 语文,
sum(case subject when '数学' then score else 0 end) as 数学,
sum(case subject when '英语' then score else 0 end) as 英语
from sql_1
group by name;
select name,
sum(case when subject = '语文' then score else 0 end) as 语文,
sum(case when subject = '数学' then score else 0 end) as 数学,
sum(case when subject = '英语' then score else 0 end) as 英语
from sql_1
group by name;
```
### 题目2
#### 描述
```txt
# 日期 结果
# 2022-01-01 胜
# 2022-01-01 胜
# 2022-01-02 负
# 2022-01-02 负
# 2022-01-01 负
# 2022-01-02 负
# 2022-01-02 胜
# 日期 胜 负
# 2022-01-01 2 1
# 2022-01-02 1 3
```
#### 脚本
```sql
create table SQL_2(
ddate varchar(10), result varchar(2)
);
insert into SQL_2 (ddate, result) values('2022-01-01','胜');
insert into SQL_2 (ddate, result) values('2022-01-01','胜');
insert into SQL_2 (ddate, result) values('2022-01-02','负');
insert into SQL_2 (ddate, result) values('2022-01-02','负');
insert into SQL_2 (ddate, result) values('2022-01-01','负');
insert into SQL_2 (ddate, result) values('2022-01-02','负');
insert into SQL_2 (ddate, result) values('2022-01-02','胜');
select * from SQL_2;
```
#### 解题SQL
```sql
select ddate,
sum(case when result = '胜' then 1 else 0 end) as 胜,
sum(case when result = '负' then 1 else 0 end) as 负
from sql_2
group by ddate;
```
## 列转行
### 题目3
#### 描述
```txt
name 语文 数学 英语
张三 78 88 98
李四 89 76 90
王五 99 66 91
name subject score
张三 语文 78
张三 数学 88
张三 英语 98
李四 语文 89
李四 数学 76
李四 英语 90
王五 语文 99
王五 数学 66
王五 英语 91
```
#### 脚本
```sql
CREATE TABLE SQL_3 (
name varchar(20),
语文 float,
数学 float,
英语 float
);
insert into SQL_3 (name, '语文', '数学', '英语') values ('张三', 78, 88, 98);
insert into SQL_3 (name, '语文', '数学', '英语') values ('李四', 89, 76, 90);
insert into SQL_3 (name, '语文', '数学', '英语') values ('王五', 99, 66, 91);
```
#### 解题步骤
1 确定转换列,非转换列
2 生成新列
3 使用union或union all 进行合并
4 根据需要进行order by
#### 公式
```sql
SELECT 非转换列, '转换列1' AS 新转换列名, 转换列1 AS 新数据列名 FROM 表名
UNION ALL
SELECT 非转换列, '转换列2' AS 新转换列名, 转换列2 AS 新数据列名 FROM 表名
UNION ALL
SELECT 非转换列, '转换列3' AS 新转换列名, 转换列3 AS 新数据列名 FROM 表名
ORDER BY ...;
```
#### 解题SQL
```sql
SELECT name,'语文' AS subject,语文 AS score FROM SQL_3
UNION ALL
SELECT name,'数学' AS subject,数学 AS score FROM SQL_3
UNION ALL
SELECT name,'英语' AS subject,英语 AS score FROM SQL_3
ORDER BY name ASC, subject DESC;
```
### 题目4
#### 描述
```txt
Q1 Q2 Q3 Q4
1000 2000 3000 4000
季度 业绩
Q1 1000
Q2 2000
Q3 3000
Q4 4000
```
#### 脚本
```sql
CREATE TABLE SQL_4 (
Q1 int, Q2 int, Q3 int, Q4 int
);
insert into SQL_4 values (1000, 2000, 3000, 4000);
```
#### 解题SQL
```sql
SELECT 'Q1' AS 季度, Q1 AS 业绩 FROM SQL_4
UNION ALL
SELECT 'Q2' AS 季度, Q2 AS 业绩 FROM SQL_4
UNION ALL
SELECT 'Q3' AS 季度, Q3 AS 业绩 FROM SQL_4
UNION ALL
SELECT 'Q4' AS 季度, Q4 AS 业绩 FROM SQL_4
ORDER BY 季度;
```
### 技巧:
扩展列select ... as 新列名
减少列:直接不写
扩展行union/ union all
减少行: 聚合函数

231
SQL窗口函数(二).md Normal file
View File

@ -0,0 +1,231 @@
# SQL窗口函数—— 连续问题
### 题目一
#### 表格
| user_id | login_date |
| ------- | ---------- |
| A | 2022-09-02 |
| A | 2022-09-03 |
| A | 2022-09-04 |
| B | 2021-11-25 |
| B | 2021-12-31 |
| C | 2022-01-01 |
| C | 2022-04-04 |
| C | 2022-09-03 |
| C | 2022-09-04 |
| C | 2022-09-05 |
| A | 2022-09-03 |
| D | 2022-10-20 |
| D | 2022-10-21 |
| A | 2022-10-03 |
| D | 2022-10-22 |
| D | 2022-10-23 |
#### 脚本
```sql
CREATE TABLE SQL_8
(
user_id varchar(2),
login_date date
);
INSERT INTO SQL_8 (user_id,login_date)
VALUES ('A', '2022-09-02'), ('A', '2022-09-03'), ('A', '2022-09-04'), ('B', '2021-11-25'),
('B', '2021-12-31'), ('C', '2022-01-01'), ('C', '2022-04-04'), ('C', '2022-09-03'),
('C', '2022-09-05'), ('C', '2022-09-04'), ('A', '2022-09-03'), ('D', '2022-10-20'),
('D', '2022-10-21'), ('A', '2022-10-03'), ('D', '2022-10-22'), ('D', '2022-10-23');
```
#### 问题
找出这张表中所有的连续3天登录用户
#### 分析
连续N天登录用户要求数据行满足以下条件
1) userid 要相同,表示同一用户
2) 同一用户每行记录以登录时间从小到大排序
3) 后一行记录比前一行记录的登录时间多一天
4) 数据行数大于等于N
#### 解答
```sql
-- 方法一
with t1 as (
select distinct user_id, login_date from SQL_8
),
t2 as (
select *, row_number() over (partition by user_id order by login_date) as rn from t1
),
t3 as (
select *, DATE_SUB(login_date, interval rn day) as sub from t2
)
select distinct user_id from t3 group by user_id, sub having count(user_id) >= 3;
-- 方法二
with t1 as (
select distinct user_id, login_date from SQL_8
),
t2 as (
select *, DATEDIFF(login_date, lag(login_date, 1) over (partition by user_id order by login_date)) as diff from t1
)
select user_id from t2 where diff = 1 group by user_id having count(user_id) >= 2;
```
### 题目二
#### 表格
| player_id | score | score_time |
| --------- | ----- | ------------------- |
| B3 | 1 | 2022-09-20 19:00:14 |
| A2 | 1 | 2022-09-20 19:01:04 |
| A2 | 3 | 2022-09-20 19:01:16 |
| A2 | 3 | 2022-09-20 19:02:05 |
| A2 | 2 | 2022-09-20 19:02:25 |
| B5 | 2 | 2022-09-20 19:02:54 |
| A4 | 3 | 2022-09-20 19:03:10 |
| B1 | 2 | 2022-09-20 19:03:34 |
| B1 | 2 | 2022-09-20 19:03:58 |
| B1 | 3 | 2022-09-20 19:04:07 |
| A2 | 1 | 2022-09-20 19:04:19 |
| A3 | 2 | 2022-09-20 19:04:31 |
#### 脚本
```sql
CREATE TABLE SQL_9
(
player_id varchar(2),
score int,
score_time datetime
);
INSERT INTO SQL_9 (player_id, score, score_time)
VALUES ('B3', 1, '2022-09-20 19:00:14'), ('A2', 1, '2022-09-20 19:01:04'),
('A2', 3, '2022-09-20 19:01:16'), ('A2', 3, '2022-09-20 19:02:05'),
('A2', 2, '2022-09-20 19:02:25'), ('B3', 2, '2022-09-20 19:02:54'),
('A4', 3, '2022-09-20 19:03:10'), ('B1', 2, '2022-09-20 19:03:34'),
('B1', 2, '2022-09-20 19:03:58'), ('B1', 3, '2022-09-20 19:04:07'),
('A2', 1, '2022-09-20 19:04:19'), ('B3', 2, '2022-09-20 19:04:31');
```
#### 问题
统计出连续三次(及以上)为球队得分的球员名单
#### 分析
连续N次以上为球队得分 要求数据行满足以下条件:
1) player_id 要相同表示同一球员
2) 每行记录以得分时间从小到大排序
3) 数据行数大于等于N
#### 解答
```sql
-- 方法一
with t1 as (
select *, lag(player_id, 1) over (order by score_time) as last_play_id from SQL_9
)
select distinct player_id from t1 where player_id = last_play_id group by player_id having count(player_id) >= 2;
-- 方法二
with t1 as (
select *, row_number() over (order by score_time) as rn from SQL_9
),
t2 as (
select *, row_number() over (order by score_time) + 1 as rn from SQL_9
),
t3 as (
select t1.player_id as player_id from t1 join t2 on t1.rn = t2.rn and t1.player_id = t2.player_id
)
select distinct player_id from t3 group by player_id having count(player_id) >= 2;
```
### 题目三
#### 表格
| log_id |
| :----: |
| 1 |
| 2 |
| 3 |
| 7 |
| 8 |
| 10 |
#### 脚本
```sql
CREATE TABLE SQL_10
(
log_id int
);
INSERT INTO SQL_10 (log_id) VALUES (1), (2), (3), (7), (8), (10);
```
#### 问题
编写SQL 查询得到 Logs 表中的连续区间的开始数字和结束数字。按照 start_id 排序。查询结果格式如下:
| start_id | end_id |
| -------- | ------ |
| 1 | 3 |
| 7 | 8 |
| 10 | 10 |
#### 解答
```sql
-- 方法一
with t1 as (
select *, log_id - row_number() over (order by log_id) as gr from SQL_10
)
select min(log_id) as start_id, max(log_id) as end_id from t1 group by gr
-- 方法二
with t1 as (
select *, log_id - row_number() over (order by log_id) as gr, log_id - lag(log_id,1) over () as diff from SQL_10
),
t2 as (
select log_id, gr from t1 where ifnull(diff,-1) <> 1
),
t3 as (
select *, log_id - row_number() over (order by log_id) as gr, log_id - lead(log_id,1) over () as diff from SQL_10
),
t4 as (
select log_id, gr from t3 where ifnull(diff, 1) <> -1
)
select t2.log_id as start_id, t4.log_id as end_id from t2, t4 where t2.gr = t4.gr;
```
### 技巧
如何求连续区间?
1行号过滤法
通过row_number() 生成连续行号,与区间列进行差值运算,得到的临时结果如果相同表示为同一连续区间
2 错位比较法
通过row_number() / row_number() + 1 分别生成原生的和错位的连续行号列,进行连表操作
也可以通过lag/lead函数直接生成错位列

View File

@ -1,6 +1,6 @@
# Centos 安装 Docker # Linux 安装 Docker
@ -9,7 +9,7 @@
一键安装脚本Linux系统都支持 一键安装脚本Linux系统都支持
```bash ```bash
curl -sSL https://get.docker.com/ | sh curl -sSL https://get.docker.com/ | sh
#国内阿里云镜像 #国内阿里云镜像
curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun
#Azure源(中国区azure) #Azure源(中国区azure)
@ -18,6 +18,39 @@
## oracle linxu
1. 执行以下命令以安装Docker依赖项
```
sudo yum -y install yum-utils device-mapper-persistent-data lvm2
```
2. 执行以下命令以添加Docker官方GPG key
```
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
```
3. 执行以下命令以安装Docker CE
```
sudo yum install docker-ce
```
4. 启动并设置Docker服务为开机自启
```
sudo systemctl start docker
sudo systemctl enable docker
```
通过`docker --version`命令您可以检查Docker是否已经安装。如果命令输出了版本信息说明已经成功安装Docker CE。
希望这次能给您提供一个更简洁的方法。
## 启动服务 ## 启动服务
```bash ```bash
@ -64,14 +97,17 @@ Docker中国https://registry.docker-cn.com
### 写入配置文件 重启服务 ### 写入配置文件 重启服务
``` ```
sudo mkdir -p /etc/docker sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF' #注意EOF前不能有空格
{ sudo tee /etc/docker/daemon.json <<-'EOF'
"registry-mirrors": ["https://mirror.ccs.tencentyun.com"] {
} "registry-mirrors": ["https://mirror.ccs.tencentyun.com"]
EOF }
EOF
```
```
sudo systemctl daemon-reload sudo systemctl daemon-reload
sudo systemctl restart docker sudo systemctl restart docker
``` ```

View File

@ -22,7 +22,6 @@ s :取代,可以直接进行取代的工作哩!通常这个 s 的动作可
可以使用正则 特殊字符\转义 可以使用正则 特殊字符\转义
sed ”s/要被取代的字串/新的字串/g“ fileName sed "s/要被取代的字串/新的字串/g" fileName
``` ```

View File

@ -637,7 +637,15 @@ psql -Uicctestedb -dicctestedb -h192.168.53.123 -f ~/
# -O 不设置表归属, # -O 不设置表归属,
# -F c 自定义压缩 # -F c 自定义压缩
# -v 显示详情 # -v 显示详情
./pg_dump -Uenterprisedb -diccedb -h192.168.53.118 -O -v -F c -f ~/diccedb_202207_29.data.sql ./pg_dump -Uenterprisedb -diccedb -h192.168.53.118 -O -v -F c -f ~/alg.da
```
#### 数据导入
```shell
#导入数据 #导入数据
#-c 指定恢复过程中清空目标数据库中的现有表 #-c 指定恢复过程中清空目标数据库中的现有表
@ -655,10 +663,6 @@ psql -Uicctestedb -dicctestedb -h192.168.53.123 -f ~/
#### COPY 导出导出部分数据 #### COPY 导出导出部分数据
```sh ```sh

View File

@ -0,0 +1,214 @@
## redis-v3.0.3哨兵集群搭建文档
### 准备工作
### 主机
准备三台全新 centos linux 服务器
固定IP
```
192.168.1.11
192.168.1.12
192.168.1.13
```
**以下命令如无特殊说明 在三台主机中均要执行**
准备环境
```bash
yum install make gcc wget
```
创建用户
```bash
useradd redis
```
创建相关目录
```bash
mkdir -p /opt/{app/redis,applog}
```
授权
```bash
chown -R redis:redis /opt/app/redis/
```
```bash
chown -R redis:redis /opt/applog/redis/
```
#### 编译源码
切换用户
```bash
su - redis
```
下载源码
```bash
cd /opt/app/redis/
wget https://download.redis.io/releases/redis-3.0.3.tar.gz
```
解压源码
```bash
tar -zxvf redis-3.0.3.tar.gz
```
编译源码 这里为了和生产一致 使用libc 内存分配器
```bash
cd redis-3.0.3
make MALLOC=libc
```
安装
```bash
make PREFIX=/opt/app/redis/ install
```
配置 path
```bash
echo 'export PATH=$PATH:/opt/app/redis/bin' >> ~/.bashrc
source ~/.bashrc
```
### 配置redis
复制配置文件
```bash
cp /opt/app/redis/redis-3.0.3/src/redis.conf /opt/app/redis/
cp /opt/app/redis/redis-3.0.3/src/sentinel.conf /opt/app/redis/
```
编辑配置文件 redis.conf
```bash
sed -i 's/daemonize no/daemonize yes/g' redis.conf
sed -i 's|^logfile ""$|logfile "/opt/applog/redis/redis.log"|g' redis.conf
sed -i 's|^dir "./"$|dir "/opt/app/redis/"|g' redis.conf
sed -i 's/appendonly no/appendonly yes/g' redis.conf
sed -i 's/^# cluster-node-timeout 15000$/cluster-node-timeout 5000/g' redis.conf
#设置主从复制密码
sed -i 's/^# masterauth <master-password>$/masterauth dUw~7a)6/g' redis.conf
#设置 节点密码
sed -i 's/^# requirepass foobared$/requirepass dUw~7a)6/g' redis.conf
```
配置主从节点
在 192.168.1.12192.168.1.13 两台机子中执行
```bash
echo "slaveof 10.23.101.3 6379" >> redis.conf
```
编辑配置文件 sentinel.conf
```bash
sed -i 's|^\(sentinel monitor mymaster\) 127.0.0.1|\1 181.168.1.11|' sentinel.conf
#设置哨兵密码
sed -i 's/^#sentinel auth-pass <master-name> <password>/sentinel auth-pass mymaster HmpYZ2KB/g' sentinel.conf
echo "daemonize yes" >> sentinel.conf
```
编写动脚本
```bash
echo "bin/redis-server redis.config">/opt/app/redis/start.sh
chmod 775 start.sh
echo "bin/redis-sentinel sentinel.config">/opt/app/redis/start-sentinel.sh
chmod 775 start.sh
```
指定用户启动
```bash
#!/bin/bash
su -s /bin/bash -c "$(pwd)/bin/redis-sentinel $(pwd)/sentinel.conf " redis
```
### 启动
先启动三个redis节点
```bash
bash start.sh
```
再启动 sentinel j节点
```bash
bash start-sentinel.sh
```
### 验证
登录三个redis节点 分别写入key
192.168.1.11 成功写入 并同步至 192.168.1.12192.168.1.13
192.168.1.12 写入失败
192.168.1.13 写入失败
登录哨兵节点 查看哨兵信息
```bash
redis-cli -p 26379
sentinel sentinels mymaster
```
杀死 主节点
分别查看 另外两台redis 的info 信息 是否发生切换