diff --git a/SQL-窗口函数.md b/SQL-窗口函数.md new file mode 100644 index 0000000..5542dcc --- /dev/null +++ b/SQL-窗口函数.md @@ -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 语句 -> 生成临时表(相当于高级语言的局部方法) + +​ 把复杂的问题拆分成多个子问题并用临时表去表达 + + + + + + + + + + + + + + + + + + + + + diff --git a/SQL-行转列与列转行.md b/SQL-行转列与列转行.md new file mode 100644 index 0000000..ca10eb0 --- /dev/null +++ b/SQL-行转列与列转行.md @@ -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 + +减少行: 聚合函数 + + + diff --git a/SQL窗口函数(二).md b/SQL窗口函数(二).md new file mode 100644 index 0000000..aabfe46 --- /dev/null +++ b/SQL窗口函数(二).md @@ -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函数直接生成错位列 \ No newline at end of file diff --git a/centos 安装 docker.md b/centos 安装 docker.md index 6910a39..e4de180 100644 --- a/centos 安装 docker.md +++ b/centos 安装 docker.md @@ -1,6 +1,6 @@ -# Centos 安装 Docker +# Linux 安装 Docker @@ -9,7 +9,7 @@ 一键安装脚本!Linux系统都支持! ```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 #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 @@ -64,14 +97,17 @@ Docker中国:https://registry.docker-cn.com ### 写入配置文件 重启服务 ``` - sudo mkdir -p /etc/docker - - sudo tee /etc/docker/daemon.json <<-'EOF' - { - "registry-mirrors": ["https://mirror.ccs.tencentyun.com"] - } - EOF +sudo mkdir -p /etc/docker +#注意EOF前不能有空格 +sudo tee /etc/docker/daemon.json <<-'EOF' +{ +"registry-mirrors": ["https://mirror.ccs.tencentyun.com"] +} +EOF +``` + +``` sudo systemctl daemon-reload sudo systemctl restart docker ``` diff --git a/linux/linux使用sed命令.md b/linux/linux使用sed命令.md index b978516..155fdca 100644 --- a/linux/linux使用sed命令.md +++ b/linux/linux使用sed命令.md @@ -22,7 +22,6 @@ s :取代,可以直接进行取代的工作哩!通常这个 s 的动作可 可以使用正则 特殊字符\转义 -sed ”s/要被取代的字串/新的字串/g“ fileName +sed "s/要被取代的字串/新的字串/g" fileName ``` - diff --git a/postgresql_and_edb/postgresql导入导出.md b/postgresql_and_edb/postgresql导入导出.md index e1ba8ae..df4882e 100644 --- a/postgresql_and_edb/postgresql导入导出.md +++ b/postgresql_and_edb/postgresql导入导出.md @@ -637,7 +637,15 @@ psql -Uicctestedb -dicctestedb -h192.168.53.123 -f ~/ # -O 不设置表归属, # -F c 自定义压缩 # -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 指定恢复过程中清空目标数据库中的现有表 @@ -655,10 +663,6 @@ psql -Uicctestedb -dicctestedb -h192.168.53.123 -f ~/ - - - - #### COPY 导出导出部分数据 ```sh diff --git a/redis/redis-3.0.3,哨兵集群.md b/redis/redis-3.0.3,哨兵集群.md new file mode 100644 index 0000000..5817184 --- /dev/null +++ b/redis/redis-3.0.3,哨兵集群.md @@ -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 $/masterauth dUw~7a)6/g' redis.conf +#设置 节点密码 +sed -i 's/^# requirepass foobared$/requirepass dUw~7a)6/g' redis.conf +``` + + + +配置主从节点 + +在 192.168.1.12,192.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 /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.12,192.168.1.13 +192.168.1.12 写入失败 +192.168.1.13 写入失败 + + + +登录哨兵节点 查看哨兵信息 + +```bash +redis-cli -p 26379 + +sentinel sentinels mymaster +``` + + + +杀死 主节点 + +分别查看 另外两台redis 的info 信息 是否发生切换 \ No newline at end of file