牛客sql题

sql

最差是第几名(二)

首先我们需要知道:当某一数的正序和逆序累计均大于整个序列的数字个数的一半即为中位数
比如:
A A B B C C D D
1 2 3 4 5 6 7 8
8 7 6 5 4 3 2 1
那么上面的4,5以及5,4就是中位数,如果是奇数的话,就只有1个
再比如
A2个,B3个,C5个,D2个,
正序2,5,10,12
倒序12,10,7,2
正序和12,大于等于6的为C,D,
逆序和12,大于等于6的为A,B,C,所以最后中位数为C
跳转习题

select grade from
(select grade,(select sum(number) from class_grade) as total,
sum(number) over(order by grade) as a,
sum(number) over(order by grade desc) as b
from class_grade ) as s1
where a>=total*1.0/2 and b>=total*1.0/2
order by grade

最差是第几名(一)

1、sum() over (order by …)

select grade,sum(number) over(order by grade) as cnt from class_grade

本题出题的题意其实主要是考察sum() over (order by ) 开窗函数,sum(a) over (order by b) 的含义是:
例如
a b
1 2
3 4
5 6
按照b列排序,将a依次相加,得到结果,如下:
a b sum(a) over (order by b):
1 2 1
3 4 1+3
5 6 1+3+5

此题就是将b换成了grade,即使b列乱序也没关系,因为有order by b:
number grade sum(number) over(order by grade):
2 A 2
2 B 2+2
2 C 2+2+2
1 D 2+2+2+1

所以我们代码可以如下写:
1
2
3
select grade, sum(number) over(order by grade) t_rank
from class_grade
order by grade;
2、case when … then … end
其中比如要给出sum(number) 都要从列表中去求解
select sum(number) from table where grade <='D’这样

select grade,
(case when grade='A' then (SELECT SUM(number) FROM class_grade WHERE grade <='A')
when grade='B' then (SELECT SUM(number) FROM class_grade WHERE grade <='B')
when grade='C' then (SELECT SUM(number) FROM class_grade WHERE grade <='C')
when grade='D' then (SELECT SUM(number) FROM class_grade WHERE grade <='D')
else (select sum(number) from class_grade) end) as t_rank
from class_grade
order by grade

实习广场(三)

实习广场3
重点:
2025表和2026表求出来之后,最后的关联条件,on 月份相同并且job相同
自己解法:让她们的年月相等,最终出来的结果是none

select s1.job,s1.first_year_mon,s1.first_year_cnt,s2.second_year_mon,s2.second_year_cnt
from 
(select job, strftime('%Y-%m',date) as first_year_mon,
strftime('%m',date) as mon1,sum(num) as first_year_cnt
from resume_info
where date between '2025-01-01' and '2025-12-31'
group by job,first_year_mon) as s1
left join
(select job, strftime('%Y-%m',date) as second_year_mon,
strftime('%m',date) as mon2,sum(num) as second_year_cnt
from resume_info
where date between '2026-01-01' and '2026-12-31'
group by job,second_year_mon) as s2
on s1.mon1=s2.mon2 and s1.job=s2.job
order by s1.first_year_mon desc,s1.job desc

strftime()函数和cast()函数

参考函数 链接
select strftime(’%Y-%m’,日期) as 月份,sum(支出) as 月支出 from 流水帐 group by 月份;

牛客订单(七)

在五的基础上求
牛客订单七
在这里插入图片描述
在这里插入图片描述

求解错误的点:
1、case when condation1 then result1 else result 2 ==构成新表的表名() as source
2、我当时的条件是case when client_name id ‘null’ then ‘GroupBuy’ else name as source 显然不通过,null真实没有定义
答案是case is_group_by when ‘Yes’ then … 显然答案从另一个列入手,换位思考
3、我当时是count(client_name) 然而无效 答案是count(*)

select 
(case is_group_buy when 'Yes' then 'GroupBuy'
else name end) as source,
count(*) as cnt
from 
(select s3.is_group_buy,client.name
from 
(select s2.* from 
(select * from order_info
where status='completed' and date>'2025-10-15'
and product_name in ('Python','C++','Java')) as s2
join 
(select user_id from order_info
where status='completed' and date>'2025-10-15'
and product_name in ('Python','C++','Java')
group by user_id having count(*)>1) as s1
on s1.user_id=s2.user_id) as s3
left join client
on s3.client_id=client.id) as s4
group by name
order by source asc;

牛客订单(四)

添加链接描述
点击跳转原题
难点:查找第二小的日期date
知识点:lead() over() 函数
lead( a,0) over(partition by vin ,fee_type order by time asc)
首先,先看over里面的内容,指的是根据vin 和fee_type进行分组
然后,根据time进行排序
最后,因为lead(a,0)里面是0,所以选取的是分组排序后的所有的a元素数据。
此外,如果是lead(a,1)里面是1,选取的就是分组排序后的第二行元素,
此外,如果是lead(a,2)里面是2,选取的就是分组排序后的第三行元素,

参考

select user_id,min(date) as first_buy_date,next_date as second_buy_date,
count(*) as num from 
(select *,lead(date,1) over(partition by user_id 
                            order by date asc) as next_date
from order_info
where status='completed' and date>'2025-10-15'
and product_name in ('C++','Python','Java'))
group by user_id having count(*) > 1 
 order by user_id asc;

考试分数(五)

1、找出job start end 每一种语言的中位数的位置 s1
2、按照id1,2,…,7 按语言的种类,score的大小,进行排名,排名方法用row_number over(partition by job order by score desc) s2
3、合并两个表,合并条件
s1.job= s2.job where s1.start=s2.rank or s1.end=s2.rank
第三步没有想出来

select s3.id,s3.job,s3.score,s3.rank
from 
(select id,job,score,row_number() over(partition by job order by score desc) as rank
from grade order by id) as s3 
join 
(select job,
case when num%2=0 then num/2 else num/2+1 end as start,
case when num%2=0 then num/2+1 else num/2+1 end as end
from 
(select job,count(id) as num from grade group by job) as s1) as s2
on s3.job=s2.job 
where s3.rank=s2.start or s3.rank=s2.end
order by s3.id

考试分数(四)

1、对当前的分数不需要排名
2、只要计算每种语言的个数,然后确定中位数在第几个即可
3、用case when condation then result1 else result2 end as start来表示

select job,
case when num%2=0 then num/2 else num/2+1 end as start,
case when num%2=0 then num/2+1 else num/2+1 end as end
from 
(select job,count(id) as num from grade group by job) as s1
order by job;

考试分数(三)

重点:
对不同的语言,分别进行排名
1、不同语言排名,得到id,名次
2、选择名次1,2的id
3、原始表合并语言表合并名次1,2id表

select g.id,l.name,g.score from grade as g
join language as l on g.language_id=l.id join
(select id from
(select id,dense_rank() 
over( partition by language_id order by score desc) as rank from 
grade) as s1 where s1.rank<3) as s2
on g.id=s2.id
order by l.name,g.score desc,g.id asc

考试分数(二)

写一个sql语句查询用户分数大于其所在工作(job)分数的平均分的所有grade的属性,并且以id的升序排序
注意:
1、当前科目的分数要大于当前科目的平均分 (找到当前科目平均分)
2、怎么保证大于平均分的是一样的科目 job相同 and grade > avg(grade) 双重条件

select g.* from grade as g
join (select job,round(sum(score)*1.0/count(id), 3) as avg1 from grade group by job) as s1
on g.job=s1.job and g.score>s1.avg1
order by g.id;

66、牛客每个人最近的登录日期

1、最大日期:max(date)
2、只出现一次id 所以要group by id

select id, max(date) from login group by id order by id

62、出现3次及以上相同number

select number from grade group by number having count(id)>2

61、employee表,给出奇数行的first_name

注意:输出不需要排序

# 1、这样运行失败,因为排序了,要保持顺序一致不变
select s1.first_name from 
(select first_name, row_number() over(order by first_name ) as rank
from employees ) as s1
where s1.rank%2=1
#2、与原表的first_name进行对比
select s1.first_name from employees as e join
(select first_name, row_number() over(order by first_name ) as rank
from employees ) as s1
on e.first_name = s1.first_name
where s1.rank%2=1

创建外键
46牛客
alter table table_name1 add foreign key(key_name) reference table_name2(row)

alter table audit add  foreign key(emp_no) references employees_test(id)

修改表名rename to
alter table tablename rename to tablename1

alter table titles_test rename to titles_2017

替换表信息 replace into/update …set
replace into table_name values(1,2,3)
update table_name set 更改列=replace(列名,更改前值,更改后值) where id=

replace into titles_test values('5', '10005', 'Senior Engineer', '1986-06-26', '9999-01-01')
update titles_test set emp_no=replace(emp_no, 10001, 10005) where id = 5

更新表 update … set
update table_name set condation

update  titles_test
set from_date='2001-01-01' , to_date=Null where to_date = '9999-01-01'

删除表中某条记录 delete from … where…

delete from table_name where condition

创建视图
根据table表创建视图view

create view view_name as select table_name1 from table_name as view_name1

create view actor_name_view as 
select first_name as first_name_v,
last_name as last_name_v from actor

37创建普通索引和唯一索引
create unique index index_name from tablename(table_row)

create unique index uniq_idx_firstname on actor(first_name);
create index idx_lastname on actor(last_name);

