跳至主要內容

SQL

HeChuangJun约 1228 字大约 4 分钟

t_grade成绩表

namekemufenshu
张三语文81
张三数学72
李四语文76
李四数学90

CASE WHEN使用

分数小于80为及格,大于等于80低于90分为中等;大于等于90分为优秀

namekemufenshu
张三语文中等
select name,kemu,case when fenshu>=90 then '优秀' when fenshu<90 and fenshu>=80 then '中等' 
when fenshu<80 then '合格' end  as fenshu from t_grade

分组及其组内比较

查出每个学科低于平均分/60的学生(结果表头为:姓名、学科、分数、学科平均分)
查出平均分临时表再连接原表

SELECT 
	a.name,a.kemu,a.fenshu,b.aa
FROM 
	test a,(SELECT kemu,AVG(fenshu) aa FROM test GROUP BY kemu) b
WHERE a.kemu=b.kemu AND a.fenshu<b.aa

都大于问题

查出每门课都大于80分的学生姓名
查出某条件下都大于都小于的数据(not in)

SELECT 
	name,kemu,fenshu 
from 
	t_grade 
where name not in(select name from t_grade where fenshu<=80)

行转列

姓名语文数学
张三8172

先按某个条件分组通过max/sum(case when then)或者sum/max(IF())行转列,但列数有限制

SELECT 
	a.name '分数',a.fenshu '语文',b.fenshu '数学'
FROM 
	(SELECT NAME,fenshu FROM test WHERE kemu='语文') a,
	(SELECT NAME,fenshu FROM test WHERE kemu='数学') b
WHERE 
	a.name=b.name;

SELECT
	name as 姓名,
	MAX(
		CASE 
		WHEN  kemu='语文' THEN
			fenshu
		END
	) AS 语文,
	MAX(
		CASE 
		WHEN kemu='数学' THEN
			fenshu
		END
	) AS 数学 
FROM
	t_grade
GROUP BY name

列转行

将上面的结果变回例子的样子

select name, '语文' kemu , 语文 as SCORE from t_grade
union select name, '数学' kemu, 数学 as SCORE from t_grade
union select name, '英语' kemu, 英语 as SCORE from t_grade
order by name,kemu;

TopN问题

求出第二(n)高/低的薪水
第2高:select max(distinct 字段) from table where 字段< (select max(distinct 字段) from table group by 分组字段) where 条件
第/前N高:select distinct 字段 FROM table t1 where (SELECT count(distinct 字段) from table t2 WHERE t2.字段 > t1.字段) = n-1/< n
第几:select 字段,(select count(distinct 字段) from table t2 where t2.字段 >= t1.字段) from Scores t1 order by 字段 DESC

Employee 表包含所有员工信息,工号Id,姓名Name,工资Salary和部门编号DepartmentId

IdNameSalaryDepartmentId
1Joe850001
2Henry800002
3Sam600002
4Max900001
5Janet690001
6Randy850001
7Will700001

Department表包含所有部门

IdName
1IT
2Sales

找出每个部门获得前三高工资的所有员工

DepartmentEmployeeSalary
ITMax90000
ITRandy85000
ITJoe85000
ITWill70000
SalesHenry80000
SalesSam60000
SELECT
	d.NAME AS Department,
	e.NAME AS Employee,
	e.Salary AS Salary 
FROM
	employee e
	LEFT JOIN Department d ON d.id = e.DepartmentId 
WHERE
	e.id IN (
	SELECT
		e1.id 
	FROM
		Employee e1
		LEFT JOIN Employee e2 ON e2.DepartmentId = e1.DepartmentId 
		AND e2.Salary > e1.Salary 
	GROUP BY
		e1.Id 
	HAVING
		count( DISTINCT e2.Salary ) <= 2 --选取top几
	) 
and e.DepartmentId in (select Id from Department) 补全数据,不用补全的话不用加上这句
ORDER BY
	d.Id ASC,
	e.Salary DESC

获取Employee表中第二高薪水(Salary),返回200,如果不存在第二高的薪水,那么查询应返回 null

删除重复数据

-- 只保留最小id的记录
DELETE FROM student WHERE id IN (
    SELECT * FROM (
        SELECT id FROM student WHERE (stuno,stuname) -- 加括号当成联合字段来处理
        IN (
            -- 查找学号和姓名均重复的学生信息
            SELECT stuno,stuname FROM student GROUP BY stuno,stuname HAVING COUNT(1) > 1
        ) AND id NOT IN (
            -- 查询最小id的记录
            SELECT MIN(id) FROM student GROUP BY stuno,stuname HAVING COUNT(1) > 1
        )
    ) AS stu_repeat_copy
);

查找每个学科分数低于60的人数

CREATE TABLE `xueke`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `tid` int(11) NULL DEFAULT NULL,
  `sid` int(11) NULL DEFAULT NULL,
  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `score` int(11) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = latin1 COLLATE = latin1_swedish_ci ROW_FORMAT = Compact;

-- ----------------------------
-- Records of xueke
-- ----------------------------
INSERT INTO `xueke` VALUES (1, 1, 1, '数学', 80);
INSERT INTO `xueke` VALUES (2, 1, 2, '数学', 50);
INSERT INTO `xueke` VALUES (3, 2, 3, '英语', 61);
INSERT INTO `xueke` VALUES (4, 2, 4, '英语', 59);
INSERT INTO `xueke` VALUES (5, 3, 5, '语文', 62);
INSERT INTO `xueke` VALUES (6, 3, 6, '语文', 58);
INSERT INTO `xueke` VALUES (7, 1, 7, '数学', 81);

select name,count(sid) from xueke where score < 60 GROUP BY name;

去除最高值与最低值的平均问题

SELECT
	t.cid,
	round(AVG(t.saleMoney),2) '平均值'
FROM
(
SELECT
	a.date,
	a.cid,
	a.saleMoney,
	RANK() over(partition by a.cid order by a.saleMoney) as rank1,
	RANK() over(partition by a.cid order by a.saleMoney desc) as rank2
FROM
	city_sale a
GROUP BY a.date,a.cid
)t
WHERE t.rank1>1 
and t.rank2>1
GROUP BY t.cid

lead、lag函数平移列(求记录之间的差值最大值或者最小值)

select 
	t2.uid,
	count(1),
	min(t2.minute2)
from
(
select *,
	TIMESTAMPDIFF(MINUTE,t.crtime,t.rank1) minute2
from 
(
SELECT
	a.uid,
	a.crtime,
	lead(a.crtime,1) over(PARTITION BY a.uid ORDER BY a.crtime) as rank1
FROM
	user_sale a
)t
)t2
where t2.minute2>0
GROUP BY t2.uid
ORDER BY count(1) desc

中位数

## 单个cid的中位数

SELECT
	avg(t.saleMoney)
FROM
(
SELECT
	a.cid,
	a.saleMoney,
	row_number() over(ORDER BY a.saleMoney desc) rank1,
	row_number() over(ORDER BY a.saleMoney) rank2
FROM
	city_sale a
where a.cid=10240
)t
where  t.rank1=t.rank2+1 or t.rank1=t.rank2-1 or rank1=rank2

## 分组求中位数

SELECT
	t.cid,
	avg(t.saleMoney)
FROM
(
SELECT
	a.cid,
	a.saleMoney,
	row_number() over(PARTITION by a.cid ORDER BY a.saleMoney desc) rank1,
	row_number() over(PARTITION by a.cid ORDER BY a.saleMoney) rank2
FROM
	city_sale a
)t
where  t.rank1=t.rank2+1 or t.rank1=t.rank2-1 or rank1=rank2

GROUP BY t.cid