【SQL】 牛客网SQL训练Part3 较难难度

 

 

获取当前薪水第二多的员工的emp_no以及其对应的薪水salary

请你查找薪水排名第二多的员工编号emp_no、薪水salary、last_name以及first_name,不能使用order by完成,以上例子输出为:
(温馨提示:
  sqlite通过的代码不一定能通过mysql,因为SQL语法规定,使用聚合函数时,
  select子句中一般只能存在以下三种元素:常数、聚合函数,group by 指定的列名。
  如果使用非group by的列名,sqlite的结果和mysql 可能不一样
)
drop table if exists  `employees` ; 
drop table if exists  `salaries` ; 
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`));
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`));
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 salaries VALUES(10001,88958,'2002-06-22','9999-01-01');
INSERT INTO salaries VALUES(10002,72527,'2001-08-02','9999-01-01');
INSERT INTO salaries VALUES(10003,43311,'2001-12-01','9999-01-01');
INSERT INTO salaries VALUES(10004,74057,'2001-11-27','9999-01-01');


请你查找薪水排名第二多的员工编号emp_no、薪水salary、last_name以及first_name,不能使用order by完成,
以上例子输出为:
(温馨提示:
sqlite通过的代码不一定能通过mysql,因为SQL语法规定,使用聚合函数时,
select子句中一般只能存在以下三种元素:
常数、聚合函数,group by 指定的列名。
如果使用非group by的列名,sqlite的结果和mysql 可能不一样)


-- 1、查询最大薪资值
SELECT MAX(`salary`) 
FROM `salaries` WHERE 
`to_date` = '9999-01-01'

-- 2、查询第二大的薪资值
SELECT MAX(`salary`)
FROM `salaries` 
WHERE 
	`to_date` = '9999-01-01'
	AND `salary` < (
		SELECT MAX(`salary`) 
		FROM `salaries` 
		WHERE `to_date` = '9999-01-01'
	)

-- 3、联表查询
SELECT
	e.emp_no,
	s.salary,
	e.last_name,
	e.first_name
FROM 
	employees AS e
	LEFT JOIN salaries AS s ON e.emp_no = s.emp_no
WHERE
	s.salary = (
		SELECT MAX(`salary`)
		FROM `salaries` 
		WHERE 
			`to_date` = '9999-01-01'
			AND `salary` < (
				SELECT MAX(`salary`) 
				FROM `salaries` 
				WHERE `to_date` = '9999-01-01'
			)
	)
	

  

 对所有员工的薪水按照salary降序进行1-N的排名

所有员工的薪水按照salary降序先进行1-N的排名,如果salary相同,再按照emp_no升序排列

