MySQL刷题笔记

MySQL刷题笔记-2021-10-28

题目一: 现在运营想要分别查看学校为山东大学或者性别为男性的用户的device_id、gender、age和gpa数据,请取出相应结果,结果不去重。

表: user_profile
在这里插入图片描述
要求输出:
在这里插入图片描述

分析: 本题要求按大学条件和性别条件在同一张表中取出指定字段时不去重,因此不能直接进行提取,需要用到union函数。

思路: 根据两个条件分别进行查询,然后使用union all来进行合并。

SQL:

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

总结: 本题考察对于union函数的使用,当遇到需要按多个条件查询结果且不去重的时候可以使用union all。

题目二: 运营想要查看参加了答题的山东大学的用户在不同难度下的平均答题题目数,请取出相应数据。

表1: user_profile
在这里插入图片描述
表2: question_practice_detail
在这里插入图片描述
表3: question_detail
在这里插入图片描述
要求输出:
在这里插入图片描述

分析: 本题涉及三张表,其中university字段在user_profile中,平均答题题目数需要用到question_practice_detail中的question_id(题目总数)和device_id(用户总数)字段,题目的难度需要用到question_detail表,最后还要考虑按照学校和难度进行分组,因此这题考察的是一个多表连接。

思路: 首先根据device_id将user_profile和question_practice_detail进行连接,因为后面会按照学校进行Group by,所以可以直接利用question_id和device_id的商求出各学校用户平均答题数量;将question_practice_detail和question_detail进行连接,后面按照difficult_level进行分组,就可以获得需要的信息。

SQL:

SELECT 
	u.university,q2.difficult_level,COUNT(q1.question_id) / COUNT(DISTINCT q1.device_id)
FROM user_profile u
LEFT JOIN
question_practice_detail q1
ON u.device_id = q1.device_id
RIGHT JOIN
question_detail q2
ON q1.question_id = q2.question_id
GROUP BY
u.university,q2.difficult_level
HAVING university='山东大学';

总结: 在遇到多表之间的连接时,要一个一个进行JOIN,而不是放在同一个JOIN函数里进行。

题目三: 现在运营想要将用户划分为25岁以下和25岁及以上两个年龄段,分别查看这两个年龄段用户数量(age为NULL的样本也算作25岁以下)。

表: user_profile
在这里插入图片描述
要求输出:
在这里插入图片描述
分析: 该题首先需要新加一列age_cut来表示该用户的年龄是否大于25岁,然后还要在遇到NULL值的时候将其判断为小于25岁。

思路: 使用IF和IFNULL函数。

SQL:

SELECT u.age_cut, COUNT(*)
FROM 
    (SELECT IF(IFNULL(age,0)>=25,'25岁及以上','25岁以下') as age_cut FROM user_profile) u
GROUP BY u.age_cut;

总结: IF(目标字段,‘命名1’,‘命名2’),第二个命名就是ELSE结果的命名;IFNULL(字段名,赋值)。

题目四: 现在运营想要将用户划分为20岁以下,20-24岁,25岁及以上三个年龄段,分别查看不同年龄段用户的明细情况,请取出相应数据。(注:若年龄为空请返回其他。)

表: user_profile
在这里插入图片描述
要求输出:
在这里插入图片描述
分析: 该题有多个条件划分,可以嵌套多个IF,但是用case函数更好。

思路: 使用case函数进行划分

SQL:

SELECT 
    device_id,gender,
    case when age<20 then '20岁以下'
         when age between 20 and 24 then '20-24岁'
         when age>=25 then '25岁及以上'
       else '其他' end age_cut
FROM user_profile;

总结: 当2个以上条件划分的时候要用case,语法为:
case when 条件1 then 输出1 when 条件2 then 输出2 when 条件3 then 输出3… end

题目五: 现在运营想要计算出2021年8月每天用户练习题目的数量,请取出相应数据。

