10 力扣练习题
文章浏览I

查询所有浏览过自己文章的作者。结果按照作者的 id 升序排列。

注意:
1. 去重用distinct或者group by
2. 升序 从小到大是 asc; 降序 从大到小是 desc
员工奖金

select * from (select name, bonus from Employee a left join Bonus b on a.empId=b.empId) c where c.bonus is null or c.bonus<1000;
注意:这里一定是is null 而不是 bonus=null 如果这样写等价于false

按分类统计薪水【2次】

我的原始版本,这里面最严重的错误就是插入表格写错。这里只有一列,但我这样写插入的应该是三列的数值。

注意,这里的Create不是用{} 而是要用()。并且最后要有分号结尾,表格字段的最后一行不要有逗号,其余的行的列名都要有逗号作为结尾。

并且力扣是只能够写一条查询语句的,所以我学习到了select union all这个语法,可以构建一个临时的表。

注意这个UNION ALL 后面直接接你要加的列值就行,如果是多列也不需要括号括起来:

这里我用这个语句代替了我的表构建,但是还是不对的,如果按照我原先的顺序:

首先第一个错误,这里a和b都有category所以应该说明展示的是谁的category列,否则select不知道去查谁的。
此外,如果Accounts是我的左表,我其实这里是没有Average Salary的,那么连接之后就只会展示出仅有的两个类别:

所以我将临时表放在左边进行连接,但这里还是错了。

结果还是不对的,我如果使用的是count(*):

所以其实我们应该有正确的写法:


有更加精简的答案:

查询近30天活跃用户数


这个是我的最开始的SQL语句,这里为了去重注意要使用distinct。日期的范围注意使用between and来限制范围并且要使用''单引号框起来,并且日期是两位,要用07这样的两位填充。
WHERE 必须在 GROUP BY 之前

包含27的三十天要往前29天这个细节也没有注意到。



买下所有产品的客户

我最开始是这样写的,因为我不知道怎么看是否购买了所有的产品,所以我只能够看数量是否等于所有的产品数量。
但后来我发现这样有的同一个客户买同一个产品有重复的,所以还需要对客户表去重,但我不会整行去重,学习一下:
![]()

但是我发现题解的一个更加简单的方法,我现在其实可以在分组后直接在having中去重,因为此时已经分组了,那么只需要对表中的产品列去重就好:

但是这个其实有点投机取巧,还有更加完美的办法:

2016年的投资

我的写法:

注意:这里必须要用concat中间加一个连接符,否则lat=1,lon=23;lat=12,lon=3。这种情况会被排除出去。
看到一个更加简单的窗口函数实现的:

这个with t as 可以创建一个临时表格

上升的温度

一开始我是使用的left join 把表跟前面一行表格连接。后来发现要按照日期排序然后再连接,又要重新对排序后的赋予新的ID,这样比较复杂:
select a.id Id from (select *,rank() ID from Weather order by recordDate) a left join (select *,rank() ID from Weather order by recordDate) b on a.ID-1=b.ID where a.Temperature > b.Temperature;
这里的rank()还写错了,rank()必须要搭配窗口使用
rank() over(group by xxx order by xxx)

但是这样还有一个问题:这里的题目的温度只能跟前一天的温度来比较,所以还要加一个判断
我首先就想到的 a.recordDate-b.recordDate=1但是这个不对31号使用。只能 DateDiff函数来计算日期差值:

但其实也可以直接用 Lag窗口函数获取排序后的前一行数据:
# Write your MySQL query statement below select temp.id Id from (select *,LAG(Temperature) over (order by recordDate) pre_Temperature, LAG(recordDate) over (order by recordDate) pre_recordDate from Weather ) temp
where temp.Temperature > temp.pre_Temperature and datediff(temp.recordDate,temp.pre_recordDate)=1;
即时食物配送II

我首先写的是:

但这里有个问题:
我们要区分一下having 和 where的区别:


然后我就改为了这一版:

这里又有问题了,为什么一定要group by customer_id,这和前面的count(temp.is_time)/count(*)冲突,因为这个我是想要计算得到一个值,而和group by在一起就会得到每个分组都有一个值。
这里其实是不需要按照每个客户端进行分组,因为每个人只会有一个首单,最后最终结果我修改为下面这种:
select round(count(temp.is_time)/count(*)*100,2) immediate_percentage from
(select *, if(order_date=customer_pref_delivery_date,'yes',null) is_time,
first_value(order_date) over(partition by customer_id order by order_date) first_orderdate from Delivery) temp
where temp.first_orderdate=temp.order_date;
电影评分