36:牛客:创建一个actor_name表,其中last_name,first_name信息来自于actor表

create table if not exists actor_name 
(1 2 3,3 4 5) 
insert into actor_name select 1,3 from actor;

32:last_name 和first_name 拼接
牛客32

concat,concat_ws,group_concat三种函数

一、concat

# CONCAT()函数用于将多个字符串连接成一个字符串
CONCAT(str1,str2,)                       
#返回结果为连接参数产生的字符串。如有任何一个参数为NULL ,则返回值为 NULL。可以有一个或多个参数。

二、concat_ws
指定参数之间的分隔符,使用函数CONCAT_WS()。
CONCAT_WS() 代表 CONCAT With Separator
第一个参数是分割符,分割符放在两个字符串之间,分隔符也可以是字符串
1、分隔符为null,结果为null
2、字符串中为null,自然忽略

CONCAT_WS(separator,str1,str2,)

三、group_concat
GROUP_CONCAT函数返回一个字符串结果,该结果由分组中的值连接组合而成

group_concat(id)

拼接参考链接

牛客:求出第二薪水高的,但是不用order by
重点:用max()函数来一次次查找

select salary from salaries
where salary=(select max(salary) from salaries
where salary < (select max(salary) from salaries))

答案:

select e.emp_no, s.salary, e.last_name, e.first_name
from employees as e join salaries as s
on e.emp_no=s.emp_no
where 
s.salary=(select max(salary) from salaries
where salary < (select max(salary) from salaries))

牛客:获取每个部门当前员工薪水的最高值【困难】

select t1.dept_no, t1.emp_no, max(t1.salary) as maxSalary
from 
(select d.dept_no, s.emp_no, s.salary
from
dept_emp as d left join salaries as s
on d.emp_no=s.emp_no
order by s.salary desc limit 10000
) as t1
group by t1.dept_no
order by t1.dept_no;

解释:
GROUP BY 默认取非聚合的第一条记录!!!!!!
1、创建两张表,一张为maxsalary,用于存放当前每个部门薪水的最大值;另一张为currentsalary,用于存放当前每个部门所有员工的编号和薪水;
2、限定条件为两张表的 dept_no 和 salary 相等,这样就可以找出当前每个部门所有薪水等于最大值的员工的相关信息了;
max和group by
group by 是先排序,然后选择最大值之后,其余value值是排序后的第一条记录,所以输出有错
order by s.salary desc limit 10000 先按照从大到小的方式倒序选择前10000条,这样,最大的那条记录不会出现拼接的问题

group by 和max() 连用

72考试分数

分组之后的分数和是 sum(score)

求平均数是 *round(sum(score)1.0/count(id),3) as avg

sqlite 1/2得到的不是0.5,得到的是0,只有1*1.0/2才会得到0.5,sqlite四舍五入的函数为round

按照分数降序排序: order by avg desc

66 牛客每个人最近的登录日期

首先是按照用户id升序没问题

要统计每个用户最近登录的是哪一天:找到天数的最大值 并且按照用户去分组

select MAX(date) as d group by user_id order by user_id

64 找到每个人的任务

没有任务的也要输出,所以使用左连接 from person left join task on person.id=task.person_id

person的id升序排序 order by p.id asc

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

对积分进行分组后,用having来控制条件

改表名

alter table titles_test rename to titles_2017

44 将id=5以及emp_no=10001的行数据替换成id=5以及emp_no=10005

,其他数据保持不变,使用replace实现

更新表,用update来更新表 replace(指定某列,原值,新值)

set是sql中对已经定义的变量赋值的方式,经常与update一起使用

update 表名称 set 列名称 = 新值 where 列名称=某值

注意:如果是字符串的话,要加字符串索引

42 删除emp_no重复的记录,只保留ID最小的

删除用delete from 表 where 条件

delete from titles_test where
id > (select min(id) from titles_test group by emp_no )

34 批量插入数据

insert into 表名(列名) values(行内容) 加行
alter table 表名 add column 列名 列约束,列名 列约束; 加列

注意字符串要带引号

32 拼接两个name,并且中间隔着空格

last_name||’ '||first_name)

CREATE TABLE salaries (
emp_no int(11) NOT NULL,
salary int(11) NOT NULL,
from_date date NOT NULL,
to_date date NOT NULL,
PRIMARY KEY (emp_no,from_date));

20 查找员工编号emp_no为10001其自入职以来的薪水salary涨幅值growth

方法1:利用子查询查询最后一次工资和第一次工资 可以order by limit 也可以max(to_date) min(to_date)

方法1:利用ORDER BY查询最后一次工资和第一次工资

select (select max(salary) from salaries where emp_no=10001)-
(select min(salary) from salaries where emp_no=10001) as growth