drop table if exists  `salaries` ; 
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`));
INSERT INTO salaries VALUES(10001,88958,'2002-06-22','9999-01-01');
INSERT INTO salaries VALUES(10002,72527,'2001-08-02','9999-01-01');
INSERT INTO salaries VALUES(10003,43311,'2001-12-01','9999-01-01');
INSERT INTO salaries VALUES(10004,72527,'2001-12-01','9999-01-01');

-- MYSQL8版本支持
SELECT
	emp_no,
	salary,
	dense_rank ( ) over ( ORDER BY salary DESC ) AS rank 
FROM salaries 
WHERE to_date = '9999-01-01' 
ORDER BY rank ASC, emp_no ASC;

-- MYSQL5支持 rank排名:查询表中大于自己薪水的员工的数量(考虑并列:去重)
SELECT
  s1.emp_no,
  s1.salary,
  (SELECT
    COUNT(DISTINCT s2.salary)
  FROM salaries s2
  WHERE 
		s2.to_date = '9999-01-01'
    AND s2.salary >= s1.salary) AS `rank`  -- 去重:计算并列排名
FROM salaries s1
WHERE s1.to_date = '9999-01-01'
ORDER BY 
	s1.salary DESC,
  s1.emp_no ;

  

 

获取所有非manager员工当前的薪水情况

获取所有非manager员工薪水情况,给出dept_no、emp_no以及salary

drop table if exists  `dept_emp` ; 
drop table if exists  `dept_manager` ; 
drop table if exists  `employees` ; 
drop table if exists  `salaries` ; 
CREATE TABLE `dept_emp` (
`emp_no` int(11) NOT NULL,
`dept_no` char(4) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,
PRIMARY KEY (`emp_no`,`dept_no`));
CREATE TABLE `dept_manager` (
`dept_no` char(4) NOT NULL,
`emp_no` int(11) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,
PRIMARY KEY (`emp_no`,`dept_no`));
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`));
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`));
INSERT INTO dept_emp VALUES(10001,'d001','1986-06-26','9999-01-01');
INSERT INTO dept_emp VALUES(10002,'d001','1996-08-03','9999-01-01');
INSERT INTO dept_manager VALUES('d001',10002,'1996-08-03','9999-01-01');
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','1996-08-03');
INSERT INTO salaries VALUES(10001,88958,'1986-06-26','9999-01-01');
INSERT INTO salaries VALUES(10002,72527,'1996-08-03','9999-01-01');

-- 4表连接
SELECT
	D.dept_no,
	E.emp_no,
	S.salary
FROM
	employees AS E
	LEFT JOIN dept_manager AS MGR ON E.emp_no = MGR.emp_no
	LEFT JOIN salaries AS S ON E.emp_no = S.emp_no
	LEFT JOIN dept_emp AS D ON E.emp_no = D.emp_no
WHERE 
	MGR.dept_no IS NULL

  

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

其中bonus类型btype为1其奖金为薪水salary的10%,btype为2其奖金为薪水的20%,其他类型均为薪水的30%。

to_date='9999-01-01'表示当前薪水。

请你给出emp_no、first_name、last_name、奖金类型btype、对应的当前薪水情况salary以及奖金金额bonus。

bonus结果保留一位小数,输出结果按emp_no升序排序。

drop table if exists  `employees` ; 
drop table if exists  emp_bonus; 
drop table if exists  `salaries` ; 
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`));
 create table emp_bonus(
emp_no int not null,
recevied datetime not null,
btype smallint not null);
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`));
insert into emp_bonus values
(10001, '2010-01-01',1),
(10002, '2010-10-01',2);
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 salaries VALUES(10001,60117,'1986-06-26','1987-06-26');
INSERT INTO salaries VALUES(10001,62102,'1987-06-26','1988-06-25');
INSERT INTO salaries VALUES(10001,66074,'1988-06-25','1989-06-25');
INSERT INTO salaries VALUES(10001,66596,'1989-06-25','1990-06-25');
INSERT INTO salaries VALUES(10001,66961,'1990-06-25','1991-06-25');
INSERT INTO salaries VALUES(10001,71046,'1991-06-25','1992-06-24');
INSERT INTO salaries VALUES(10001,74333,'1992-06-24','1993-06-24');
INSERT INTO salaries VALUES(10001,75286,'1993-06-24','1994-06-24');
INSERT INTO salaries VALUES(10001,75994,'1994-06-24','1995-06-24');
INSERT INTO salaries VALUES(10001,76884,'1995-06-24','1996-06-23');
INSERT INTO salaries VALUES(10001,80013,'1996-06-23','1997-06-23');
INSERT INTO salaries VALUES(10001,81025,'1997-06-23','1998-06-23');
INSERT INTO salaries VALUES(10001,81097,'1998-06-23','1999-06-23');
INSERT INTO salaries VALUES(10001,84917,'1999-06-23','2000-06-22');
INSERT INTO salaries VALUES(10001,85112,'2000-06-22','2001-06-22');
INSERT INTO salaries VALUES(10001,85097,'2001-06-22','2002-06-22');
INSERT INTO salaries VALUES(10001,88958,'2002-06-22','9999-01-01');
INSERT INTO salaries VALUES(10002,72527,'1996-08-03','1997-08-03');
INSERT INTO salaries VALUES(10002,72527,'1997-08-03','1998-08-03');
INSERT INTO salaries VALUES(10002,72527,'1998-08-03','1999-08-03');
INSERT INTO salaries VALUES(10002,72527,'1999-08-03','2000-08-02');
INSERT INTO salaries VALUES(10002,72527,'2000-08-02','2001-08-02');
INSERT INTO salaries VALUES(10002,72527,'2001-08-02','9999-01-01');

-- 主要是考察CASE WHEN 语法
SELECT
  E.emp_no,
  E.first_name,
  E.last_name,
  B.btype,
  S.salary,
  (
    CASE B.btype
    WHEN 1 THEN ROUND(S.salary * 0.1, 4)
    WHEN 2 THEN ROUND(S.salary * 0.2, 4)
    ELSE ROUND(S.salary * 0.3, 4)
    END
  ) AS bonus
FROM
  employees AS E
  LEFT JOIN salaries AS S ON E.emp_no = S.emp_no
  LEFT JOIN emp_bonus AS B ON E.emp_no = B.emp_no
WHERE 
  S.to_date = '9999-01-01'
ORDER BY E.emp_no ASC

  

统计salary的累计和running_total

按照salary的累计和running_total,

其中running_total为前N个当前( to_date = '9999-01-01')员工的salary累计和,其他以此类推。

drop table if exists  `salaries` ; 
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`));
INSERT INTO salaries VALUES(10001,60117,'1986-06-26','1987-06-26');
INSERT INTO salaries VALUES(10001,62102,'1987-06-26','1988-06-25');
INSERT INTO salaries VALUES(10001,66074,'1988-06-25','1989-06-25');
INSERT INTO salaries VALUES(10001,66596,'1989-06-25','1990-06-25');
INSERT INTO salaries VALUES(10001,66961,'1990-06-25','1991-06-25');
INSERT INTO salaries VALUES(10001,71046,'1991-06-25','1992-06-24');
INSERT INTO salaries VALUES(10001,74333,'1992-06-24','1993-06-24');
INSERT INTO salaries VALUES(10001,75286,'1993-06-24','1994-06-24');
INSERT INTO salaries VALUES(10001,75994,'1994-06-24','1995-06-24');
INSERT INTO salaries VALUES(10001,76884,'1995-06-24','1996-06-23');
INSERT INTO salaries VALUES(10001,80013,'1996-06-23','1997-06-23');
INSERT INTO salaries VALUES(10001,81025,'1997-06-23','1998-06-23');
INSERT INTO salaries VALUES(10001,81097,'1998-06-23','1999-06-23');
INSERT INTO salaries VALUES(10001,84917,'1999-06-23','2000-06-22');
INSERT INTO salaries VALUES(10001,85112,'2000-06-22','2001-06-22');
INSERT INTO salaries VALUES(10001,85097,'2001-06-22','2002-06-22');
INSERT INTO salaries VALUES(10001,88958,'2002-06-22','9999-01-01');
INSERT INTO salaries VALUES(10002,72527,'1996-08-03','1997-08-03');
INSERT INTO salaries VALUES(10002,72527,'1997-08-03','1998-08-03');
INSERT INTO salaries VALUES(10002,72527,'1998-08-03','1999-08-03');
INSERT INTO salaries VALUES(10002,72527,'1999-08-03','2000-08-02');
INSERT INTO salaries VALUES(10002,72527,'2000-08-02','2001-08-02');
INSERT INTO salaries VALUES(10002,72527,'2001-08-02','9999-01-01');
INSERT INTO salaries VALUES(10003,40006,'1995-12-03','1996-12-02');
INSERT INTO salaries VALUES(10003,43616,'1996-12-02','1997-12-02');
INSERT INTO salaries VALUES(10003,43466,'1997-12-02','1998-12-02');
INSERT INTO salaries VALUES(10003,43636,'1998-12-02','1999-12-02');
INSERT INTO salaries VALUES(10003,43478,'1999-12-02','2000-12-01');
INSERT INTO salaries VALUES(10003,43699,'2000-12-01','2001-12-01');
INSERT INTO salaries VALUES(10003,43311,'2001-12-01','9999-01-01');