我最开始写的是这样:

但是union 不能直接连接两个子查询,外面必须包一层

并且我开始不知道order by里面可以写多个不同的排序方法,我就分开写,这样其实反而不好,会将里层排序好的又打乱重排。
并且注意还是老问题,select里面的语句要在group by中出现,最终答案:

第二高的薪水

这个是我最开始写的:

很严重一个问题就是在rank()里面不能写参数的。

然后就改成这一版,但是没有满足没查询到返回null。
然后我想到了ifnull函数,改为了

但是这个也是有问题的,ifnull是存在这一行如果为null值则会赋值:

但是这里是没有第二高的这个行返回null,这一行是不存在的,所以没有函数可以处理这种情况,只能外面套一个select。


然后又发现一个问题,这里有的并列的两个排名都为2就会返回多行值,所以我又加了一个limit 1

然后又发现一个问题,rank()排名,可能不产生第二名的编号:




官方的解答:

这里的offset是跳过几行,这里跳过第一行就会取第二个值,但是为什么跳过第一行就一定是第二高呢?因为:distinct会在order by之前执行!!所以是会先去重,然后再排序,这样就不可能产生并列的结果。
员工的直属部门

注意这种写法:


注意:所以group by 不会返回所有的行 而是每一个类别随机返回一行,所以会丢失数据。
标准写法可以像我这个:我开始这里写的
or employee_id=(select em...) 但是这是不对的,因为这里的select返回的可能是多行一列的表,然后我就试了一下in原来可以用!
select employee_id,department_id from Employee where primary_flag='Y'or employee_id in (select employee_id from Employee group by employee_id having count(*)=1)
指定日期的产品价格

我最开始想到的就是用窗口函数,然后分区取每个产品的最后一个的修改日期然后对最后的日期判断。

而且我开始还是用的group by 这是很严重的语法错误:

所以要改一下分区的窗口

这里我学习到窗口函数为查询结果中的每一行都计算一个值,而不会减少结果集的行数。

这里我想用limit 1来取每个分区的第一行,然而limit不对分区作用,而是对整个区域。

我就想着我用分组查询来把小于这个时期的先全部按组查出来,但犯了大错误!

我这样写 每个类别只会给我返回一行,并不会按照我原有的想法返回。

按日期分组销售产品


介绍一下group_concat函数:






正确答案:这里我才知道couont里面也可以加distinct

学生们参加各科测试的次数




我最开始的写法:

这里如果使用的count(*)就会计算有多少行,如果这一行是null也会计数进入,所以我们如果只要计某个列非null值,就要改为这个列的列名
其次,因为里面的temp是排序了的,但是我们发现外面展示出来的结果又没有序了,是因为外面做了group by 打乱了顺序,所以order应该放在最外面。

餐馆营业额变化增长



这里首先要学一个窗口函数
取当前行和前五行:ROWS between 5 preceding and current row --共6行 取当前行和后五行:ROWS between current row and 5 following --共6行 取前五行和后五行:ROWS between 5 preceding and 5 following --共11行 取当前行和前六行:ROWS 6 preceding(等价于between...and current row) --共7行 这一天和前面6天:RANGE between interval 6 day preceding and current row --共7天 这一天和前面6天:RANGE interval 6 day preceding(等价于between...and current row) --共7天 字段值落在当前值-100到+200的区间:RANGE between 100 preceding and 200 following --共301个数值
![]()
这行语句会自动帮你计算,比如第十天有两个行都是,但是它会自动按照最大的一行计算最终总和

最终结果:

注意前面用distinct去重
每月交易I


我最开始这样写的:

但是count(表达式)也只会计算不是null的值,这个前面讲过了可以翻翻。所以approved_count并不会按照表达式来计算其实。
但其实可以这样搭配

注意这个函数是date_format 不是mate
至少有5名直接下属的经理

这是我写的语句 但是有一个问题就是在经理没有在表格中的时候会返回null而不是无返回


内连接inner join 只会返回匹配的行。

外连接 left/right join全部都返回。

正确的答案:

