mysql牛客网网刷题函数总结57-

SQL57 使用含有关键字exists查找未分配具体部门的员工的所有信息。

existsselect * from employees e
where not exists
(select emp_no from dept_emp d where d.emp_no = e.emp_no);
不用exists:
select * from employees e
left join dept_emp d on d.emp_no = e.emp_no
where d.emp_no is null;

SQL59 获取有奖金的员工相关信息。

方法)CASE WHEN 求出bonus
给出emp_no、first_name、last_name、奖金类型btype、对应的当前薪水情况salary以及奖金金额bonus
bonus类型btype为1其奖金为薪水salary的10%,btype为2其奖金为薪水的20%,其他类型均为薪水的30%
当前薪水表示to_date='9999-01-01'

SELECT e.emp_no, e.first_name, e.last_name, eb.btype, s.salary,
CASE WHEN eb.btype = 1 THEN s.salary * 0.1
WHEN eb.btype = 2 THEN s.salary * 0.2
ELSE s.salary * 0.3
END bonus
FROM emp_bonus AS eb, employees AS e, salaries AS s
WHERE eb.emp_no = e.emp_no
AND s.emp_no = e.emp_no
AND s.to_date='9999-01-01';

SQL 59) -CASE WHEN 给出员工及其奖金信息

(方法)CASE WHEN 求出bonus
给出emp_no、first_name、last_name、奖金类型btype、对应的当前薪水情况salary以及奖金金额bonus
bonus类型btype为1其奖金为薪水salary的10%,btype为2其奖金为薪水的20%,其他类型均为薪水的30%
当前薪水表示to_date=‘9999-01-01’

SELECT e.emp_no, e.first_name, e.last_name, eb.btype, s.salary,
CASE WHEN eb.btype = 1 THEN s.salary * 0.1
WHEN eb.btype = 2 THEN s.salary * 0.2
ELSE s.salary * 0.3
END bonus
FROM emp_bonus AS eb, employees AS e, salaries AS s
WHERE eb.emp_no = e.emp_no
AND s.emp_no = e.emp_no
AND s.to_date='9999-01-01';

SQL60 统计salary的累计和running_total

难点running_total为前N个当前( to_date = ‘9999-01-01’)员工的salary累计

select emp_no,salary, sum(salary) over(order by emp_no) as running_total 
from salaries 
where to_date = '9999-01-01'

sum(salary) over(order by emp_no) 能计算前N个员工的累计

其实这里主要的理解清楚 e1.first_name>=e2.first_name,同样都是字符串怎么比较呢?
比如 select ‘a’ > ‘b’ 。可能会看不出来谁大谁小,怎么比较的呢?其实字符串内部是通过转换成 ascii 编码来进行大小比较的。
如 我们 查询 select ascii(‘a’) , asc(‘b’) 会得到 65 与 66。所以 select ‘a’ > ‘b’ 返回 0 。
那么再回到原题 e1.first_name>=e2.first_name

直接说可能不会懂 放两段代码
第一段: select e2.first_name,( select count(*) from employees as e1 where e1.first_name <= e2.first_name ) as rownum
from employees as e2
第二段: select e1.first_name,ascii(e1.first_name),row_number() over(order by first_name)
from employees e1 ;(mysql8 已经内置了窗体函数。)

SQL61 对于employees表中,给出奇数行的first_name

select e1.first_name from employees e1 
where ( select count(*) from employees e2 WHERE
      e1.first.name >=e2first.name)%2=1

对于employees表中,输出first_name排名(按first_name升序排序)为奇数的first_name

row_number(): 同薪不同名,相当于行号,例如3000200020001000排名后为1234
rank(): 同薪同名,有跳级,例如3000200020001000排名后为1224
dense_rank(): 同薪同名,无跳级,例如3000200020001000排名后为1223
ntile(): 分桶排名,即首先按桶的个数分出第一二三桶,然后各桶内从1排名,实际不是很常用
partition by,按某字段切分
order by,与常规order by用法一致,也区分ASC(默认)DESC,因为排名总得有个依据

SQL62 出现三次以上相同积分的情况

方法一:要三次以上的积分,那么肯定要查找3个id不同但是积分相同的情况,怎么比较一列和另外一列是否相等呢?在一个表里面感觉无法做到,那么最简单就是利用笛卡尔积了,1个表看成3个表,联立三个表number相同的部分
笛卡尔积如下:

第1个表第2个表第3个表
idnumberidnumberidnumber
111111111111
233323332333
311131113111
411141114111
533353335333

那么可能就是举例一种情况就是寻找第1个表id为1的111,寻找第2个表id为3的111,寻找第3个表id4为的111,那么就找到一个111,输出111
但是上面这种找法可能会有重复的,比如第1个表id为3的111,寻找第2个表id为1的111,寻找第3个表id4为的111,那么又找到一个111,所以要去重。代码如下:

select distinct g1.number as times 
from 
	grade g1,
	grade g2,
	grade g3