-- 查询running_total为前N个当前( to_date = '9999-01-01')员工的salary累计
SELECT
	s1.emp_no,
	s1.salary,
	( SELECT SUM( s2.salary ) FROM salaries AS s2 WHERE s2.emp_no <= s1.emp_no AND s2.to_date = '9999-01-01' ) AS running_total 
FROM salaries AS s1 
WHERE s1.to_date = '9999-01-01' 
ORDER BY s1.emp_no

  

给出employees表中排名为奇数行的first_name

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(10005,'1955-01-21','Kyoichi','Maliniak','M','1989-09-12');
INSERT INTO employees VALUES(10006,'1953-04-20','Anneke','Preusig','F','1989-06-02');

-- 主要实现SQL1 - MYSQL8开窗函数实现
SELECT 
  first_name, 
  row_number ( ) over ( ORDER BY first_name ) AS rank_num 
FROM employees 

-- 主要实现SQL1 - 不使用MYSQL8开窗函数,设置变量实现
SET @orderId = 0;
SELECT 
  first_name, 
  @orderId:= @orderId + 1 AS `rank` 
FROM `employees` 
ORDER BY first_name

-- 解决SQL:
SELECT E.first_name
FROM 
	employees AS E JOIN
	(
		-- 上面两种SQL任选一种作为联表查询
	) AS E2 ON E.first_name = E2.first_name