表: question_practice_detail
在这里插入图片描述
要求输出:
在这里插入图片描述
分析: 本题考察日期函数的应用,涉及到月份的提取,可以使用DAY、YEAR、MONTH函数来单独进行提取。

思路: 使用DAY和MONTH函数提取日期函数指定位置的值。

SQL:

SELECT 
    DAY(date) AS day, COUNT(question_id) AS question_cnt
FROM question_practice_detail
WHERE MONTH(date) = '08'
GROUP BY date;

总结: 在遇到日期字段的时候,要求提取日期中的年份、月份以及日可以使用YEAR、MONTH、DAY函数,也可以使用DATE_FORMAT(date,"%Y-%m")=“2021-08”。

题目六: 现在运营想要查看用户在某天刷题后第二天还会再来刷题的平均概率。请你取出相应数据。

表: question_practice_detail
在这里插入图片描述
要求输出:
在这里插入图片描述
分析: 首先要搞清楚如何计算用户某天刷题后,第二天再次来刷题的概率(即:第一天刷题且第二天刷题的总人数/第一天刷题的总人数);其次,要注意一个用户可能在一天内会多次刷题,因此要先进行去重。

思路: 首先在选择device_id的时候要进行去重,可以利用date_add(date, interval 1 day)与date连接来筛选出第二天还来刷题的用户,接着即可计算;还可以使用lead函数。

SQL:

# 解法1
SELECT 
    COUNT(q2.datee) / COUNT(q1.date) AS avg_ret
FROM 
    (SELECT distinct device_id, date 
     FROM question_practice_detail) q1
LEFT JOIN
    (SELECT distinct device_id, date_add(date, INTERVAL 1 day) AS datee 
     FROM question_practice_detail) q2
ON q1.device_id = q2.device_id and q1.date = q2.datee;

# 解法2
SELECT AVG(IF(datediff(date2,date1)=1,1,0))
FROM
    (SELECT 
        DISTINCT device_id, date as date1, lead(date,1,NULL) over (PARTITION BY device_id ORDER BY date) AS date2
    FROM 
        (SELECT DISTINCT device_id, date  FROM question_practice_detail) AS q1 ) AS q2; 

总结: 统计一个用户第一天来且第二天还来,记得要去重;datediff(date2,date1)就是计算两个日期之间的差距;lead函数:lead(字段,n,”null值如何填充“) over (PARTITION BY 分组字段 ORDER BY 排序字段); date_add(date,inverval n day/hour) 加时间。

题目七: 现在运营举办了一场比赛,收到了一些参赛申请,表数据记录形式如下所示,现在运营想要统计每个性别的用户分别有多少参赛者,请取出相应结果

表: user_submit
在这里插入图片描述
要求结果:
在这里插入图片描述
分析: 本题考点是如何从用特殊字符分割的字符串提取指定字符,可以使用LIKE函数、SUBSTRING_INDEX(字段,分割符,位置)

SQL:

# 解法1
SELECT 
    SUBSTRING_INDEX(profile,',',-1)  AS gender, COUNT(device_id) AS number
FROM user_submit
GROUP BY gender;

# 解法2
SELECT 
	IF(profile LIKE '%male', 'male','female') AS gender, COUNT(device_id) AS number
FROM user_submit
GROUP BY gender;

总结: SUBSTRING函数可以提取从指定位置开始后面的所有字符(SUBSTRING(profile,n));SUBSTRING_INDEX(profile, ‘,’,n):按照逗号进行分割,提取最后一组元素。

题目七: 现在运营想要找到每个学校gpa最低的同学来做调研,请你取出每个学校的最低gpa

表: user_profile
在这里插入图片描述
要求输出:
在这里插入图片描述
分析: 本题如果直接用MIN函数会丢失最低gpa学生对应的device_id,可以使用row_number()这个窗口函数

思路: 使用row_number窗口函数,以university作为partition字段,然后根据gpa进行升序排序,这样每个学校gpa最低的row_number就会都是1,加个WHERE条件即可。

SQL:

SELECT device_id,university,gpa
FROM ( 
    SELECT 
        *, ROW_NUMBER() OVER (PARTITION BY university ORDER BY gpa) as rn
    FROM 
        user_profile) u1
WHERE u1.rn = 1;

总结: row_number() over (partition by 分组字段 order by 排序字段) as rn可以根据字段进行分组排序。

题目八: 创建一个actor表,包含如下列信息
在这里插入图片描述

DROP TABLE IF EXISTS actor;
CREATE TABLE actor(
                    actor_id smallint(5) PRIMARY KEY,
                    first_name varchar(45) NOT NULL,
                    last_name varchar(45) NOT NULL,
                    last_update date NOT NULL);

题目八: 请你对于表actor批量插入如下数据(不能有2条insert语句哦!)

INSERT INTO actor(
					actor_id,
					first_name,
					last_name,
					last_update)
VALUES(1,'PENELOPE','GUINESS','2006-02-15 12:34;33'),
	  (2,'NICK','WAHLBERG','2006-02-15 12:34;33');

题目九: 对于表actor插入如下数据,如果数据已经存在,请忽略(不支持使用replace操作)
在这里插入图片描述
mysql中常用的三种插入数据的语句:

  1. insert into表示插入数据,数据库会检查主键,如果出现重复会报错;
  2. replace into表示插入替换数据,需求表中有PrimaryKey,或者unique索引,如果数据库已经存在数据,则用新数据替换,如果没有数据效果则和insert into一样;
  3. insert ignore表示,如果中已经存在相同的记录,则忽略当前新数据;
    insert ignore into actor values(“3”,“ED”,“CHASE”,“2006-02-15 12:34:33”);
INSERT IGNORE INTO actor VALUES("3","ED","CHASE","2006-02-15 12:34:33");

题目十: 请你创建一个actor_name表,并且将actor表中的所有first_name以及last_name导入该表.
actor_name表结构如下,题目最后会查询actor_name表里面的数据来对比结果输出:
在这里插入图片描述

CREATE TABLE IF NOT EXISTS 
    actor_name(
    first_name VARCHAR(45) NOT NULL,
    last_name VARCHAR(45) NOT NULL)
    SELECT first_name,last_name FROM actor;

题目十一: 针对如下表actor结构创建索引:
(注:在 SQLite 中,除了重命名表和在已有的表中添加列,ALTER TABLE 命令不支持其他操作,mysql支持ALTER TABLE创建索引)

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  datetime NOT NULL);
  1. 添加主键
    ALTER TABLE tbl_name ADD PRIMARY KEY (col_list);
    // 该语句添加一个主键,这意味着索引值必须是唯一的,且不能为NULL。

  2. 添加唯一索引
    ALTER TABLE tbl_name ADD UNIQUE index_name (col_list);
    // 这条语句创建索引的值必须是唯一的。

  3. 添加普通索引
    ALTER TABLE tbl_name ADD INDEX index_name (col_list);
    // 添加普通索引,索引值可出现多次。

  4. 添加全文索引
    ALTER TABLE tbl_name ADD FULLTEXT index_name (col_list);
    // 该语句指定了索引为 FULLTEXT ,用于全文索引。

  5. 删除索引
    DROP INDEX index_name ON tbl_name;
    // 或者
    ALTER TABLE tbl_name DROP INDEX index_name;
    ALTER TABLE tbl_name DROP PRIMARY KEY;

题目十二: 针对actor表创建视图actor_name_view,只包含first_name以及last_name两列,并对这两列重新命名,first_name为first_name_v,last_name修改为last_name_v:

CREATE VIEW 
    actor_name_view 
    AS 
    SELECT first_name AS first_name_v, last_name AS last_name_v FROM actor;

题目十三: 针对salaries表emp_no字段创建索引idx_emp_no,查询emp_no为10005,使用强制索引。

SELECT *
FROM salaries
FORCE INDEX (idx_emp_no)
WHERE emp_no = 10005;

题目十四: 现在在last_update后面新增加一列名字为create_date, 类型为datetime, NOT NULL,默认值为’2020-10-01 00:00:00’

ALTER TABLE actor ADD COLUMN (create_date datetime NOT NULL DEFAULT '2020-10-01 00:00:00' );

题目十五: 构造一个触发器audit_log,在向employees_test表中插入一条数据的时候,触发插入相关的数据到audit中。

相关知识点:
SQL中创建触发器的语法如下:

CREATE TRIGGER trigger_name
trigger_time triggert_event ON table_name
FOR EACH ROW
BEGIN
	trigger_syst;
END

其中,

  • trigger_name为自定义的触发器名称;
  • trigger_time为触发的时间(BEFORE/AFTER);
  • trigger_event(以什么事件为触发点:UPDATE/INSERT/DELETE);
  • table_name(表名)
  • trigger_syst为触发器程序体,可以是一句SQL语句,或者用 BEGIN 和 END 包含的多条语句,每条语句结束要分号结尾。

【NEW 与 OLD 详解】
MySQL 中定义了 NEW 和 OLD,用来表示触发器的所在表中,触发了触发器的那一行数据。
具体地:

  • 在 INSERT 型触发器中,NEW 用来表示将要(BEFORE)或已经(AFTER)插入的新数据;
  • 在 UPDATE 型触发器中,OLD 用来表示将要或已经被修改的原数据,NEW 用来表示将要或已经修改为的新数据;
  • 在 DELETE 型触发器中,OLD 用来表示将要或已经被删除的原数据;
    使用方法: NEW.columnName (columnName 为相应数据表某一列名)

SQL:

CREATE TRIGGER audit_log
AFTER INSERT ON employees_test
FOR EACH ROW
BEGIN
	INSERT INTO audit (new.id,new.name);
END

题目十六: 删除emp_no重复的记录,只保留最小的id对应的记录。

相关知识点: SQL中删除指定数据的语法:

DELETE FROM table_name
WHERE condition

注:不能在同一张表中一边子查询一遍删除,所以一般会进行重命名。

DELETE FROM titles_test
WHERE id NOT IN(
	SELECT * FROM (SELECT MIN(id) FROM titles_test GROUP BY emp_no) a);

题目十七: 将所有to_date为9999-01-01的全部更新为NULL,且 from_date更新为2001-01-01

相关知识点: SQL中更新的语法为

UPDATE table_name SET col_name = new_value WHERE col_name = ...;

SQL:

UPDATE titles_test SET to_date = NULL WHERE to_date = '9999-01-01';
UPDATE titles_test SET from_date = '2001-01-01';

题目十八: 将id=5以及emp_no=10001的行数据替换成id=5以及emp_no=10005,其他数据保持不变,使用replace实现,直接使用update会报错。

相关知识点: 本题考察对某一条数据的某个值进行修改,可以使用UPDATE、Replace或INSERT方法。

SQL:

#方法一
REPLACE INTO titles_test VALUE(5,10005,'Senior Engineer','1986-06-26','9999-01-01');
#方法二
UPDATE titles_test SET emp_no = REPLACE(emp_no,10001,10005) WHERE id=5;
#方法三
INSERT INTO titles_test VALUES(5,10005,'Senior Engineer','1986-06-26','9999-01-01')
ON DUPLICATE KEY UPDATE emp_no = 10005;

题目十九: 将titles_test表名修改为titles_2017

相关知识点:

  • ALTER TABLE 表名 ADD 列名/索引/主键/外键等;
  • ALTER TABLE 表名 DROP 列名/索引/主键/外键等;
  • ALTER TABLE 表名 ALTER 仅用来改变某列的默认值;
  • ALTER TABLE 表名 RENAME 列名/索引名 TO 新的列名/新索引名;
  • ALTER TABLE 表名 RENAME TO/AS 新表名;
  • ALTER TABLE 表名 MODIFY 列的定义但不改变列名;
  • ALTER TABLE 表名 CHANGE 列名和定义都可以改变。

SQL:

ALTER TABLE titles_test RENAME AS titles_2017;

题目二十: 在audit表上创建外键约束,其emp_no对应employees_test表的主键id。

相关知识点: 为表插入外键的语法

ALTER TABLE table_name1
ADD CONSTRAINT FOREIGN KEY (col1)
REFERENCES table_name2 (col2);
ALTER TABLE audit ADD
CONSTRAINT FOREIGN KEY (emp_no)
REFERENCES employees_test (id);

题目二十一: 查找字符串’10,A,B’ 中逗号’,'出现的次数cnt。
在这里插入图片描述

SELECT LENGTH('10,A,B')-LENGTH(REPLACE('10,A,B',',','')) AS cnt;

题目二十二: 按照dept_no进行汇总,属于同一个部门的emp_no按照逗号进行连接,结果给出dept_no以及连接出的结果employees。

思路: 本题使用GROUP_CONCAT函数。

SQL:

SELECT 
    dept_no, GROUP_CONCAT(emp_no) AS employees
FROM dept_emp
GROUP BY dept_no;

题目二十三: 分页查询,5行一页,显示第二页

相关知识点: n行一页就是LIMIT n,第二页就是n开始,注意最后不加分号

SQL:

SELECT *
FROM employees
LIMIT 5, 5

题目二十四: 按照salary的累计和running_total,其中running_total为前N个当前( to_date = ‘9999-01-01’)员工的salary累计和,其他以此类推。
相关知识点:

  • 窗口函数:主要是为了实现实时分析处理用的,只能在select子句中使用。
  1. 能够作为窗口函数的聚合函数(sum,avg,count,max,min)
  2. rank,dense_rank。row_number等专用窗口函数。
SELECT
    emp_no,salary, SUM(salary) OVER (ORDER BY emp_no) AS running_total
FROM salaries
WHERE to_date = '9999-01-01';

题目二十四: 牛客每天有很多人登录,请你统计一下牛客每个日期登录新用户个数,
有一个登录(login)记录表,简况如下:
在这里插入图片描述
难点:没有新用户登录的日期不能不显示。

(SELECT date,SUM(l2.number)
FROM (SELECT l1.*,
IF(datediff(l1.second_login,l1.date)=1,1,0) AS number
FROM(SELECT
user_id,date,LEAD(date) OVER(PARTITION BY user_id ORDER BY date ASC) AS second_login
FROM login) l1) l2
GROUP BY date) l3
JOIN ;

题目二十五: 有很多同学在牛客购买课程来学习,购买会产生订单存到数据库里。有一个订单信息表(order_info),简况如下:
在这里插入图片描述
请你写出一个sql语句查询在2025-10-15以后,同一个用户下单2个以及2个以上状态为购买成功的C++课程或Java课程或Python课程的订单信息,并且按照order_info的id升序排序,以上例子查询结果如下:
在这里插入图片描述
本题知识点: HAVING和WHERE的区别,HAVING是在GROUP BY之后执行的,WHERE是在GROUP BY 之后执行的,本题可以根据user_id分组,分组完后HAVING count(id)>2

SQL:

SELECT *
FROM order_info
      WHERE date>'2025-10-15' 
      AND status ='completed' 
      AND product_name IN ('C++','Python','Java')
      AND user_id IN 
(SELECT user_id
      FROM order_info
      WHERE date>'2025-10-15' 
      AND status ='completed' 
      AND product_name IN ('C++','Python','Java')
      GROUP BY user_id
      HAVING count(id)>1);

猜你喜欢

转载自blog.csdn.net/weixin_44027006/article/details/121022560