select
(select salary from salaries where emp_no=10001 and
to_date=(select max(to_date) from salaries where emp_no=10001))-
(select salary from salaries where emp_no=10001 and
to_date=(select min(to_date) from salaries where emp_no=10001)) as growth

15 查找employees表所有emp_no为奇数,且last_name不为Mary的员工信息,并按照hire_date逆序排列

题意:每个title下,emp_no重复的员工忽略不计

emo为基数 emo %2 == 1

lastname 不是mary last_name != ‘mary’ last_name <> ‘mary’

select * from employees where last_name != ‘Mary’ and emp_no%2 == 1 order by hire_date desc

select * from employees where last_name <>‘Mary’ and emp_no& 1 order by hire_date desc

10 获取所有非manager的员工的emp_no

方法1:NOT IN+子查询

not in 后面接子查询 而不是 e.emp_no != d.emp_no

select emp_no from employees where emp_no not in(select emp_no from dept_manager)

方法2:LEFT JOIN左连接+IS NULL
select e.emp_no from employees as e left join dept_manager as d on e.emp_no=d.emp_no where dept_no is null

left join … on 以左边表为主

主管就是有dept_no的人

7 ※查找薪水变动超过15次的员工号emp_no以及其对应的变动次数t

直接按照emp_no 进行分组,查找一共出现了多少次,选取大于十五次的

考虑1:分组+聚合函数

select emp_no,count() as t from salaries group by emp_no having t > 15

考虑2:考虑表中salary 去重后的数目

考虑3:严格意义上的涨幅 a.to_date = b.from_date 并且 a.salary < b.salary

表中name去重后的数目 select count(distinct name) from A;distinct必须放在开头

73 考试分数2

请你写一个sql语句查询用户分数大于其所在工作(job)分数的平均分的所有grade的属性,并且以id的升序排序

1.相同的job才能比较某一科目自己与科目平均分之间的值,首先要创建一个新的表

select job,round(sum(score)*1.0/count(id),3) as avg from grade group by job

2.用join函数对两个表进行连接

select grade.* from grade join (select job,round(sum(score)*1.0/count(id),3) as avg from grade group by job )as bb on grade.job = bb.job and grade.score > bb.avg order by id 这里and where都可以

也可以不用制表,用同一个表赋予不同的名称 其中分数> 平均分数(在job相同并且按job分组的情况下求得平均分数)

select * from grade as g where g.score > (select avg(y.score) from grade as y where g.job = y.job group by y.job) order by g.id asc

63 刷题通过的题目排名

输出通过的题目的排名,通过题目个数相同的,排名相同,此时按照id升序排列,

select id,number,dense_rank() over (order by number desc) as rank from passing_number order by number desc,id asc

一、ROW_NUMBER

row_number的用途的非常广泛,排序最好用他,一般可以用来实现web程序的分页,他会为查询出来的每一行记录生成一个序号,依次排序且不会重复,注意使用row_number函数时必须要用over子句选择对某一列进行排序才能生成序号。row_number用法实例:

select ROW_NUMBER() OVER(order by [SubTime] desc) as row_num,* from [Order]
二、RANK

rank函数用于返回结果集的分区内每行的排名, 行的排名是相关行之前的排名数加一。简单来说rank函数就是对查询出来的记录进行排名,与row_number函数不同的是,rank函数考虑到了over子句中排序字段值相同的情况,如果使用rank函数来生成序号,over子句中排序字段值相同的序号是一样的,后面字段值不相同的序号将跳过相同的排名号排下一个,也就是相关行之前的排名数加一,可以理解为根据当前的记录数生成序号,后面的记录依此类推。可能我描述的比较苍白,理解起来也比较吃力,我们直接上代码,rank函数的使用方法与row_number函数完全相同。

select RANK() OVER(order by [UserId]) as rank,* from [Order] 
三、DENSE_RANK

dense_rank函数的功能与rank函数类似,dense_rank函数在生成序号时是连续的,而rank函数生成的序号有可能不连续。dense_rank函数出现相同排名时,将不跳过相同排名号,rank值紧接上一次的rank值。在各个分组内,rank()是跳跃排序,有两个第一名时接下来就是第四名,dense_rank()是连续排序,有两个第一名时仍然跟着第二名。将上面的Sql语句改由dense_rank函数来实现。

select DENSE_RANK() OVER(order by [UserId]) as den_rank,* from [Order]
四、NTILE

ntile函数可以对序号进行分组处理,将有序分区中的行分发到指定数目的组中。 各个组有编号,编号从一开始。 对于每一个行,ntile 将返回此行所属的组的编号。这就相当于将查询出来的记录集放到指定长度的数组中,每一个数组元素存放一定数量的记录。ntile函数为每条记录生成的序号就是这条记录所有的数组元素的索引(从1开始)。也可以将每一个分配记录的数组元素称为“桶”。ntile函数有一个参数,用来指定桶数。下面的SQL语句使用ntile函数对Order表进行了装桶处理:

select NTILE(4) OVER(order by [SubTime] desc) as ntile,* from [Order]

60、统计salary的累计和
60、
注意看清条件,当前时间to_date=‘9999-01-01’
窗口函数: sum(汇总列) over(排序列)

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

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

不用exist 用is null

select e.* from employees as e left join dept_emp as d on e.emp_no = d.emp_no where d.dept_no is null

用exist

select e.* from employees as e where not exists (select dept_no from dept_emp as d where e.emp_no = d.emp_no)

54 查找排除最大、最小salary之后的当前(to_date = ‘9999-01-01’ )员工的平均工资avg_salary

select avg(salary) as avg_salary from salaries 

where  salary not in (select max(salary) from salaries where to_date='9999-01-01') 

and  salary not in (select min(salary) from salaries where to_date='9999-01-01') 

and to_date = '9999-01-01'

还可以升序降序选首位元素,进行排除

本来想试着用limit选择第一到倒数第二的元素也可以 用到sqlite语言

53 按照dept_no进行汇总

属于同一个部门的emp_no按照逗号进行连接,结果给出dept_no以及连接出的结果employees

group_concat(连接的字段,连接的符号) 默认是逗号 此函数必须与group by 连用
默认逗号可不写,如果写group_concat(id,’,’) == 1,2,3,

select dept_no,group_concat(emp_no) as employees from dept_emp group by dept_no
concat函数

在进行数据库查询时,偶尔会遇到字符串拼接的情景,我们通常会运用concat函数进行字符串的拼接。但在使用该函数时,需要注意如下细节:

拼接字符串str1、str2时,我们会如下使用:concat(str1,str2),在数据库中操作时例如:

select concat(str1,str2) from t_user;

(其中str1和str2是t_user表的两个字段),正常来说该查询语句会返回str1和str2拼接之后的值,可在实际运用过程中会出现意想不到的结果,例如:str1为null,str2为"aaa",返回的结果为null,并不是我们期待的"aaa"。

出现这种情况是因为concat函数在使用时,它会判断他的参数是否有为null的情况,只要有其中一个参数为null,则concat函数返回值就为null,上述示例如果想返回"aaa",则需要将SQL语句进行如下修改:

select concat(ifnull(str1,“defaultvalue”),str2) from t_user

这里使用了ifnull函数,该函数的用处是判断一个字段是否为null,如果为null,第二个参数则为该字段指定一个默认值。

52 获取Employees中的first_name,查询按照first_name最后两个字母,按照升序进行排列

SUBSTR(str,pos,len): 从pos开始的位置,截取len个字符

SBUSTR(str,pos,len);
就是从pos开始的位置,一直截取len长度。
select first_name from employees
order by substr(first_name,-2,2) asc

从倒数第二位向后取两位

查找字符串’10,A,B’ 中逗号’,'出现的次数cnt。

用replace进行替换

select length('10,A,B')-length(replace('10,A,B',',','')) as cnt

48 将所有获取奖金的员工当前的(salaries.to_date=‘9999-01-01’)薪水增加10%。(emp_bonus里面的emp_no都是当前获奖的所有员工)

注意是向表中增加一条信息:
update tablename set condation where condation1
选取的emp_no 用的是in而不是is

update salaries set salary = salary*1.1 
where emp_no in (select emp_no from emp_bonus)
and to_date ='9999-01-01'sql

这里是两张表 所以要判断emp_no是否属于获得奖金的员工

40 现在在last_update后面新增加一列名字为create_date

ALTER TABLE .表名… ADD 内容…
语句可以向已存在的表插入新字段,并且能够与创建表时一样,在字段名和数据类型后加入NOT NULL、DEFAULT等限定

39 针对salaries表emp_no字段创建索引idx_emp_no,查询emp_no为10005, 使用强制索引

MYSQL中强制索引查询使用:FORCE INDEX(indexname);

SQLite中强制索引查询使用:INDEXED BY indexname;

select * from salaries indexed by 'idx_emp_no' where emp_no='10005'

38 针对actor表创建视图actor_name_view

只包含first_name以及last_name两列,并对这两列重新命名,first_name为first_name_v,last_name修改为last_name_v:

方法一:注意 CREATE VIEW … AS … 的 AS 是创建视图语法中的一部分,而后面的两个 AS 只是为字段创建别名

方法二:直接在视图名的后面用小括号创建视图中的字段名

CREATE view actor_name_view as
select first_name as first_name_v,last_name as last_name_v from actor
CREATE view actor_name_view (first_name_v,last_name_v) as
select first_name,last_name from actor

37对first_name创建唯一索引uniq_idx_firstname,对last_name创建普通索引idx_lastname

给指定表或者视图的某列添加索引使用语句:create (unique) index 索引名 on 表名(列名)

create unique index uniq_idx_firstname on actor(first_name);
create index idx_lastname on actor(last_name)

36 请你创建一个actor_name表,并且将actor表中的所有first_name以及last_name导入该表.

1.用create table 语句建立actor_name 表

2.用inset into actor select插入子查询的结果集(不需要用values(),()这种形式。这种形式是手工插入单条数据或多条数据时用圆括号分割。插入结果集是不需要)

create table actor_name as
select first_name,last_name from actor
create table if not exists actor_name 
(first_name varchar(45) not null,
last_name varchar(45) not null);
insert into actor_name select first_name,last_name from actor

create table xx

insert into xx select xx from xx

35 插入数据

insert or ignore into values(11,22,33)

insert or ignore into actor
values ('3','ED','CHASE','2006-02-15 12:34:33')

33创建一个actor表

,包含如下列信息(注:sqlite获取系统默认时间是datetime(‘now’,‘localtime’))

1、在actor_id字段末尾加上PRIMARY KEY是将该字段设置为主键,或者在表的最后一行加上PRIMARY KEY(actor_id),注意各个字段结束逗号

2、在last_update末尾加上DEFAULT是为该字段设置默认值,且默认值为(datetime(‘now’,‘localtime’)),即获得系统时间,注意最外层的括号不可省略

create table name (
aa leixing not null primary key,
bb leixing not null default (morenshijian)
)

CREATE TABLE actor
(
actor_id smallint(5) NOT NULL PRIMARY KEY,
first_name varchar(45) NOT NULL,
last_name varchar(45) NOT NULL,
last_update timestamp NOT NULL DEFAULT (datetime('now','localtime')) -- ,
-- PRIMARY KEY(actor_id)
)

30 三张表查找动作片信息

最简单粗暴的解法是直接 FROM 三张表查询,且用 WHERE 并列三个限定条件

1、三个限定条件分别是【f.film_id = fc.film_id】、【fc.category_id =
c.category_id 】、【c.name = ‘Action’】

select f.title,f.description from film as f,category as c,film_category as fc
where f.film_id = fc.film_id and fc.category_id = c.category_id and c.name = 'Action'

三个where xx in xx

select title,description from film as f where
f.film_id in (
select film_id from film_category as fc where
fc.category_id in (
select category_id from category where name = 'Action' ))

29 使用join查询方式找出没有分类的电影id以及名称

  1. 电影表 电影id 电影名称 电影描述信息
  2. 分类表 电影分类id 电影分类明细 电影分类最后更新时间
  3. 电影分类表 电影id 电影分类id 最后更新时间

既然要查询没有分类的电影id,也就是找到电影表中的电影,去电影分类表中查找分类id是否存在

  1. join 的话 左连接,以电影表为基准,如果找不到分类则为null
  2. 直接查到电影id是否存在于电影分类表中
select film_id,title from film where
film.film_id not in (
select film_id from film_category)
select f.film_id,f.title from (film as f 
left join film_category as fc 
on f.film_id = fc.film_id) as ff
where ff.category_id is null

左连接:包含左表的所有信息,如果右表中没有,则为0

22 统计各个部门的工资记录数,

select dt.dept_no,dt.dept_name,count(*) as sum
from departments as dt,dept_emp as de,salaries as s # 三者重新命名
where dt.dept_no=de.dept_no and de.emp_no = s.emp_no # 连接起来
group by dt.dept_no # 按照部门进行分组
select dt.dept_no,dt.dept_name,count(*) as sum
from (departments as dt inner join dept_emp as de
on dt.dept_no=de.dept_no) inner join salaries as s
on de.emp_no = s.emp_no
group by dt.dept_no

用inner join进行对两两表之间连接 最后还是要通过group by 分组函数来确定sum

19查找所有员工的last_name和first_name以及对应的dept_name

也包括暂时没有分配部门的员工(left join)

select e.last_name,e.first_name,dp.dept_name
from 
(employees as e left join dept_emp as de
on de.emp_no = e.emp_no )  
left join 
departments as dp on dp.dept_no=de.dept_no

两次left join 是用到了 但最后一次on后面的条件是de和dp比较,而不把前面left join当作一个整体

select s.last_name,s.first_name,de.dept_name
from ((employees as s left join dept_emp as dp
on dp.emp_no = s.emp_no ) as tmp left join 
departments as de on tmp.dept_no=de.dept_no)

自己写的这个也通过了 注意字母一定要正确

14 从titles表获取按照title进行分组,每组个数大于等于2,给出title以及对应的数目t

WHERE后不可跟COUNT()函数,故用HAVING语句

COUNT(DISTINCT
emp_no)可以统计同一title值且不包含重复emp_no值的记录条数

select title,count(distinct emp_no) as t 
from titles group by  title having t >1 
select title,count(*) as t 
from(select distinct emp_no,title,from_date,to_date from titles) 
group by  title having t >1 

12 获取所有部门中当前(dept_emp.to_date = ‘9999-01-01’)员工当前(salaries.to_date=‘9999-01-01’)薪水最高的相关信息,给出dept_no, emp_no以及其对应的salary,按照部门升序排列。

select d.dept_no,d.emp_no,max(salary) as salary
from dept_emp as d inner join salaries as s  # 这里要取交集
on d.emp_no=s.emp_no where
d.to_date = '9999-01-01' and  s.to_date = '9999-01-01'
group by d.dept_no order by d.dept_no 

having可以再group by 后面使用,但一般不用

使用group by子句时,select子句中只能有聚合键、聚合函数、常数。

emp_no并不符合这个要求

select d.dept_no,d.emp_no, s.salary
from dept_emp as d inner join salaries as s
on d.emp_no=s.emp_no and
d.to_date = '9999-01-01' and  s.to_date = '9999-01-01'
where s.salary = (select max(s.salary) from salaries as s inner join
                 dept_emp as de on s.emp_no=de.emp_no and s.to_date ='9999-01-01'
                 and de.to_date='9999-01-01'
                  where de.dept_no=d.dept_no
                  group by de.dept_no)
order by d.dept_no 

11 获取所有员工当前的(dept_manager.to_date=‘9999-01-01’)manager,如果员工是manager的话不显示(也就是如果当前的manager是自己的话结果不显示)。输出结果第一列给出当前员工的emp_no,第二列给出其manager对应的emp_no。

注意:这道题用inner join 取交集

并且员工的编号不等于经理表中的经理编号

1、用 INNER JOIN 连接两张表,因为要输出自己的经理,得知自己与经理的部门要相同,故有限制条件 de.dept_no = dm.dept_no

2、再用 WHERE 限制当前员工与当前经理的条件,即 dm.to_date 等于 ‘9999-01-01’ 、de.to_date 等于 ‘9999-01-01’ 、 de.emp_no 不等于 dm.emp_no

select de.emp_no,dm.emp_no as manager_no
from dept_emp as de
inner join dept_manager as dm 
on de.dept_no = dm.dept_no
where de.to_date = '9999-01-01' and dm.to_date = '9999-01-01'
and de.emp_no <> dm.emp_no
select de.emp_no,dm.emp_no as manager_no  
from dept_emp as de
inner join dept_manager as dm 
on de.dept_no = dm.dept_no
and dm.to_date = '9999-01-01'and de.to_date = '9999-01-01'
where de.emp_no not in
(select emp_no from dept_manager)

join默认是inner join

75 请你写一个sql语句查询各个岗位分数升序排列之后的中位数位置的范围,并且按job升序排序,

Case when 的用法: 一旦满足了某一个WHEN, 则这一条数据就会退出CASE WHEN , 而不再考虑 其他CASE;

Case函数(Case搜索函数): 判断表达式的真假,如果为真,返回结果;如果为假,返回else值;如果未定义else值,则返回空值(使用条件确定返回值);

select name,id,(case when id=34 then salary*2

                                    when id=45 then salary*3

                                    else salary

                                    end) new_salary

from semp;

注意:

1.case when 条件1 then 结果 1 when 条件2 then 结果 2 else 结果3 end

2.这个条件要判断每种工作的总数,首先得到num,这个num就是按照工作进行分组得到的

select job,
case when num%2=1 then (num+1)/2 else num/2 end as start,
case when num%2=1 then (num+1)/2 else num/2+1 end as end
from (select job,count(id) as num  from grade group by job)
order by job

74 ※考试分数三

请你找出每个岗位分数排名前2(算并列的所以用dense_rank())的用户,得到的结果先按照language的name升序排序,再按照积分降序排序,最后按照grade的id升序排序

dense_rank函数的功能与rank函数类似,dense_rank函数在生成序号时是连续的,而rank函数生成的序号有可能不连续。
dense_rank函数 出现相同排名时,将不跳过相同排名号 ,rank值紧接上一次的rank值。在各个分组内,rank()是跳 跃排序,有两个第一名时接下来就是第四名,dense_rank()是连续排序,有两个第一名时仍然跟着第二名。


row_number():暂且称为去重排序,在每个分组内,为查询出来的每一行记录生成一个序号,依次排序且不会重复;其基本原理是先使用over子句中的排序语句对记录进行排序,然后按照这个顺序生成序号。over子句中的order by子句与SQL语句中的order by子句没有任何关系,这两处的order by 可以完全不同

partition by 用于给结果集分组,如果没有指定那么它把整个结果集作为一个分组,它和聚合函数不同的地方在于它能够返回一个分组中的多条记录,而聚合函数一般只有一个反映统计值的记录

rank(): 跳跃排序,在每个分组内,如果有两个第一位时,接下来就是第三位

partition by e.deptno —— 按部门编号划分(分区)。

over —— 在什么条件之上。
select g.id,l.name,g.score 
from ( select *,dense_rank() over ( partition by language_id order by score desc ) as rank from grade) as g,language as l where g.language_id = l.id and g.rank < 3
order by l.name asc,g.score desc,g.id asc

注意: over 后面加括号

dense_rank() over (partition by xx )

25 获取员工其当前的薪水比其manager当前薪水还高的相关信息

  1. 联合员工和薪资表
  2. 联合经理和薪资表
  3. 条件第一个表的薪水大于第二个表的薪水并且他们的部门相同
select sde.emp_no as emp_no ,sdm.emp_no as manager_no,
sde.salary as emp_salary,sdm.salary  as manager_salary
from (dept_emp as de inner join salaries as s
on de.emp_no=s.emp_no and s.to_date='9999-01-01') as sde,
 (dept_manager as dm inner join salaries as s
on dm.emp_no=s.emp_no and s.to_date='9999-01-01') as sdm
where sde.salary > sdm.salary and sde.dept_no=sdm.dept_no

on 后面加where

可以得到where条件是在left join操作完成后所进行的条件筛选

可得到on条件是在left join之前先进行条件筛选,而后才对两个表格join操作

on比where起作用更早,先根据on条件进行多表的连接操作,生成一个临时表再通过where来筛选

18 薪水第二多员工(不用order by)

那么用max() 找到薪水不等于最大值的哪个最大薪水(可以不等于,也可以小于)

select e.emp_no,max(s.salary) as salary,e.last_name,e.first_name
from employees as e,salaries as s 
where e.emp_no=s.emp_no and 
s.salary not in  (select max(salary) from salaries
                 where to_date='9999-01-01')

也可以内连接

21 查找所有员工自入职以来的薪水涨幅情况,给出员工编号emp_no以及其对应的薪水涨幅growth,并按照growth进行升序

1.找到当前工资

2.找到入职时候的工资

3.相减得到growth

select e.emp_no,s1.salary from employees e inner join salaries s1
on e.emp_no=s1.emp_no where s1.to_date = '9999-01-01'

select e.emp_no,s2.salary from employees e inner join salaries s2
on e.hire_date=s2.from_date 

select e.emp_no,(s1.salary-s2.salary) as growth 
from employees e inner join salaries s1
on e.emp_no=s1.emp_no 
inner join salaries s2
on e.hire_date=s2.from_date 
where s1.to_date = '9999-01-01'
order by growth

23 对所有员工的当前(to_date=‘9999-01-01’)薪水按照salary进行按照1-N的排名,相同salary并列且按照emp_no升序排列

select s.emp_no,s.salary,dense_rank() over ( order by salary desc ) 
as rank from salaries as s
where s.to_date='9999-01-01' order by salary desc,emp_no asc

26※ 汇总各个部门当前员工的title类型的分配数目,

即结果给出部门编号dept_no、dept_name、其部门下所有的当前(dept_emp.to_date = ‘9999-01-01’)员工的当前(titles.to_date = ‘9999-01-01’)title以及该类型title对应的数目count

1、利用inner join 连接title 和dept_emp两张表限定条件为:t.emp_no=de.emp_no

2、利用inner join 连接 departments限定条件为:de.dept_no=dp.dept_no

3、当前员工限定条件为:de.to_date=‘9999-01-01’ and t.to_date=‘9999-01-01’

4、最后用 group by 同时对 de.dept_no 和 t.title 进行分组

两次inner join

select dp.dept_no,dp.dept_name,t.title,count(t.title) as count
from departments as dp join dept_emp as de on dp.dept_no=de.dept_no
and de.to_date = '9999-01-01'
inner join titles as t on de.emp_no=t.emp_no
and t.to_date = '9999-01-01'
group by dp.dept_no, t.title

创建表:

create table table_name (id1 xingzhi not null; id1 xingzhi not null; id1 xingzhi not null)

插入数据:

insert into table_name (id1,id2,id3) values (1,2,3)(2,3,4)

插入数据,如果已经存在,就忽略

insert or ignore into actor 
values ('3', 'WD', 'GUINESS', '2006-02-15 12:34:33');

猜你喜欢

转载自blog.csdn.net/weixin_44697051/article/details/115058831