WHERE E2.`rank`  % 2 = 1;

-- 直接子查询嵌套会因为不是原表顺序,答案不符合
SELECT
    e.first_name
FROM employees e JOIN
(
    SELECT 
        first_name, 
        ROW_NUMBER() OVER(ORDER BY first_name ASC) AS  r_num
    FROM employees
) AS t 
ON e.first_name = t.first_name
WHERE t.r_num % 2 = 1;

  

异常的邮件概率

 每一个日期里面,正常用户发送给正常用户邮件失败的概率是多少,

结果保留到小数点后面3位(3位之后的四舍五入),并且按照日期升序排序

drop table if exists email;
drop table if exists user;
CREATE TABLE `email` (
`id` int(4) NOT NULL,
`send_id` int(4) NOT NULL,
`receive_id` int(4) NOT NULL,
`type` varchar(32) NOT NULL,
`date` date NOT NULL,
PRIMARY KEY (`id`));

CREATE TABLE `user` (
`id` int(4) NOT NULL,
`is_blacklist` int(4) NOT NULL,
PRIMARY KEY (`id`));

INSERT INTO email VALUES
(1,2,3,'completed','2020-01-11'),
(2,1,3,'completed','2020-01-11'),
(3,1,4,'no_completed','2020-01-11'),
(4,3,1,'completed','2020-01-12'),
(5,3,4,'completed','2020-01-12'),
(6,4,1,'completed','2020-01-12');

INSERT INTO user VALUES
(1,0),
(2,1),
(3,0),
(4,0);


-- 1、联表实现筛选白名单用户
SELECT
	`email`.* 
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 )

-- 2、子查询实现筛选
-- 查询黑名单用户,或者正常用户	
SELECT id FROM `user` WHERE `is_blacklist` = 1

-- 在EMAIL表中筛选正常用户来往的邮件记录
SELECT
	`email`.* 
FROM
	`email`
WHERE 
	email.send_id NOT IN (SELECT id FROM `user` WHERE `is_blacklist` = 1)
	AND email.receive_id NOT IN (SELECT id FROM `user` WHERE `is_blacklist` = 1)
	
-- 3、按日期分组进行筛选,直接计数异常次数和总次数即可实现
SELECT
	`email`.date,
	SUM(`email`.type = 'no_completed' ) AS `异常次数`,
	COUNT(`email`.type) AS `总计次数`,
	ROUND (SUM(`email`.type = 'no_completed' ) * 1.0 / COUNT(`email`.type), 3 )AS `概率`
FROM
	`email`
WHERE 
	email.send_id NOT IN (SELECT id FROM `user` WHERE `is_blacklist` = 1)
	AND email.receive_id NOT IN (SELECT id FROM `user` WHERE `is_blacklist` = 1)
GROUP BY `email`.date 
ORDER BY `email`.date 

-- 最终SQL
SELECT
	`email`.date,
	ROUND (SUM(`email`.type = 'no_completed' ) * 1.0 / COUNT(`email`.type), 3 )AS `p`
FROM
	`email`
WHERE 
	email.send_id NOT IN (SELECT id FROM `user` WHERE `is_blacklist` = 1)
	AND email.receive_id NOT IN (SELECT id FROM `user` WHERE `is_blacklist` = 1)
GROUP BY `email`.date 
ORDER BY `email`.date 

-- 或者按非异常次数查询
SELECT
	`email`.date,
	SUM( CASE `email`.type WHEN 'completed' THEN 0 ELSE 1 END ) AS `总计`,
	COUNT(`email`.type) AS `次数`,
	ROUND (SUM( CASE `email`.type WHEN 'completed' THEN 0 ELSE 1 END ) * 1.0 / COUNT(`email`.type), 3 )AS `概率`
FROM
	`email`
WHERE 
	email.send_id NOT IN (SELECT id FROM `user` WHERE `is_blacklist` = 1)
	AND email.receive_id NOT IN (SELECT id FROM `user` WHERE `is_blacklist` = 1)
GROUP BY `email`.date 
ORDER BY `email`.date 

  

 

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

查询每个用户最近一天登录的日子,用户的名字,以及用户用的设备的名字,

并且查询结果按照user的name升序排序

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,2,2,'2020-10-13'),
(4,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');


-- 全部连接起来筛选最近日期
SELECT
	U.`name`,
	C.`name` AS `client`,
	L.`date`
FROM 
	`user` AS U
	JOIN login AS L ON U.id = L.user_id
	JOIN client AS C ON L.client_id = C.id
WHERE 
	L.`date` = (SELECT MAX(date) FROM login)
ORDER BY U.`name` ASC

  

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

 查询新登录用户次日成功的留存率,即第1天登陆之后,第2天再次登陆的概率,保存小数点后面3位(3位之后的四舍五入)

drop table if exists login;
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`));

INSERT INTO login VALUES
(1,2,1,'2020-10-12'),
(2,3,2,'2020-10-12'),
(3,1,2,'2020-10-12'),
(4,2,2,'2020-10-13'),
(5,4,1,'2020-10-13'),
(6,1,2,'2020-10-13'),
(7,1,2,'2020-10-14');

SELECT
	round( count( DISTINCT user_id ) * 1.0 / ( SELECT count( DISTINCT user_id ) FROM login ), 3 ) AS `p`
FROM
	login 
WHERE
	( user_id, date ) IN ( 
		SELECT user_id, DATE_ADD( min( date ), INTERVAL 1 DAY ) 
		FROM login 
		GROUP BY user_id
	);

  

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

查询每个日期登录新用户个数,并且查询结果按照日期升序排序

drop table if exists login;
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`));

INSERT INTO login VALUES
(1,2,1,'2020-10-12'),
(2,3,2,'2020-10-12'),
(3,1,2,'2020-10-12'),
(4,2,2,'2020-10-13'),
(5,1,2,'2020-10-13'),
(6,3,1,'2020-10-14'),
(7,4,1,'2020-10-14'),
(8,4,1,'2020-10-15');

-- 拿到首次登录用户和时间
SELECT `user_id`, MIN( `date` ) 
FROM `login` 
GROUP BY user_id

-- 1、可以对Login表的日期分组,用户和日期可以用IN和上述查询结果进行匹配处理
-- 2、如果匹配成功,表示这个用户是在这个日期是首次登录
-- 3、ELSE NULL 不能写 ELSE 0,因为0就会算一条记录,NULL则不被COUNT函数计数
SELECT
	`date`,
	COUNT( 
		DISTINCT
		CASE
			WHEN ( user_id, date ) IN ( SELECT user_id, min( date ) FROM login GROUP BY user_id ) THEN `user_id`
			ELSE NULL 
		END 
	) AS `new`
FROM login 
GROUP BY `date` 
ORDER BY `date` ASC;

  

 

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

查询刷题信息,包括: 用户的名字,以及截止到某天,累计总共通过了多少题,

并且查询结果先按照日期升序排序,再按照姓名升序排序,有登录却没有刷题的哪一天的数据不需要输出

drop table if exists login;
drop table if exists passing_number;
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 `passing_number` (
`id` int(4) NOT NULL,
`user_id` int(4) NOT NULL,
`number` 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`));

INSERT INTO login VALUES
(1,2,1,'2020-10-12'),
(2,3,2,'2020-10-12'),
(3,1,2,'2020-10-12'),
(4,1,3,'2020-10-13'),
(5,3,2,'2020-10-13');

INSERT INTO passing_number VALUES
(1,2,4,'2020-10-12'),
(2,3,1,'2020-10-12'),
(3,1,0,'2020-10-13'),
(4,3,2,'2020-10-13');

INSERT INTO user VALUES
(1,'tm'),
(2,'fh'),
(3,'wangchao');

-- 1、使用开窗函数
SELECT 
  `name` AS `u_n`,
  `date`,
  SUM(number) OVER (PARTITION BY `user_id` ORDER BY date) AS `ps_num`
FROM 
  `passing_number` AS `p`
  INNER JOIN `user` ON `user`.`id` = `p`.`user_id`
GROUP BY `date`, `u_n`
ORDER BY `date`, `name`

-- 2、自连接实现
SELECT `name` AS u_n,date, p.ps_num
FROM 
	(
		SELECT 
			p1.user_id,
			p1.date,
			SUM(p2.number) AS ps_num
		FROM 
			passing_number AS p1,
			passing_number AS p2
		WHERE 
			p1.date >= p2.date
			AND p1.user_id = p2.user_id
		GROUP BY p1.date,p1.user_id
	) AS p
	JOIN `user` ON `user`.id = p.`user_id`
ORDER BY p.date, `user`.`name`

  

 

 考试分数(三)

 找出每个岗位分数排名前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');


SELECT
	g1.id,
	l.NAME,
	g1.score 
FROM
	grade g1
	JOIN `language` l ON g1.language_id = l.id 
WHERE
	(
		SELECT
			count( DISTINCT g2.score ) 
		FROM
			grade g2 
		WHERE
			g2.score >= g1.score 
			AND g1.language_id = g2.language_id 
	) <= 2 
ORDER BY l.NAME, g1.score DESC, g1.id;

 

 考试分数(四)

查询各个岗位分数升序排列之后的中位数位置的范围,并且按job升序排序

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);

-- 题解
SELECT job,
FLOOR( (COUNT(1) + 1) / 2) AS 'start',
CEIL( (COUNT(1) + 1) / 2) AS 'end'
FROM grade
GROUP BY job
ORDER BY job;
+------+-------+-----+
| job | start | end |
+------+-------+-----+
| B | 2 | 2 |
| C++ | 2 | 2 |
| Java | 1 | 2 |
+------+-------+-----+
3 rows in set (0.05 sec)

  

牛客的课程订单分析(四)

查询在2025-10-15以后,如果有一个用户下单2个以及2个以上状态为购买成功的C++课程或Java课程或Python课程,那么输出这个用户的user_id,以及满足前面条件的第一次购买成功的C++课程或Java课程或Python课程的日期first_buy_date,以及所有日期里购买成功的C++课程或Java课程或Python课程的次数cnt,并且输出结果按照user_id升序排序

drop table if exists order_info;
CREATE TABLE order_info (
id int(4) NOT NULL,
user_id int(11) NOT NULL,
product_name varchar(256) NOT NULL,
status varchar(32) NOT NULL,
client_id int(4) NOT NULL,
date date NOT NULL,
PRIMARY KEY (id));

INSERT INTO order_info VALUES
(1,557336,'C++','no_completed',1,'2025-10-10'),
(2,230173543,'Python','completed',2,'2025-10-12'),
(3,57,'JS','completed',3,'2025-10-23'),
(4,57,'C++','completed',3,'2025-10-23'),
(5,557336,'Java','completed',1,'2025-10-23'),
(6,57,'Java','completed',1,'2025-10-24'),
(7,557336,'C++','completed',1,'2025-10-25'),
(8,557336,'Python','completed',1,'2025-10-25');


-- 直接分组之后筛选对应条件,取MIN(date) 和 COUNT(product_name)
SELECT 
	user_id, 
	MIN(date) AS `first_buy_date`, 
	COUNT(product_name) AS `cnt`
FROM order_info 
WHERE
	date > '2025-10-15' 
	AND STATUS = 'completed' 
	AND product_name IN ( 'C++', 'Java', 'Python' ) 
GROUP BY user_id 
HAVING COUNT( user_id ) > 1 ;
+---------+----------------+-----+
| user_id | first_buy_date | cnt |
+---------+----------------+-----+
|      57 | 2025-10-23     |   2 |
|  557336 | 2025-10-23     |   3 |
+---------+----------------+-----+
2 rows in set (0.05 sec)

  

 牛客的课程订单分析(七)

查询在2025-10-15以后,同一个用户下单2个以及2个以上状态为购买成功的C++课程或Java课程或Python课程的来源信息,

第一列是显示的是客户端名字,如果是拼团订单则显示GroupBuy,第二列显示这个客户端(或者是拼团订单)有多少订单,

最后结果按照第一列(source)升序排序

drop table if exists order_info;
drop table if exists client;
CREATE TABLE order_info (
id int(4) NOT NULL,
user_id int(11) NOT NULL,
product_name varchar(256) NOT NULL,
status varchar(32) NOT NULL,
client_id int(4) NOT NULL,
date date NOT NULL,
is_group_buy 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 order_info VALUES
(1,557336,'C++','no_completed',1,'2025-10-10','No'),
(2,230173543,'Python','completed',2,'2025-10-12','No'),
(3,57,'JS','completed',0,'2025-10-23','Yes'),
(4,57,'C++','completed',3,'2025-10-23','No'),
(5,557336,'Java','completed',0,'2025-10-23','Yes'),
(6,57,'Java','completed',1,'2025-10-24','No'),
(7,557336,'C++','completed',0,'2025-10-25','Yes');

INSERT INTO client VALUES
(1,'PC'),
(2,'Android'),
(3,'IOS'),
(4,'H5');

--
SELECT 
	IF(a.client_id = 0,'GroupBuy',c.name) AS source,
	COUNT(*) AS cnt
FROM (
		SELECT client_id 
		FROM order_info 
		WHERE
			date > '2025-10-15' 
			AND STATUS = 'completed' 
			AND product_name IN ( 'C++', 'Java', 'Python' ) 
			AND user_id IN ( 
				SELECT user_id 
				FROM order_info 
				WHERE STATUS = 'completed' 
				GROUP BY user_id 
				HAVING COUNT( * ) >= 2 
			)
	) AS a
	LEFT JOIN client c ON a.client_id = c.id
 GROUP BY a.client_id
 ORDER BY source

  

 

最差是第几名(二)

查询一下,如果只有1个中位数,输出1个,如果有2个中位数,按grade升序输出

drop table if exists class_grade;
CREATE TABLE class_grade (
grade varchar(32) NOT NULL,
number int(4) NOT NULL
);

INSERT INTO class_grade VALUES
('A',2),
('C',4),
('B',4),
('D',2);

-- 题解
SELECT
	grade 
FROM
	(
		SELECT
			grade,
			( SELECT SUM(number) FROM class_grade c1 WHERE c1.grade < c2.grade ) AS s,
			( SELECT SUM(number) FROM class_grade c1 WHERE c1.grade <= c2.grade ) AS e 
		FROM class_grade c2 
		ORDER BY grade
	) AS t1,
	( 
		SELECT sum( number ) AS sums 
		FROM class_grade 
	) AS t2 
WHERE
	( sums / 2 ) BETWEEN s AND e

  

获得积分最多的人

 查找积分增加最高的用户的id(可能有多个),名字,以及他的总积分是多少,查询结果按照id升序排序

drop table if exists user;
drop table if exists grade_info;

CREATE TABLE user (
id  int(4) NOT NULL,
name varchar(32) NOT NULL
);

CREATE TABLE grade_info (
user_id  int(4) NOT NULL,
grade_num int(4) NOT NULL,
type varchar(32) NOT NULL
);

INSERT INTO user VALUES
(1,'tm'),
(2,'wwy'),
(3,'zk'),
(4,'qq'),
(5,'lm');

INSERT INTO grade_info VALUES
(1,3,'add'),
(2,3,'add'),
(1,1,'add'),
(3,3,'add'),
(4,3,'add'),
(5,3,'add'),
(3,1,'add'); 

-- 1、获取所有人积分
SELECT `user_id`, SUM(`grade_num`) AS `total`
FROM 
	grade_info
WHERE `type` = 'add'
GROUP BY `user_id`
ORDER BY `total` DESC

-- 2、获取积分最大值
SELECT SUM(`grade_num`) AS `grade_sum` 
FROM grade_info 
WHERE `type` = 'add'
GROUP BY user_id 
ORDER BY `grade_sum` DESC LIMIT 1

-- 3、子查询筛选符合的用户
SELECT `user_id`, SUM(`grade_num`) AS `total`
FROM grade_info
WHERE `type` = 'add'
GROUP BY `user_id`
HAVING `total` = (
	SELECT SUM(`grade_num`) AS `grade_sum` 
	FROM grade_info 
	WHERE `type` = 'add'
	GROUP BY user_id 
	ORDER BY `grade_sum` DESC LIMIT 1
)
ORDER BY `total` DESC, `user_id` ASC

-- 最终解题SQL
SELECT 
	`user`.`name`,
	`main`.`total`
FROM
	(
		SELECT `user_id`, SUM(`grade_num`) AS `total`
		FROM grade_info
		WHERE `type` = 'add'
		GROUP BY `user_id`
		HAVING `total` = (
			SELECT SUM(`grade_num`) AS `grade_sum` 
			FROM grade_info 
			WHERE `type` = 'add'
			GROUP BY user_id 
			ORDER BY `grade_sum` DESC LIMIT 1
		)
		ORDER BY `total` DESC, `user_id` ASC
	) AS `main`
	JOIN `user` ON `main`.`user_id` = `user`.`id`

  

网易云音乐推荐(网易校招笔试真题)

查询向user_id = 1 的用户,推荐其关注的人喜欢的音乐。
不要推荐该用户已经喜欢的音乐,并且按music的id升序排列。你返回的结果中不应当包含重复项

CREATE TABLE `follow` (
`user_id` int(4) NOT NULL,
`follower_id` int(4) NOT NULL,
PRIMARY KEY (`user_id`,`follower_id`));

CREATE TABLE `music_likes` (
`user_id` int(4) NOT NULL,
`music_id` int(4) NOT NULL,
PRIMARY KEY (`user_id`,`music_id`));

CREATE TABLE `music` (
`id` int(4) NOT NULL,
`music_name` varchar(32) NOT NULL,
PRIMARY KEY (`id`));

INSERT INTO follow VALUES(1,2);
INSERT INTO follow VALUES(1,4);
INSERT INTO follow VALUES(2,3);

INSERT INTO music_likes VALUES(1,17);
INSERT INTO music_likes VALUES(2,18);
INSERT INTO music_likes VALUES(2,19);
INSERT INTO music_likes VALUES(3,20);
INSERT INTO music_likes VALUES(4,17);

INSERT INTO music VALUES(17,'yueyawang');
INSERT INTO music VALUES(18,'kong');
INSERT INTO music VALUES(19,'MOM');
INSERT INTO music VALUES(20,'Sold Out');


-- ID为1的用户的关注人
SELECT `follower_id`
FROM follow 
WHERE `user_id` = 1

-- ID为1的用户 喜欢的音乐
SELECT music_id
FROM music_likes 
WHERE `user_id` = 1

-- 关注的人喜欢的音乐
SELECT music_id
FROM music_likes 
WHERE `user_id` IN (
	SELECT `follower_id`
	FROM follow 
	WHERE `user_id` = 1
)

-- 去掉用户自己的音乐, 就是题目需要的歌曲了
SELECT music_id
FROM music_likes 
WHERE 
`user_id` IN (
	SELECT `follower_id`
	FROM follow 
	WHERE `user_id` = 1
) 
AND music_id NOT IN (
	SELECT music_id
	FROM music_likes 
	WHERE `user_id` = 1
)

-- 联表得出最终结果
SELECT music.`music_name`
FROM 
(
	SELECT music_id
	FROM music_likes 
	WHERE 
	`user_id` IN (
		SELECT `follower_id`
		FROM follow 
		WHERE `user_id` = 1
	) 
	AND music_id NOT IN (
		SELECT music_id
		FROM music_likes 
		WHERE `user_id` = 1
	)
) AS `main`
JOIN `music` ON `main`.music_id = `music`.id
ORDER BY `music`.id ASC

  

 

 

 

 

 

 

 

 

posted @ 2022-06-07 15:18  emdzz  阅读(43)  评论(0编辑  收藏  举报