怎么算熟练使用MySQL_掌握MySQL!你有多熟练?

学习MySQL也有一阵子,从秦路老师的七周成为数据分析,到《对比EXCEL学习,轻松学习SQL》,再到刷牛客网的SQL题目。总结过SQL要点,也对比EXCLE总结过SQL,但还是觉得不成一体。

所以我想借此文,将自己现在掌握的MySQL一一呈现,也算给自己个交代。

至今学过的知识点:简单查询(select)

复杂查询:子查询,多表查询,聚合函数(group by)的使用

行转列问题(归根结底case when end的用法)列转行(union)

SQL中常用高级函数相关的题目【case when end】【字符串相关:substr/concat/split】

运营数据分析:复购率、回购率

不熟悉的SQL函数

1)取百分比可以用【percentile】

但不知为何我的电脑里不存在percentile,用法记录在此

#想要获取top10%的值?—— percentile 百分位函数

-- 获取income字段的top10%的阈值

select percentile(CAST (salary AS int),0.9)) as income_top10p_threshold from table_1;

-- 获取income字段的10个百分位点

select percentile(CAST (salary AS int),array(0.0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0)) as income_percentiles

from table_1;

#CAST它常用于string/int/double型的转换。

查找前20%的数据,我用以下方法【解题思路】①用row_rumber()对数据进行降序排名,得到ranking ②ranking<=(select max(ranking) from 表)

#查找前20%数据(拼多多面试题)

用户访问次数表,列名包括用户编号、用户类型、访问量。要求在剔除访问次数前20%的用户后,每类用户的平均访问次数。

SELECT t.用户类型,avg(t.访问量) from

(SELECT *,row_number() over (ORDER BY 访问量 desc) as ranking from 拼多多访问) t

where t.ranking>(SELECT count(用户编号) from 拼多多访问)*0.2

GROUP BY t.用户类型;

2)字符串相关substr/concat/splitconcat( A, B...)返回将A和B按顺序连接在一起的字符串,如:concat('foo', 'bar') 返回'foobar'

select concat('www','.iteblog','.com') from

iteblog;

--得到 www.iteblog.com

#变形①--以分隔符连接

SELECT CONCAT_WS(',','GOOD','JOB')

---得到GOOD,JOB

#变形②--同组连接

select dept_no,group_concat(emp_no) as employees from dept_emp

group by dept_no

;

--将表dept_emp中相同dept_no的emp_no连接(from牛客网53题)

2. split(str, regex)用于将string类型数据按regex提取,分隔后转换为array。???

-- 以","为分隔符分割字符串,并转化为array

Select split("1,2,3",",")as value_array from table_1;

-- 结合array index,将原始字符串分割为3列

select value_array[0],value_array[1],value_array[2] from

(select split("1,2,3",",")as value_array from table_1 )t

3. substr(str,0,len) 截取字符串从0位开始的长度为len个字符。

select substr('abcde',3,2) from

iteblog;

-- 得到cd字符串长度/替换

查找字符串'10,好,B' 中逗号','出现的次数cnt(第51题)

#总长度-剔除(,)长度

#剔除(,)用replace

SELECT CHAR_LENGTH('10,好,B')-CHARACTER_LENGTH(replace('10,好,B',',',''))

注意:char_length()基于字符计数,length()基于字节计数

SELECT replace('10,好,B',',',''),

CHAR_LENGTH('10,好,B'),

CHARACTER_LENGTH(replace('10,好,B',',','')),

length('10,好,B'),

length(replace('10,好,B',',',''))

以下是各种练习

这部分,是我的错题本,会随刷题不断增加内容~

1)基础过关50题

2)牛客网题目复杂查询——【牛客网28题】查找描述信息(film.description)中包含robot的电影对应的分类名称(category.name)以及电影数目(count(film.film_id)),而且还需要该分类包含电影总数量(count(film_category.category_id))>=5部

【思路拆解】

Step1:明确最终输出:包含robot的电影【分类名称】以及对应分类的电影【数目】

Step2:明确限制条件

①film.description包含robot(like '%robot%')

②查找出category_id,满足(count(film_category.category_id))>=5

【解答】

SELECT c.`name`,count(f.film_id) from film f

LEFT JOIN film_category fc

on f.film_id=fc.film_id

LEFT JOIN category c

on c.category_id=fc.category_id

where f.description like '%robot%'

and fc.category_id=(

select category_id from film_category

group by category_id

having count(film_id)>=5);

考虑重复值1——【牛客网8题】找出所有员工当前(to_date='9999-01-01')具体的薪水salary情况,对于相同的薪水只显示一次,并按照逆序显示

【已知】

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