where 
	g1.id != g2.id
	and g2.id != g3.id
	and g1.id != g3.id
	and g1.number = g2.number 
	and g2.number = g3.number ;

方法二:
使用group by 将积分分组,然后用having找到积分的个数大于等于3的:

select number from grade group by number having count(*)>=3

==count 专栏 ==

  • 比较count(*)和count(字段名)的区别:前者对行的数目进行计算,包含null,后者对特定的列的值具有的行数进行计算,不包含null,得到的结果将是除去值为null和重复数据后的结果。
  • count(1)跟count(主键)一样,只扫描主键
  • count(*)和count(主键)使用方式一样,但是在性能上有略微的区别,mysql对前者做了优化。
  • count(主键)不一定比count(其余索引快)。
  • count(字段),该字段非主键的情况最好不要出现,因为该方式不走索引。

QL63 刷题通过的题目排名

方法一:dense_rank()函数

row_number()对应唯一排序:1、2、3、4
dense_rank()对应相同次序可重复,但不跳过下一个次序值:1、2、2、3
rank()对应相同次序可重复,并且跳过下一个次序值:1、2、2、4

select id, number,
  dense_rank() over (order by number desc) t_rank
from passing_number

方法二:使用where子查询
注意:筛选条件需要使用distinct去重才能得到真正的dense_rank排名,如果没有去重,得到的是rank的排名。
select p1.id ,p1.number,
(select count(distinct p2.number) from passing_number as p2 where p2.number >= p1.number) as t_rank
from passing_number as p1
order by number desc,id asc

SQL64 找到每个人的任务

找到每个人的任务情况,并且输出出来,没有任务的也要输出,而且输出结果按照person的id升序排序。
没什么好说的,左右连接搞定。注意person.id对应task.person_id。

select p.id,p.name,t.content
from person p left join task t
    on p.id=t.person_id
order by p.id

SQL65 异常的邮件概率 难

要想到题目要求求的是 正常用户发送邮件失败的数目/正常用户发送邮件总数目,所以必然要连接user表排除掉黑名单用户,所以第一时间写出来的是

select xxx 
from email
join user as u1 on (email.send_id=u1.id and u1.is_blacklist=0)
join user as u2 on (email.receive_id=u2.id and u2.is_blacklist=0)

这里连了两张表,发送用户和接收用户都被排除
这样就联立user表并且排除掉了所有黑名单用户,

  • 然后就是找到日期,和每个日期邮件发送的成功率,
  • 日期就select email.date就行了,但是成功率不太好写,
  • 我们这里用一个函数叫case…when …then …else …end
    就很简单了,如下
sum(case email.type when'completed' then 0 else 1 end)

当email.type为completed时,就为0,如果不为completed就为1,然后把这一列所有的和加起来,我们就得到了失败的发送邮件的数目,然后使用round函数保留后面3位:

round
(
    sum(case email.type when'completed' then 0 else 1 end)*1.0/count(email.type),3
) 

最后再按照日期分组,得到每一天的数据,并且按照日期升序排序:group by email.date order by email.date;

select email.date, round(
    sum(case email.type when'completed' then 0 else 1 end)*1.0/count(email.type),3
) as p
from email
join user as u1 on (email.send_id=u1.id and u1.is_blacklist=0)
join user as u2 on (email.receive_id=u2.id and u2.is_blacklist=0)
group by email.date order by email.date;

SQL66 牛客每个人最近的登录日期(一)

大家 多数都是 用MAX () 看着 代码很简洁
我写点不一样 但是 也是很容易理解的
我的方法是用Rank 根据 User_ID 在按照日期排序 得到序号
最后 嵌套 抓出 第一个也就是最近日期

select user_id ,date
From (

select user_id,date,rank() over(partition by user_id order by date desc ) rk
from login
)
where rk =1
order by user_id

大佬 用的是 rank 然后加了关键字 rows between current row and unbounded following
具体如下
select distinct
user_id,
last_value(date) over(partition by user_id order by date rows between current row and unbounded following) as d
from login;
大写的佩服啊 学习到新的知识了

SQL67 牛客每个人最近的登录日期(二)

题目描述:sql语句查询每个用户最近一天登录的日子,用户的名字,以及用户用的设备的名字,并且查询结果按照user的name升序排序。
考点:group by 子句常见错误:
select 子句中只能存在以下三种元素:常数、聚合函数、group by子句指定列(聚合键)
所以下面这么写法错误:

select u.name as u_n, c.name as c_n, max(l.date) as d  
from login as l
inner join user as u
on l.user_id=u.id
inner join client as c
on l.client_id=c.id
group by u.name
order by u.name asc

正确代码
select u.name as u_n,
       c.name asc_n,
       l.date
from login l join user u on l.user_id=u.id
             join client c on l.client_id=c.id
where (l.user_id,l.date) in (select user_id,max(date) from login group by user_id)
order by u_n
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值