MySQL 数据库函数教程
https://www.begtut.com/mysql/mysql-sample-database.html
MySQL LAG() 函数
https://www.begtut.com/mysql/mysql-lag-function.html
简介:在本教程中,您将学习如何使用MySQL LAG()
函数从同一结果集中的当前行访问上一行的数据。
LAG()
函数是一个窗口函数,允许您回顾多行并从当前行访问行的数据。
以下说明了LAG()
函数的语法:
LAG(<expression>[,offset[, default_value]]) OVER (
PARTITION BY expr,...
ORDER BY expr [ASC|DESC],...
)
expression
LAG()
函数返回expression
当前行之前的行的值,其值为offset
其分区或结果集中的行数。
offset
offset
是从当前行返回的行数,以获取值。offset
必须是零或文字正整数。如果offset
为零,则LAG()
函数计算expression
当前行的值。如果未指定offset
,则LAG()
默认情况下函数使用一个。
default_value
如果没有前一行,则LAG()
函数返回default_value
。例如,如果offset为2,则第一行的返回值为default_value
。如果省略default_value
,则默认LAG()
返回函数NULL
。
PARTITION BY
子句
PARTITION BY
子句将结果集中的行划分LAG()
为应用函数的分区。如果省略PARTITION BY
子句,LAG()
函数会将整个结果集视为单个分区。
ORDER BY
子句
ORDER BY
子句指定在LAG()
应用函数之前每个分区中的行的顺序。
LAG()
函数可用于计算当前行和上一行之间的差异
Customers: 用户
Products: 比例模型车列表
ProductLines: 产品系列类别列表
Orders: 销售订单
OrderDetails: 每个销售订单的单行项目
Payments: 根据账户付款
Employees: 存储员工组织架构,向谁报告
Offices: 销售办公室数据
1.牛客网
SQL1 查找最晚入职员工的所有信息
https://www.nowcoder.com/practice/218ae58dfdcd4af195fff264e062138f?tpId=82&tags=&title=&difficulty=0&judgeStatus=0&rp=1
drop table if exists `employees` ;
CREATE TABLE `employees` (
`emp_no` int(11) NOT NULL,
`birth_date` date NOT NULL,
`first_name` varchar(14) NOT NULL,
`last_name` varchar(16) NOT NULL,
`gender` char(1) NOT NULL,
`hire_date` date NOT NULL,
PRIMARY KEY (`emp_no`));
INSERT INTO employees VALUES(10001,'1953-09-02','Georgi','Facello','M','1986-06-26');
INSERT INTO employees VALUES(10002,'1964-06-02','Bezalel','Simmel','F','1985-11-21');
INSERT INTO employees VALUES(10003,'1959-12-03','Parto','Bamford','M','1986-08-28');
INSERT INTO employees VALUES(10004,'1954-05-01','Chirstian','Koblick','M','1986-12-01');
INSERT INTO employees VALUES(10005,'1955-01-21','Kyoichi','Maliniak','M','1989-09-12');
INSERT INTO employees VALUES(10006,'1953-04-20','Anneke','Preusig','F','1989-06-02');
INSERT INTO employees VALUES(10007,'1957-05-23','Tzvetan','Zielinski','F','1989-02-10');
INSERT INTO employees VALUES(10008,'1958-02-19','Saniya','Kalloufi','M','1994-09-15');
INSERT INTO employees VALUES(10009,'1952-04-19','Sumant','Peac','F','1985-02-18');
INSERT INTO employees VALUES(10010,'1963-06-01','Duangkaew','Piveteau','F','1989-08-24');
INSERT INTO employees VALUES(10011,'1953-11-07','Mary','Sluis','F','1990-01-22');
解法1 :按照入职时间 排序 然后取出第一条
select * from employees order by hire_date desc limit 1 ;
SELECT * FROM employees order by hire_date desc limit 0,1
SELECT * FROM employees WHERE hire_date == (SELECT MAX(hire_date) FROM employees)
SQL 3 : SQL3 查找当前薪水详情以及部门编号dept_no
-- select d.emp_no ,s.salary ,s.from_date ,d.to_date,d.dept_no
-- from dept_manager d left join salaries s on d.emp_no =s.emp_no order by emp_no ;
select d.emp_no ,s.salary ,s.from_date ,d.to_date,d.dept_no
from dept_manager d , salaries s where d.emp_no =s.emp_no order by emp_no ;
SQL4 查找所有已经分配部门的员工的last_name和first_name以及dept_no
select e.last_name,e.first_name,d.dept_no from employees e ,dept_emp d where e.emp_no =d.emp_no
and d.dept_no is not null
SQL5 :
select e.last_name,e.first_name,d.dept_no from employees e left join dept_emp d on e.emp_no =d.emp_no
1.2 分数
1.2.1 题解 | #考试分数(一)
题目链接:http://www.nowcoder.com/practice/f41b94b4efce4b76b27dd36433abe398
drop table if exists grade;
CREATE TABLE grade(
`id` int(4) NOT NULL,
`job` varchar(32) NOT NULL,
`score` int(10) NOT NULL,
PRIMARY KEY (`id`));
INSERT INTO grade VALUES
(1,'C++',11001),
(2,'C++',10000),
(3,'C++',9000),
(4,'Java',12000),
(5,'Java',13000),
(6,'JS',12000),
(7,'JS',11000),
(8,'JS',9999);
预期:
输出:
Java|12500.000
JS|10999.667
C++|10000.333
题解:
先按照job 分组然后统计每个分组的求和除以统计个数、然后保留小数3位。再按照平均分排序
-- select job|avg(1.0*score) from grade group by job ;
select job , round((1.0*sum(score)/count(id) ),3) as a from grade group by job order by a desc ;
1.2.2 考试分数2
:http://www.nowcoder.com/practice/f456dedf88a64f169aadd648491a27c1
drop table if exists grade;
CREATE TABLE grade(
`id` int(4) NOT NULL,
`job` varchar(32) NOT NULL,
`score` int(10) NOT NULL,
PRIMARY KEY (`id`));
INSERT INTO grade VALUES
(1,'C++',11001),
(2,'C++',10000),
(3,'C++',9000),
(4,'Java',12000),
(5,'Java',13000),
(6,'JS',12000),
(7,'JS',11000),
(8,'JS',9999),
(9,'Java',12500);
输出:
1|C++|11001
5|Java|13000
6|JS|12000
7|JS|11000
写一个sql语句查询用户分数大于其所在工作(job)分数的平均分的所有grade的属性,并且以id的升序排序
-- select job|avg(1.0*score) from grade group by job ;
select job , round((1.0*sum(score)/count(id) ),3) as a from grade group by job order by a desc ;
mysql 分组查询取各分组的前两名及子查询性能优化
https://blog.csdn.net/sinat_41780498/article/details/79956561
背景 求出运动员前两个比赛记录
如何找出每个程序最近的日志条目?如何找出每个用户的最高分?在每个分类中最受欢迎的商品是什么?通常这类“找出每个分组中最高分的条目”的问题可以使用相同的技术来解决。在这篇文章里我将介绍如何解决这类问题,而且会介绍如何找出最高的前几名而不仅仅是第一名。
https://www.cnblogs.com/zhuiluoyu/p/6862547.html
1.2.3 考试分数3 重要
找出每个岗位分数排名前2名的用户,得到的结果先按照language的name升序排序,再按照积分降序排序,最后按照grade的id升序排序
drop table if exists grade;
drop table if exists language;
CREATE TABLE `grade` (
`id` int(4) NOT NULL,
`language_id` int(4) NOT NULL,
`score` int(4) NOT NULL,
PRIMARY KEY (`id`));
CREATE TABLE `language` (
`id` int(4) NOT NULL,
`name` varchar(32) NOT NULL,
PRIMARY KEY (`id`));
INSERT INTO grade VALUES
(1,1,12000),
(2,1,13000),
(3,2,11000),
(4,2,10000),
(5,3,11000),
(6,1,11000),
(7,2,11000);
INSERT INTO language VALUES
(1,'C++'),
(2,'JAVA'),
(3,'Python');
2|C++|13000
1|C++|12000
3|JAVA|11000
7|JAVA|11000
4|JAVA|10000
5|Python|11000
核心思路: 将当前表分数跟一个临时表进行比较,当临时表没有一个分数,或者只有一个分数比当前表高时,说明该分数排名是第一名,或者第二名。
举例:只看C++语言,id为1时,12000<13000,说明这是第二名,id为2时,13000统计出来为0个,没人比他小,说明他是第一名,id为6时,可以知道11000小于12000和13000,为2,说明是第三名。
避坑点:一定要去重!不然在java语言的时候,10000小于两个12000,就是第三名了,然而题目要求可以并列,为第二名
select g1.id ,la.name ,g1.score from grade g1 ,language la where
g1.language_id = la.id and
( select count( distinct g2.score) from grade g2 where g1.language_id=g2.language_id
and g1.score < g2.score) in (0,1) order by la.name asc, g1.score desc ,g1.id asc ;
SQL75 考试分数(四) 分组分数中位数 较难
drop table if exists grade;
CREATE TABLE grade(
`id` int(4) NOT NULL,
`job` varchar(32) NOT NULL,
`score` int(10) NOT NULL,
PRIMARY KEY (`id`));
INSERT INTO grade VALUES
(1,'C++',11001),
(2,'C++',10000),
(3,'C++',9000),
(4,'Java',12000),
(5,'Java',13000),
(6,'B',12000),
(7,'B',11000),
(8,'B',9999);
解法1 :
select job, FLOOR((count(*)+1)/2) as "start" ,CEIL((count(*)+1)/2) as "end"
from grade group by job order by job ;
select job, FLOOR((count(*)+1)/2) as "start" ,floor((count(*)+2)/2) as "end"
from grade group by job order by job ;
解法2 :
case when :
select job,
case when count(score)%2=0 then floor(count(score)/2)
else floor( count(score)/2 +1) end as "start",
case when count(score)%2 =0 then floor(count(score)/2)+1 else floor(count(score)/2)+1
end as "end"
from grade group by job order by job ;
SQL76 考试分数(五)
字符串截取功能
SQL30 统计每种性别的人数
CREATE TABLE `user_submit` (
`id` int NOT NULL,
`device_id` int NOT NULL,
`profile` varchar(100) NOT NULL,
`blog_url` varchar(100) NOT NULL
);
INSERT INTO user_submit VALUES(1,2138,'180cm,75kg,27,male','http:/url/bisdgboy777');
INSERT INTO user_submit VALUES(1,3214,'165cm,45kg,26,female','http:/url/dkittycc');
INSERT INTO user_submit VALUES(1,6543,'178cm,65kg,25,male','http:/url/tigaer');
INSERT INTO user_submit VALUES(1,4321,'171cm,55kg,23,female','http:/url/uhsksd');
INSERT INTO user_submit VALUES(1,2131,'168cm,45kg,22,female','http:/url/sysdney');
select * from user_submit
select case PROFILE when like '%male' then 1 when like '%fmale' then 0 else -1 end from user_submit ;
select SUBSTRING_INDEX(PROFILE,',',-1) as gender ,count(*) from user_submit group by gender ;
字符串截取:
https://www.cnblogs.com/zdz8207/p/3765073.html
1、LOCATE(substr , str ):返回子串 substr 在字符串 str 中第一次出现的位置,如果字符substr在字符串str中不存在,则返回0;
2、POSITION(substr IN str ):返回子串 substr 在字符串 str 中第一次出现的位置,如果字符substr在字符串str中不存在,与LOCATE函数作用相同;
3、LEFT(str, length):从左边开始截取str,length是截取的长度;
4、RIGHT(str, length):从右边开始截取str,length是截取的长度;
5、SUBSTRING_INDEX(str ,substr ,n):返回字符substr在str中第n次出现位置之前的字符串;
6、SUBSTRING(str ,n ,m):返回字符串str从第n个字符截取到第m个字符;
7、REPLACE(str, n, m):将字符串str中的n字符替换成m字符;
8、LENGTH(str):计算字符串str的长度。
SQL31 提取博客URL中的用户名
select device_id ,SUBSTRING_INDEX(blog_url,'/',-1) as user_name from user_submit ;
2138 bisdgboy777
3214 dkittycc
6543 tigaer
4321 uhsksd
2131 sysdney
SQL67 牛客每个人最近的登录日期(二)
drop table if exists login;
drop table if exists user;
drop table if exists client;
CREATE TABLE `login` (
`id` int(4) NOT NULL,
`user_id` int(4) NOT NULL,
`client_id` int(4) NOT NULL,
`date` date NOT NULL,
PRIMARY KEY (`id`));
CREATE TABLE `user` (
`id` int(4) NOT NULL,
`name` varchar(32) NOT NULL,
PRIMARY KEY (`id`));
CREATE TABLE `client` (
`id` int(4) NOT NULL,
`name` varchar(32) NOT NULL,
PRIMARY KEY (`id`));
INSERT INTO login VALUES
(1,2,1,'2020-10-12'),
(2,3,2,'2020-10-12'),
(3,1,3,'2020-10-12'),
(4,2,2,'2020-10-13'),
(5,3,2,'2020-10-13');
INSERT INTO user VALUES
(1,'tm'),
(2,'fh'),
(3,'wangchao');
INSERT INTO client VALUES
(1,'pc'),
(2,'ios'),
(3,'anroid'),
(4,'h5');
牛客每个人最近的登录日期(二)题解
发表于 2020-08-17 17:56:21
先根据用户分组,查出每个用户登录的最新日期(一):
1
select user_id,max(date) from login group by login.user_id ;
然后查找出所有用户的名字,所有的登录设备,所有的登录日期(二):
1
2
3
4
5
select user.name as u_n, client.name as c_n,
login.date
from login
join user on login.user_id=user.id
join client on login.client_id=client.id
查找的结果应该是:
那么再根据用户id和最新的登录日期(一),可以在所有的数据(二)里面,从而确定唯一一组数据,最后再按照名字排序(三):
复制代码
select user.name as u_n, client.name as c_n,
login.date
from login
join user on login.user_id=user.id
join client on login.client_id=client.id
where (login.user_id,login.date) in
(select user_id,max(date) from login group by login.user_id )
order by user.name;
然后查找出所有用户的名字,所有的登录设备,所有的登录日期(二):
select user.name as u_n, client.name as c_n,
login.date
from login
join user on login.user_id=user.id
join client on login.client_id=client.id
查找的结果应该是:
那么再根据用户id和最新的登录日期(一),可以在所有的数据(二)里面,从而确定唯一一组数据,最后再按照名字排序(三):
复制代码
select user.name as u_n, client.name as c_n,
login.date
from login
join user on login.user_id=user.id
join client on login.client_id=client.id
where (login.user_id,login.date) in
(select user_id,max(date) from login group by login.user_id )
order by user.name;
SQL18 分组计算练习题
每个学校每种性别用户数,30天内平均活跃天数和平均发帖数量
问题分解:
按照学校性别分组:group by gender, university
用户数:count(device_id)
30天内活跃数avg(active_days_within_30)
平均发帖数量: avg(question_cnt)
select gender ,university
count(device_id) as user_num ,
avg(activity_days_within_30)
as avg_activity_days,
avg(question_cnt) as avg_question_cnt
from user_profile group by ,university ;
SQL19 分组过滤练习题
现在运营想查看每个学校用户的平均发贴和回帖情况,寻找低活跃度学校进行重点运营,请取出平均发贴数低于5的学校或平均回帖数小于20的学校。
https://www.nowcoder.com/practice/ddbcedcd9600403296038ee44a172f2d?tpId=199&tqId=1975670&ru=%2Fexam%2Foj&qru=%2Fta%2Fsql-quick-study%2Fquestion-ranking&sourceUrl=%2Fexam%2Foj
题解:
https://www.nowcoder.com/practice/ddbcedcd9600403296038ee44a172f2d?tpId=199&tqId=1975670&ru=%2Fexam%2Foj&qru=%2Fta%2Fsql-quick-study%2Fquestion-ranking&sourceUrl=%2Fexam%2Foj
限定条件: 平均发帖数低于5 或者平局回帖数低于20 avg(question_cnt)<5 or
avg(answer_cnt) <20 ,聚合函数结果作为筛选条件时,不能用where ,而是having 语法
按照学校输出 : 对每个学校统计其平局回帖数,group by university
-- 平均发帖数小question_cnt < 5
-- 平均回帖数answer_cnt < 20
select university ,
avg(question_cnt) as avg_question_cnt
,avg(answer_cnt) as
avg_answer_cnt
from user_profile group by university
having avg_question_cnt< 5 or avg_answer_cnt <20
;
统计每个学校的答过题的用户的平均答题数
问题分解:
每个学校: 按照学校分组 group by university
平均答题数量: 每个学校的分组内,用总答题数量除以总人数即可得到平均答题数量
count(question_id )/count(distinct device_id)
表链接: 学校和答题信息不在同一个表
select u.university , count(q.question_id)/count(DISTINCT q.device_id) from question_practice_detail q inner join
user_profile u where q.device_id =u.device_id group by u.university
SQL25 查找山东大学或者性别为男生的信息
注意输出的顺序,先输出学校为山东大学再输出性别为男生的信息)
SELECT device_id ,gender,age ,gpa from user_profile
where university ='山东大学'
UNION all
SELECT device_id ,gender,age ,gpa from user_profile where
gender ='male' ;
题解 | #计算25岁以上和以下的用户数量
select '25岁以下' as age_cut , count(device_id) as number from user_profile
where age <25 OR age is null
union all
select '25岁及以上' as age_cut ,count(device_id) as number from user_profile
where age >=25 ;
SQL27 查看不同年龄段的用户明细
问题分解:
- 限定条件:无;
- 划分年龄段:数值条件判断,可以用多重if,不过更方便的是用
case when [expr] then [result1]...else [default] end
正确:
select device_id ,gender , case when
age <20 then '20岁以下'
when age >= 20 and age <=24 then '20-24岁'
when age >=25 then '25岁及以上'
else '其他' end as age_cut
from user_profile ;
2138|male|20-24岁
3214|male|其他
6543|female|20-24岁
2315|female|20-24岁
5432|male|25岁及以上
2131|male|25岁及以上
4321|male|25岁及以上
错误; case age when
select device_id ,gender , case age when
age <20 then '20岁以下'
when age >= 20 and age <=24 then '20-24岁'
when age >=25 then '25岁及以上'
else '其他' end as age_cut
from user_profile ;
2138|male|其他
3214|male|其他
6543|female|其他
2315|female|其他
5432|male|其他
日期函数
SQL28 计算用户8月每天的练题数量
分析:
2021 年8 月
用 year/month 函数的
year(date)=2021 and month(date) =8 比如用date_format 函数的 date_format(date,”%Y-%m”)=”202108” 每天: 按照天分组 group by date 题目数量 : count(question_id)
select day(date) as day , count(question_id) as question_cnt from question_practice_detail where month (date)=8 and year(date) =2021 group by date
SQL29 计算用户的平均次日留存率
将一张表的内容插入到另外一张表:
某打车公司要将驾驶里程(drivedistanced)超过5000里的司机信息转存到一张称为seniordrivers 的表中,他们的详细情况被记录在表drivers 中,正确的sql语句为()
https://www.nowcoder.com/test/question/done?tid=51750013&qid=326265#summary
- insert into 目标表(字段1,字段2, ,…) select 字段1,字段2 ,..
from 来源表
- sql insert into test ( …) select ( …) from mcc
SQL把一个表中数据更新到另一个表的多种方法
SQL39 21年8月份练题总数
现在运营想要了解2021年8月份所有练习过题目的总用户数和练习过题目的总次数,请取出相应结果
SELECT count( distinct device_id) as did_cnt, count(question_id) as
question_cnt from question_practice_detail
where MONTH(date) =8 and YEAR(date)=2021 ;
插入数据
replace into 插入重复数据
replace into examination_info(exam_id,tag,difficulty,duration ,release_time )
values(9003,'SQL','hard',90,'2021-01-01 00:00:00');
等效 :
DELETE FROM examination_info
WHERE exam_id=9003;
INSERT INTO examination_info
VALUES(NULL,9003, 'SQL','hard', 90, '2021-01-01 00:00:00')
时间差TIMESTAMPDIFF
https://www.nowcoder.com/practice/d331359c5ca04a3b87f06b97da42159c?tpId=240&tags=&title=&difficulty=0&judgeStatus=0&rp=0
明确考点:
删除记录的方式汇总:
- 根据条件删除:DELETE FROM tb_name [WHERE options] [ [ ORDER BY fields ] LIMIT n ]
- 全部删除(表清空,包含自增计数器重置):TRUNCATE tb_name
时间差:
TIMESTAMPDIFF(interval, time_start, time_end)可计算time_start-time_end的时间差,单位以指定的interval为准,常用可选:
- SECOND 秒
- MINUTE 分钟(返回秒数差除以60的整数部分)
- HOUR 小时(返回秒数差除以3600的整数部分)
- DAY 天数(返回秒数差除以3600*24的整数部分)
- MONTH 月数
- YEAR 年数
delete from exam_record where TIMESTAMPDIFF(MINUTE,start_time,submit_time)<5 and score <60 ;
删除最早的三条:
delete from exam_record where
TIMESTAMPDIFF(MINUTE,start_time,submit_time)<5 or submit_time is
null order by start_time limit 3 ;
删除表内容并且重置自增主键
方法1 : truncate table exam_record
方法2:
创建一张新表
create table user_info_vip (
id int primary key auto_increment not null comment '自增ID' ,
uid int unique not null comment '用户ID' ,
nick_name varchar(64) comment '昵称',
achievement int default 0 comment '成就值',
level int comment '用户等级',
job varchar(32) comment '职业方向',
register_time datetime default CURRENT_TIMESTAMP comment '注册时间'
)CHARACTER SET utf8 COLLATE utf8_general_ci;
SQL 12 创建索引
http://www.nowcoder.com/practice/f2ea9ccf33c740d58576608940981807
解析:
https://www.nowcoder.com/practice/f2ea9ccf33c740d58576608940981807?tpId=240&tqId=2223570&ru=/exam/oj&qru=/ta/sql-advanced/question-ranking&sourceUrl=%2Fexam%2Foj%3Ftab%3DSQL%25E7%25AF%2587%26topicId%3D240
创建唯一索引
create 方式创建
create unique -- 唯一索引
fulltext -- 全文索引
index index_name on table_name -- 不指定唯一或者
全文默认是普通索引
(column1[(length) [DESC|ASC]] [,column2,...]) -- 可以对多列建立组合索引
alter 方式创建索引: alter table tb_name add [unique | fulltext] [ index ]
index_content(content)
drop 方式删除索引 drop index <索引名> on <表名>
alter 方式删除索引: alter table < 表名> drop index <索引名>
索引的使用:
索引使用的时候满足最左匹配原则,即对组合索引(col1,col2),在不考虑引擎优化时,条件必须是col1 在前面col2 在后,或者只使用col1 ,索引才会生效。
索引不包含有null 值得列
create index idx_duration on examination_info(duration);
create unique index uniq_idx_exam_id on examination_info(exam_id);
create fulltext index full_idx_tag on examination_info(tag);
删除索引:
drop index uniq_idx_exam_id on examination_info;
drop index full_idx_tag on examination_info;
SQL14 SQL类别高难度试卷得分的截断平均值
SELECT rt.tag,rt.difficulty, ROUND((sum(score) -max(score) -min(score))/ (count(score)-2),1)
as clip_avg_score from exam_record er join examination_info rt USING(exam_id)
where rt.tag='SQL' and rt.difficulty='hard';
1 using()用于两张表的join查询,要求using()指定的列在两个表中均存在,并使用之用于join的条件。 示例: select a.*, b.* from a left join b using(colA); 等同于: select a.*, b.* from a left join b on a.colA = b.colA; 2 多表查询就使用多个这样的j
1 using()用于两张表的join查询,,要求using()指定的列在两个表中均存在,并使用之用于join的条件。 示例: select a.*, b.* from a left join b using(colA); 等同于: select a.*, b.* from a left join b on a.colA = b.colA;
2 多表查询就使用多个这样的join。 示例: select a.*, b.* from a left join b using(colA) left join c using(colB); 要求colA在a, b中均存在,colB在a, c表中均存在。
总之,using() 只是join中指定连接条件的简写,在简单的连接中常用。在列名称不同时或连接条件复杂时就无法用了,使用 a left join b on ... 是更常见的做法。
————————————————
版权声明:本文为CSDN博主「镰刀刀」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_42356552/article/details/113170473
603. 连续空余座位
# Write your MySQL query statement below
解题思路
此处撰写解题思路
cinema 表内连接 第一种情况:t2.seat_id= t1.seat_id+1 t1(3) --t2(4)
cinema 表内连接 第一种情况:t2.seat_id-1= t1.seat_id t1(4) --t2(5)
3 union 去重
代码
# Write your MySQL query statement below
select * from (
select t1.seat_id from cinema t1 ,cinema t2 where t1.free =1
and t2.seat_id= t1.seat_id+1 and t2.free=1
union
select t2.seat_id from cinema t1 ,cinema t2 where t1.free =1
and t2.seat_id-1= t1.seat_id and t2.free=1 ) a order by seat_id;
作者:zengjx
链接:https://leetcode-cn.com/problems/consecutive-available-seats/solution/union-by-zengjx-caol/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
select distinct a.seat_id from cinema a ,cinema b
where abs(a.seat_id-b.seat_id) =1 and a.free=true and b.free=true order by a.seat_id ;
511. 游戏玩法分析 I
活动表 Activity:
+———————+————-+
| Column Name | Type |
+———————+————-+
| player_id | int |
| device_id | int |
| event_date | date |
| games_played | int |
+———————+————-+
表的主键是 (player_id, event_date)。
这张表展示了一些游戏玩家在游戏平台上的行为活动。
每行数据记录了一名玩家在退出平台之前,当天使用同一台设备登录平台后打开的游戏的数目(可能是 0 个)。
写一条 SQL 查询语句获取每位玩家 第一次登陆平台的日期。
查询结果的格式如下所示:
Activity 表:
+—————-+—————-+——————+———————+
| player_id | device_id | event_date | games_played |
+—————-+—————-+——————+———————+
| 1 | 2 | 2016-03-01 | 5 |
| 1 | 2 | 2016-05-02 | 6 |
| 2 | 3 | 2017-06-25 | 1 |
| 3 | 1 | 2016-03-02 | 0 |
| 3 | 4 | 2018-07-03 | 5 |
+—————-+—————-+——————+———————+
Result 表:
+—————-+——————-+
| player_id | first_login |
+—————-+——————-+
| 1 | 2016-03-01 |
| 2 | 2017-06-25 |
| 3 | 2016-03-02 |
+—————-+——————-+
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/game-play-analysis-i
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
# 按照palayer_id 分组 ,order by event_date 取最少值
select player_id ,min(event_date) as first_login from activity
where event_date group by player_id ;
over partiotion
select player_id, event_date as first_login from (
select player_id ,
event_date,
dense_rank() over(partition by player_id order by event_date) as 排名
from activity) as temp where 排名=1 ;
512. 游戏玩法分析 II
able: Activity
+———————+————-+
| Column Name | Type |
+———————+————-+
| player_id | int |
| device_id | int |
| event_date | date |
| games_played | int |
+———————+————-+
(player_id, event_date) 是这个表的两个主键
这个表显示的是某些游戏玩家的游戏活动情况
每一行是在某天使用某个设备登出之前登录并玩多个游戏(可能为0)的玩家的记录
请编写一个 SQL 查询,描述每一个玩家首次登陆的设备名称
查询结果格式在以下示例中:
Activity table:
+—————-+—————-+——————+———————+
| player_id | device_id | event_date | games_played |
+—————-+—————-+——————+———————+
| 1 | 2 | 2016-03-01 | 5 |
| 1 | 2 | 2016-05-02 | 6 |
| 2 | 3 | 2017-06-25 | 1 |
| 3 | 1 | 2016-03-02 | 0 |
| 3 | 4 | 2018-07-03 | 5 |
+—————-+—————-+——————+———————+
Result table:
+—————-+—————-+
| player_id | device_id |
+—————-+—————-+
| 1 | 2 |
| 2 | 3 |
| 3 | 1 |
+—————-+—————-+
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/game-play-analysis-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
SQL:方法一
select activity.player_id, activity.device_id from activity, (
select player_id, min(event_date) as first_login
from activity group by player_id
) as temp
where activity.player_id = temp.player_id
and activity.event_date = temp.first_login;
解析
先是用子查询查出每个 player_id 最小的 event_date 命名为 temp 临时表
activity 和 temp 连查,筛选出 player_id 相等,并且 activity.event_date = temp.first_login 相等的数据
SQL:方法二
select player_id, device_id from (
select
player_id,
device_id,
dense_rank() over(partition by player_id order by event_date) as 排名
from activity
) as temp where temp.排名 = 1;
解析
这个方法和 游戏玩法分析 I 中的方法二一样。
SQL:方法三
select player_id, device_id from activity a1 where event_date <= all(
select a2.event_date from activity a2 where a1.player_id = a2.player_id
);
解析
使用 all 配合 <= 筛选出 a1.player_id = a2.player_id 的 event_date 数据
Tips
all 方法使用
作者:uccs
链接:https://leetcode-cn.com/problems/game-play-analysis-ii/solution/san-chong-fang-fa-jie-ti-by-uccs-ht2k/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
any、all关键字必须与一个比较操作符一起使用。any关键词可以理解为“对于子查询返回的列中的任一数值,如果比较结果为true,则返回true”。all的意思是“对于子查询返回的列中的所有值,如果比较结果为true,则返回true”
any 可以与=、>、>=、<、<=、<>结合起来使用,分别表示等于、大于、大于等于、小于、小于等于、不等于其中的任何一个数据。
all可以与=、>、>=、<、<=、<>结合是来使用,分别表示等于、大于、大于等于、小于、小于等于、不等于其中的其中的所有数据。
举个例子
select s1 from t1 where s1 > any (select s1 from t2);
假设any后面的s1返回了三个值,那其实就等价于
select s1 from t1 where s1 > result1 or s1 > result2 or s2 > result3
而all的用法相当于把上述语句的‘or’缓冲‘and’
说道这你可能已经知道了any和or常用于两表的对比,当你不知道where后面具体值是可以用any,all帮你判定。
534. 游戏玩法分析 III
Table: Activity
+———————+————-+
| Column Name | Type |
+———————+————-+
| player_id | int |
| device_id | int |
| event_date | date |
| games_played | int |
+———————+————-+
(player_id,event_date)是此表的主键。
这张表显示了某些游戏的玩家的活动情况。
每一行是一个玩家的记录,他在某一天使用某个设备注销之前登录并玩了很多游戏(可能是 0 )。
编写一个 SQL 查询,同时报告每组玩家和日期,以及玩家到目前为止玩了多少游戏。也就是说,在此日期之前玩家所玩的游戏总数。详细情况请查看示例。
查询结果格式如下所示:
Activity table:
+—————-+—————-+——————+———————+
| player_id | device_id | event_date | games_played |
+—————-+—————-+——————+———————+
| 1 | 2 | 2016-03-01 | 5 |
| 1 | 2 | 2016-05-02 | 6 |
| 1 | 3 | 2017-06-25 | 1 |
| 3 | 1 | 2016-03-02 | 0 |
| 3 | 4 | 2018-07-03 | 5 |
+—————-+—————-+——————+———————+
Result table:
+—————-+——————+——————————-+
| player_id | event_date | games_played_so_far |
+—————-+——————+——————————-+
| 1 | 2016-03-01 | 5 |
| 1 | 2016-05-02 | 11 |
| 1 | 2017-06-25 | 12 |
| 3 | 2016-03-02 | 0 |
| 3 | 2018-07-03 | 5 |
+—————-+——————+——————————-+
对于 ID 为 1 的玩家,2016-05-02 共玩了 5+6=11 个游戏,2017-06-25 共玩了 5+6+1=12 个游戏。
对于 ID 为 3 的玩家,2018-07-03 共玩了 0+5=5 个游戏。
请注意,对于每个玩家,我们只关心玩家的登录日期。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/game-play-analysis-iii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题思路
该题目的解答重点是知道如何进行内联,连接表的结构是什么样子的,然后再进行分组查询
内联:连接条件a.player_id=b.player_id and a.event_date<=b.event_date,以实例为例,执行该连接后的表为:
然后在按照b表player_id分组,再按照event_date分组
最后注意的是查询条件为b.event_date,sum(a.games_played) as games_played_so_far,至于player_id,用表a或表b的都可以
select a.player_id,b.event_date,sum(a.games_played) as games_played_so_far from activity a inner join activity b on a.player_id =b.player_id and a.event_date <=b.event_date
group by b.player_id ,b.event_date;
;
? 解法2:在 sum
函数后面可以使用 over
对其按照 player_id
分组,并按照 event_date
排序。
select
player_id,
event_date,
sum(games_played) over (partition by player_id order by event_date) as games_played_so_far from activity;
{*}SQL:方法一
select player_id, event_date,
sum(
case
when @pre_player_id = player_id then @n:=@n+games_played
when @pre_player_id:= player_id then @n:=games_played
end
) as games_played_so_far
from (
select * from activity order by player_id, event_date
) temp, (select @pre_player_id:=null, @n:=0) init
group by player_id, event_date;
解析
因为最终的结果是计算每个用户在某天玩游戏的次数,所以需要按照 player_id 和 event_date 分组。
因为 order by 执行的顺序在 sum 函数后面执行,所以这里需要先对 player_id 和 event_date 先进行排序。
具体实现:
将 activity 按照 player_id 和 event_date 升序排序,命名为 temp 临时表
将临时表 temp 按照 player_id 和 event_date 进行分组
使用 case … when … then …end 语句对分组后的 temp 中 games_played 进行输出,并用 sum 求和
作者:uccs
链接:https://leetcode-cn.com/problems/game-play-analysis-iii/solution/san-chong-fang-fa-jiang-jie-by-uccs-vanc/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
550. 游戏玩法分析 IV
难度中等65
SQL架构
Table: Activity
+--------------+---------+
| Column Name | Type |
+--------------+---------+
| player_id | int |
| device_id | int |
| event_date | date |
| games_played | int |
+--------------+---------+
(player_id,event_date)是此表的主键。
这张表显示了某些游戏的玩家的活动情况。
每一行是一个玩家的记录,他在某一天使用某个设备注销之前登录并玩了很多游戏(可能是 0)。
编写一个 SQL 查询,报告在首次登录的第二天再次登录的玩家的比率,四舍五入到小数点后两位。换句话说,您需要计算从首次登录日期开始至少连续两天登录的玩家的数量,然后除以玩家总数。
查询结果格式如下所示:
Activity table:
+-----------+-----------+------------+--------------+
| player_id | device_id | event_date | games_played |
+-----------+-----------+------------+--------------+
| 1 | 2 | 2016-03-01 | 5 |
| 1 | 2 | 2016-03-02 | 6 |
| 2 | 3 | 2017-06-25 | 1 |
| 3 | 1 | 2016-03-02 | 0 |
| 3 | 4 | 2018-07-03 | 5 |
+-----------+-----------+------------+--------------+
Result table:
+-----------+
| fraction |
+-----------+
| 0.33 |
+-----------+
只有 ID 为 1 的玩家在第一天登录后才重新登录,所以答案是 1/3 = 0.33
首次登录第二天再次登录:
577. 员工奖金
难度简单26
SQL架构
选出所有 bonus < 1000 的员工的 name 及其 bonus。
Employee
表单
+-------+--------+-----------+--------+
| empId | name | supervisor| salary |
+-------+--------+-----------+--------+
| 1 | John | 3 | 1000 |
| 2 | Dan | 3 | 2000 |
| 3 | Brad | null | 4000 |
| 4 | Thomas | 3 | 4000 |
+-------+--------+-----------+--------+
empId 是这张表单的主关键字
Bonus
表单
+-------+-------+
| empId | bonus |
+-------+-------+
| 2 | 500 |
| 4 | 2000 |
+-------+-------+
empId 是这张表单的主关键字
输出示例:
+-------+-------+
| name | bonus |
+-------+-------+
| John | null |
| Dan | 500 |
| Brad | null |
+-------+-------+
算法
首先需要知道每个员工的奖金数量,因此需要首先将 Employee 表与 Bonus 表连接。注意需要使用外连接,以处理员工没有出现在 Bonus 表上的情况。这里因为不存在员工只出现在 Bonus 表中的情况,所以只需要使用左外连接(left join 或 left outer join)。
MySQL
select name, bonus
from Employee left join Bonus
on Employee.EmpId = Bonus.EmpId
对于题目中的样例,上面的代码运行可以得到如下输出:
name | bonus |
---|---|
Dan | 500 |
Thomas | 2000 |
Brad | |
John |
其中 Brad 和 John 的 bonus 值为空,空值在数据库中的表示为 null。我们使用 bonus is null(而不是 bonus = null)判断奖金是否为 null。随后即可用 where 子句筛选奖金小于 1000 或者为空的员工。
MySQL
select name, bonus
from Employee left join Bonus
on Employee.EmpId = Bonus.EmpId
where bonus is null or bonus < 1000
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/employee-bonus/solution/yuan-gong-jiang-jin-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
607. 销售员
表: SalesPerson
+————————-+————-+
| Column Name | Type |
+————————-+————-+
| sales_id | int |
| name | varchar |
| salary | int |
| commission_rate | int |
| hire_date | date |
+————————-+————-+
Sales_id是该表的主键列。
该表的每一行都显示了销售人员的姓名和ID,以及他们的工资、佣金率和雇佣日期。
表: Company
+——————-+————-+
| Column Name | Type |
+——————-+————-+
| com_id | int |
| name | varchar |
| city | varchar |
+——————-+————-+
Com_id是该表的主键列。
该表的每一行都表示公司的名称和ID,以及公司所在的城市。
表: Orders
+——————-+———+
| Column Name | Type |
+——————-+———+
| order_id | int |
| order_date | date |
| com_id | int |
| sales_id | int |
| amount | int |
+——————-+———+
Order_id是该表的主键列。
com_id是Company表中com_id的外键。
sales_id是来自销售员表com_id的外键。
该表的每一行包含一个订单的信息。这包括公司的ID、销售人员的ID、订单日期和支付的金额。
编写一个SQL查询,报告没有任何与名为 “RED” 的公司相关的订单的所有销售人员的姓名。
以 任意顺序 返回结果表。
查询结果格式如下所示。
示例 1:
输入:
SalesPerson 表:
+—————+———+————+————————-+——————+
| sales_id | name | salary | commission_rate | hire_date |
+—————+———+————+————————-+——————+
| 1 | John | 100000 | 6 | 4/1/2006 |
| 2 | Amy | 12000 | 5 | 5/1/2010 |
| 3 | Mark | 65000 | 12 | 12/25/2008 |
| 4 | Pam | 25000 | 25 | 1/1/2005 |
| 5 | Alex | 5000 | 10 | 2/3/2007 |
+—————+———+————+————————-+——————+
Company 表:
+————+————+—————+
| com_id | name | city |
+————+————+—————+
| 1 | RED | Boston |
| 2 | ORANGE | New York |
| 3 | YELLOW | Boston |
| 4 | GREEN | Austin |
+————+————+—————+
Orders 表:
+—————+——————+————+—————+————+
| order_id | order_date | com_id | sales_id | amount |
+—————+——————+————+—————+————+
| 1 | 1/1/2014 | 3 | 4 | 10000 |
| 2 | 2/1/2014 | 4 | 5 | 5000 |
| 3 | 3/1/2014 | 1 | 1 | 50000 |
| 4 | 4/1/2014 | 1 | 4 | 25000 |
+—————+——————+————+—————+————+
输出:
+———+
| name |
+———+
| Amy |
| Mark |
| Alex |
+———+
解释:
根据表 orders 中的订单 ‘3’ 和 ‘4’ ,容易看出只有 ‘John’ 和 ‘Pam’ 两个销售员曾经向公司 ‘RED’ 销售过。
所以我们需要输出表 salesperson 中所有其他人的名字。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sales-person
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解法: 先找出 所有red 关联的员工。在salsperson 找出不在其中的。
select o.sales_id from company c ,orders o where o.com_id =c.com_id and c.name='red'
select s.name from salesperson s where s.sales_id not in
(
select o.sales_id from company c ,orders o where o.com_id =c.com_id and c.name='red'
) ;
解法2:
select s.name from salesperson s
left join
orders o on s.sales_id =o.sales_id
left join company c on o.com_id =c.com_id
group by s.name HAVING
SUM(IF(C.name = 'RED', 1, 0)) = 0 order by s.sales_id ;
1045. 买下所有产品的客户
难度中等30
SQL架构
Customer
表:
+-------------+---------+
| Column Name | Type |
+-------------+---------+
| customer_id | int |
| product_key | int |
+-------------+---------+
product_key 是 Customer 表的外键。
Product
表:
+-------------+---------+
| Column Name | Type |
+-------------+---------+
| product_key | int |
+-------------+---------+
product_key 是这张表的主键。
写一条 SQL 查询语句,从 Customer
表中查询购买了 Product
表中所有产品的客户的 id。
示例:
Customer 表:
+-------------+-------------+
| customer_id | product_key |
+-------------+-------------+
| 1 | 5 |
| 2 | 6 |
| 3 | 5 |
| 3 | 6 |
| 1 | 6 |
+-------------+-------------+
Product 表:
+-------------+
| product_key |
+-------------+
| 5 |
| 6 |
+-------------+
Result 表:
+-------------+
| customer_id |
+-------------+
| 1 |
| 3 |
+-------------+
购买了所有产品(5 和 6)的客户的 id 是 1 和 3 。
解法:因为已经是外键 ,不需要考虑在customer 而不在product表, 按照 customer_id 分组之后统计count(product_key) 个数与product的count(product_key) 相同
执行用时:616 ms, 在所有 MySQL 提交中击败了12.81%
-- 不同产品个数
-- select count(product_key) from product ;
select c.customer_id
from customer c
group by customer_id having count(distinct product_key) in ( select count( distinct(product_key)) from product);
在所有 MySQL 提交中击败了25.11%
在所有 MySQL 提交中击败了100.00%
select c.customer_id
from customer c
group by customer_id having count(distinct product_key) = ( select count( distinct(product_key)) from product);
select count(product_key) from product ;
SELECT customer_id,number FROM (SELECT customer_id, COUNT(*) AS 'number'
FROM (SELECT customer_id, product_key FROM
Customer GROUP BY customer_id, product_key) a
GROUP BY customer_id) b WHERE number =
(SELECT COUNT(DISTINCT product_key) FROM Product);