【思路一】用distinct去重

select distinct(salary) from salaries

where to_date='9999-01-01'

order by salary desc;

【思路二】用group by

select salary from salaries

where to_date='9999-01-01'

group by salary

order by salary desc;

考虑重复值2——【牛客网第14题】从titles表获取按照title进行分组,每组个数大于等于2,给出title以及对应的数目t。注意对于重复的emp_no进行忽略(即emp_no重复的title不计算,title对应的数目t不增加)。

【已知】

CREATE TABLE IF NOT EXISTS `titles` (

`emp_no` int(11) NOT NULL,

`title` varchar(50) NOT NULL,

`from_date` date NOT NULL,

`to_date` date DEFAULT NULL);

【求解】

select title,count(distinct emp_no) as t from titles

group by title

having count(distinct emp_no)>=2

;多思维解题——【牛客网第17题】获取当前(to_date='9999-01-01')薪水第二多的员工的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`));

【思路1】用limit函数来限制

select emp_no,salary from salaries

order by salary desc

limit 1,1

【思路3】第二大就是比最大小的最大

select emp_no,max(salary)

from salaries

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

【思路2】用窗口函数排序

select t.emp_no,t.salary from

(select *,row_number() over (order by salary desc) as ranking from salaries) t

where t.ranking=2

;

3)连续签到天数求解连续签到问题的关键在于找出数据间的逻辑关系,连续签到意味着某两列数据之差是一致的。

腾讯经典面试题,百度也考过☞接受挑战

数据分析SQL面试9套题里第8题也出现了连续签到,一个表记录了某论坛会员的发贴情况,存储了user_id ,发贴时间post_time和内容content。找出连续发贴三次及以上的会员。思路:①每个用户按照签到先后排序 ②所有用户按照签到先后排序 ③如果连续签到,则排序②与排序①的差值相同

SELECT t.user_id from

(SELECT user_id,post_time,

ROW_number() over (PARTITION by user_id ORDER BY post_time) as row1,

row_number() over (order by post_time) as row2,

row_number() over (order by post_time)-ROW_number() over (PARTITION by user_id ORDER BY post_time) as diff

from tablename)t

GROUP BY t.user_id,t.diff

having count(t.user_id)>=3

;

4)数据分析SQL面试题9套汇总字符串连接

留存率

行列转换

列转行

窗口函数应用(求累计和)

查询登录次数

红包领取活动

连续签到【题目】求用户号对应的前两个不同场景(场景重复的话,选重复场景的第一个访问时间,场景不足两个的输出为止)该题目与牛客网第53题类似,涉及字符串连接的知识:可以用group_concat(列名 SEPARATOR '分隔符') 解决。解题思路①排序 ②条件查询 ③concat函数连接

SELECT concat(t.user_id,'-',GROUP_CONCAT(DISTINCT t.scene SEPARATOR '-') ) as reasult FROM

(SELECT *,

row_number() over (PARTITION by user_id order by time) as ranking

from scene) t

where t.ranking<3

GROUP BY t.user_id

;【题目】求累计和(窗口函数之聚合函数,如sum. avg, count, max, min等的应用)

SELECT t.year,t.month,t.value,sum(value) over (PARTITION by year order by t.month) as ysum,

sum(value) over (order by t.ranking) as sum

FROM

(SELECT year(fdate) as year,

month(fdate) as month,

row_number() over (order by fdate) as ranking,

sum(value) as value

from sales

group by year(fdate),month(fdate))t;

【题目】生成登录表,求出最近一次登录时间以及每个用户登录总次数(当天登录多次记录1次)注意时间函数的用法。①文本转换成时间 DATE_FORMAT(文本,'%Y-%m-%d %H:%i:%s') ②时间的提取,datetime转化成date格式,直接用date()

【生成表】

create table userlog

(

id int ,

name varchar(10),

EmailAddress varchar(50),

lastlogon varchar(50)

);

insert into userlog values(100,'test4','test4@yahoo.cn','2007-11-25 16:31:26');

insert into userlog values(13,'test1','test4@yahoo.cn','2007-3-22 16:27:07');

insert into userlog values(19,'test1','test4@yahoo.cn','2007-10-25 14:13:46');

insert into userlog values(42,'test1','test4@yahoo.cn','2007-10-25 14:20:10');

insert into userlog values(45,'test2','test4@yahoo.cn','2007-4-25 14:17:39');

insert into userlog values(49,'test2','test4@yahoo.cn','2007-5-25 14:22:36');

UPDATE userlog set lastlogon=DATE_FORMAT(lastlogon,'%Y-%m-%d %H:%i:%s');

SELECT * from userlog;

【查询登录次数】

SELECT name,max(lastlogon) as 最近一次登录时间,

count(DISTINCT date(lastlogon)) as 登录次数 from userlog

group by name;

【题目】红包领取

两张表如下

1、计算2019年6月1日至今,每日DAU(活跃用户量,即有登陆的用户)

2、计算20190601至今,每日领取红包的新用户数,老用户数,及人均领取金额,人均领取次数(注意领取人数中有未登录用户)

3、计算2019年3月,每个月按领红包取天数为1、2、3……30、31天区分,计算取每个月领取红包的用户数,人均领取金额,人均领取次数

4、计算2019年3月,每个月领过红包用户和未领红包用户的数量

5、计算2019年3月至今,每个月活跃用户的注册日期,2019年3月1日前注册的用户日期填空即可

6、计算2019年3月至今,每日的用户次日留存率,领取红包用户的次日留存,未领取红包用户的次日留存率

7、计算2019年6月1日至今,每日新用户领取得第一个红包的金额

8、计算2019年3月1日至今,每个新用户领取的第一个红包和第二个红包的时间差(只计算注册当日有领取红包的用户,注册当日及以后的DAU表中新用户为1的用户)

【1】每日DUA人数

SELECT `登录日期`,count(`用户ID`) as DAU from 活跃用户表

where `登录日期`>20190531

GROUP BY `登录日期`;

【2】每日领取红包人员构成(表连接的时候不仅要id连接,还要时间连接)

SELECT p.`抢红包日期`,

count(DISTINCT case when u.`是新用户`='1' then p.`用户ID` else null end) as 新用户数,

count(DISTINCT case when u.`是新用户`='0' then p.`用户ID` else null end) as 老用户数,

count(DISTINCT case when u.`是新用户`is null then p.`用户ID` else null end) as 未登录用户数,

round(sum(p.`金额`)/count(DISTINCT p.`用户ID`),2) as 人均领取金额,

count(p.`用户ID`)/count(DISTINCT p.`用户ID`) as 人均领取次数

from `领取红包表` p

LEFT JOIN `活跃用户表` u

on p.`用户ID`=u.`用户ID` and p.`抢红包日期`=u.`登录日期`

where p.`抢红包日期`>20190531

GROUP BY p.`抢红包日期`

;

【3】每月领取红包状况~这个很简单

SELECT DATE_FORMAT(`抢红包日期`,'%Y-%m') as 月份,

count(DISTINCT `抢红包日期`) as 领取天数,

count(DISTINCT `用户ID`) as 每个月领取红包人数,

sum(`金额`)/count(DISTINCT `用户ID`) as 人均领取金额,

count(`用户ID`)/count(DISTINCT `用户ID`) as 人均领取次数

from `领取红包表`

GROUP BY DATE_FORMAT(`抢红包日期`,'%Y-%m');

【4】登录用户中,每月领取红包人数 和 未领取红包人数

SELECT DATE_FORMAT(u.`登录日期`,'%Y-%m') as 月份,

count(DISTINCT case when p.`用户ID` is not null then u.`用户ID` else null end)as 领红包人数,

count(DISTINCT case when p.`用户ID` is null then u.`用户ID` else null end)as 未领红包人数

from `活跃用户表` u

LEFT JOIN `领取红包表` p

on u.`用户ID`=p.`用户ID` and u.`登录日期`=p.`抢红包日期`

group by DATE_FORMAT(u.`登录日期`,'%Y-%m')

;

【5】各用户首次登录的日期

SELECT t1.`用户ID`,t2.`登录日期` as 注册日期 FROM

(SELECT `用户ID` from `活跃用户表`

group by `用户ID`)t1

LEFT JOIN

(SELECT * from `活跃用户表`

where `是新用户`=1)t2

on t1.`用户ID`=t2.`用户ID`

;【6】解题思路是将要的总结果拆分,再将一个个分结果连接。

①求出登录用户次日留存率,活跃用户表与活跃用户表连接

②当日领取红包占比,活跃用户表与领取红包表连接

③领取红包次日留存,领取红包表与活跃用户表连接

以上三个连接均为左连接

【6】每日登录用户的次日留存、领取红包用户的次日留存、未领取红包用户的次日留存

SELECT t1.`登录日期`,t1.`当日登录人数`,t2.`领取红包数`,t2.`当日领取红包占比`,t1.`用户次日留存率`,t3.`当日领红包次日留存率` from

(SELECT a.`登录日期`,

count(DISTINCT a.`用户ID`) 当日登录人数,

count(case when DATE_ADD(a.`登录日期`,INTERVAL 1 day)=b.`登录日期` then b.`用户ID` else null end) as 次日登录人数,

count(case when DATE_ADD(a.`登录日期`,INTERVAL 1 day)=b.`登录日期` then b.`用户ID` else null end)/count(DISTINCT a.`用户ID`) as 用户次日留存率

from `活跃用户表` a

LEFT JOIN `活跃用户表` b

on a.`用户ID`=b.`用户ID`

GROUP BY a.`登录日期`) t1

JOIN

(SELECT u.`登录日期`,count(DISTINCT u.`用户ID`) as 当日登录数,

count(DISTINCT p.`用户ID`)as 领取红包数,

count(DISTINCT p.`用户ID`)/count(DISTINCT u.`用户ID`) as 当日领取红包占比

from `活跃用户表` u

LEFT JOIN `领取红包表` p

on u.`用户ID`=p.`用户ID` and u.`登录日期`=p.`抢红包日期`

group by u.`登录日期`)t2

on t1.`登录日期`=t2.`登录日期`

JOIN

(SELECT p1.`抢红包日期`,

count(DISTINCT p1.`用户ID`) as 当日抢红包人数,

count(DISTINCT case when DATE_ADD(p1.`抢红包日期`,INTERVAL 1 day)=u1.`登录日期` then u1.`用户ID` else null end) as 领红包次日登录人数,

count(DISTINCT case when DATE_ADD(p1.`抢红包日期`,INTERVAL 1 day)=u1.`登录日期` then u1.`用户ID` else null end)/count(DISTINCT p1.`用户ID`) as 当日领红包次日留存率

from `领取红包表` p1

LEFT JOIN `活跃用户表` u1

on p1.`用户ID`=u1.`用户ID`

group by p1.`抢红包日期`)t3

on t2.`登录日期`=t3.抢红包日期

;【7】思路:①筛选出每个用户首次抢红包的时间 ②筛选出首次抢的红包金额 ③活跃用户表与筛选出的表连接,求出结果

SELECT t1.`登录日期` as 日期,t1.`用户ID`,t2.`金额` FROM

(SELECT `登录日期`,用户ID from `活跃用户表`

where `登录日期`>=20190601 and `是新用户`=1)t1

LEFT JOIN

(SELECT t.`用户ID`,p.`抢红包日期`,p.`金额` from

(SELECT `用户ID`,min(`抢红包时间`) as 第一次领取时间 from `领取红包表`

GROUP BY `用户ID`)t

JOIN `领取红包表` p

on t.`用户ID`=p.`用户ID` and t.`第一次领取时间`=p.`抢红包时间`) t2

on t1.`用户ID`=t2.`用户ID` and t1.`登录日期`=t2.`抢红包日期`

;【8】思路:①求出新用户首次抢红包时间,活跃用户表left join领取红包表,使用筛选条件,并对连接表以领取红包userid进行分组,找出组内最早时间,即为首次领取红包时间。②将表一与领取红包表再次连接,并筛选出抢红包时间一致的数据,用TIMESTAMPDIFF求出时间间隔

SELECT t1.`用户ID`,t1.`首次抢红包时间`,

min(TIMESTAMPDIFF(minute,t1.`首次抢红包时间`,t2.`抢红包时间`)) as 抢红包时间间隔

FROM

(SELECT p.`用户ID`,min(p.`抢红包时间`) as 首次抢红包时间 from `活跃用户表` u

LEFT JOIN `领取红包表` p

on u.`用户ID`=p.`用户ID` and u.`登录日期`=p.`抢红包日期`

where u.`是新用户`='1'

GROUP BY p.`用户ID`) t1

LEFT JOIN `领取红包表` t2

ON t1.`用户ID`=t2.`用户ID`

where t1.`首次抢红包时间`!=t2.`抢红包时间`

group by t1.`用户ID`;

#窗口函数来排序求解

SELECT t1.用户ID,timeDIFF(t2.抢红包时间,t1.抢红包时间) from

(SELECT * FROM

(SELECT `抢红包时间`,`用户ID`,row_number() over (PARTITION by `用户ID` ORDER BY `抢红包时间`) as ranking from `领取红包表`

WHERE `用户ID` in

(SELECT `用户ID` from `活跃用户表`

where `是新用户`=1)) t

where t.ranking=1) t1

LEFT JOIN

(SELECT * FROM

(SELECT `抢红包时间`,`用户ID`,row_number() over (PARTITION by `用户ID` ORDER BY `抢红包时间`) as ranking from `领取红包表`

WHERE `用户ID` in

(SELECT `用户ID` from `活跃用户表`

where `是新用户`=1)) t

where t.ranking=2) t2

on t1.用户ID=t2.用户ID

;

(~ ̄▽ ̄)~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值