夯实基础第二天 2022-8-6

1.数据库

1.1count(1),count(*)和count(列名)的区别

一、从执行效果来看

  1. count(1) and count(*):
    基本没差别,都是求表的总行数
    count(*)包括了所有的列,相当于求记录总行数,在统计结果的时候,不会忽略NULL

  2. count(1) and count(列名):
    (1) count(1) 会统计表中的所有的记录数,不会忽略NULL,包含字段为null 的记录。
    (2) count(列名) 会统计该列字段在表中出现的次数,会忽略字段为null 的情况,即不统计字段为null 的记录。

二、从执行效率来看

若列名为主键,count(列名)会比count(1)快
若列名不为主键,count(1)会比count(列名)快
若表多个列并且没有主键,则 count(1) 的执行效率优于 count(*)
若表有主键,则 select count(主键)的执行效率是最优的
若表只有一个字段,则 select count(*)最优。

所以实际业务中一般用count(1)比较普遍,但是如果需要聚合多个列,则用count(列名)比较合适。
————————————————
版权声明:本文为CSDN博主「攻城狮Kevin」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wx1528159409/article/details/95643499

补充关于count(1)count(*)原理 引用百度知道专业回答

count(1),其实就是计算一共有多少符合条件的行。

1并不是表示第一个字段,而是表示一个固定值。

其实就可以想成表中有这么一个字段,这个字段就是固定值1,count(1),就是计算一共有多少个1。

count(*),执行时会把星号翻译成字段的具体名字,效果也是一样的,不过多了一个翻译的动作,比固定值的方式效率稍微低一些。

1.2牛客SQL入门26计算25岁以上和以下的用户数量

1.2.1题目

题目:现在运营想要将用户划分为25岁以下和25岁及以上两个年龄段,分别查看这两个年龄段用户数量
在这里插入图片描述

1.2.2 题解

将用户划分为25岁以下和25岁及以上两个年龄段,分别查看这两个年龄段用户数量

问题分解:
限定条件:无;
年龄划分为两段:if(age>=25, “25岁及以上”, “25岁以下”)
统计用户数量:count,每个段分别统计,用group by age_cut分组
当然本题也可以用union all解决,不过用if更直观
细节问题:
表头重命名
注意题目描述的示例和testcase结果不一致!!(已修复)
针对年龄为空的记录,需要明确是划分到哪边
不同段的释义要根据testcase调整

1.2.3 SQL语句

select写法

select
    if(age>=25,"25岁及以上","25岁以下") as age_cut,count(1) as number
    from user_profile
    group by age_cut;

case写法

select 
    (case
        when age>=25 then '25岁及以上'
        else '25岁以下' end) as age_cut, 
    count(*) as number
from user_profile
group by age_cut

1.3牛客SQL入门27查看不同年龄段的用户明细

1.3.1 题目

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

1.3.2 题解

问题分解:
限定条件:无;
划分年龄段:数值条件判断,可以用多重if,不过更方便的是用case when [expr] then [result1]…else [default] end
附:case when用法
细节问题:
表头重命名:as
输出的段明文在题目描述和输出示例中不一致,以输出示例为准

1.3.3 SQL语句

select 
    device_id,gender,
    CASE
        when age<20 then "20岁以下"
        when (age>=20 and age<=24) then "20-24岁"
        when age>24 then "25岁及以上"
        else  "其他" end as age_cut
    from user_profile
    

1.4MySQL中的only_full_group_by模式

对于 GROUP BY 聚合操作,如果在 SELECT 中的列,没有在 GROUP BY 中出现,那么这个 SQL 是不合法的,因为列不在 GROUP BY 句中,所以对于设置了这个 mode 的数据库,在使用 GROUP BY 的时候,就要用 MAX(),SUM(),ANT_VALUE() 这种聚合函数,才能完成 GROUP BY 的聚合操作。
————————————————
版权声明:本文为CSDN博主「Asurplus」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_40065776/article/details/108747528

1.5牛客入门28计算用户8月每天的练题数量

1.5.1 题目

题目:现在运营想要计算出2021年8月每天用户练习题目的数量,请取出相应数据。
在这里插入图片描述

1.5.2 题解

题意明确:
2021年8月每天用户练习题目的数量

问题分解:
限定条件:2021年8月,写法有很多种,比如用year/month函数的year(date)=2021 and month(date)=8,比如用date_format函数的date_format(date, “%Y-%m”)=“202108”
每天:按天分组group by date
题目数量:count(question_id)
细节问题:
表头重命名:as
输出示例中每天的字段只取了几号,要去掉年月,用day函数即可

1.5.3 SQL语句

select
    day(date) as day,
    count(question_id) as question_cnt
    from question_practice_detail
    where month(date)=8 and year(date)=2021
    group by date

1.6牛客入门SQL29计算用户的平均次日留存率

1.6.1题目

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

1.6.2 题解

题意明确:
用户在某天刷题后第二天再来刷题的平均概率

问题分解:
限定条件:第二天再来。
解法1:表里的数据可以看作是全部第一天来刷题了的,那么我们需要构造出第二天来了的字段,因此可以考虑用left join把第二天来了的拼起来,限定第二天来了的可以用date_add(date1, interval 1 day)=date2筛选,并用device_id限定是同一个用户。
解法2:用lead函数将同一用户连续两天的记录拼接起来。先按用户分组partition by device_id,再按日期升序排序order by date,再两两拼接(最后一个默认和null拼接),即lead(date) over (partition by device_id order by date)
平均概率:
解法1:可以count(date1)得到左表全部的date记录数作为分母,count(date2)得到右表关联上了的date记录数作为分子,相除即可得到平均概率
解法2:检查date2和date1的日期差是不是为1,是则为1(次日留存了),否则为0(次日未留存),取avg即可得平均概率。

细节问题:
表头重命名:as
去重:需要按照devece_id,date去重,因为一个人一天可能来多次
子查询必须全部有重命名

1.6.3SQL语句

解法1:

select count(date2) / count(date1) as avg_ret
from 
(
    select
        distinct qpd.device_id,
        qpd.date as date1,
        uniq_id_date.date as date2
    from question_practice_detail as qpd
    left join
    (
        select distinct device_id, date
        from question_practice_detail
    ) as uniq_id_date
    on qpd.device_id=uniq_id_date.device_id
        and date_add(qpd.date, interval 1 day)=uniq_id_date.date
) as id_last_next_date

解法2:

select avg(if(datediff(date2, date1)=1, 1, 0)) as avg_ret
from (
    select
        distinct device_id,
        date as date1,
        lead(date) over (partition by device_id order by date) as date2
    from (
        select distinct device_id, date
        from question_practice_detail
    ) as uniq_id_date
) as id_last_next_date

2.算法

2.1动态规划

在我看来,动态规划就是暴力递归+记忆化

2.1.1动态规划的三大步骤

动态规划,无非就是利用历史记录,来避免我们的重复计算。而这些历史记录,我们得需要一些变量来保存,一般是用一维数组或者二维数组来保存。下面我们先来讲下做动态规划题很重要的三个步骤,

第一步骤:定义数组元素的含义,上面说了,我们会用一个数组,来保存历史数组,假设用一维数组 dp[] 吧。这个时候有一个非常非常重要的点,就是规定你这个数组元素的含义,例如你的 dp[i] 是代表什么意思?

第二步骤:找出数组元素之间的关系式,我觉得动态规划,还是有一点类似于我们高中学习时的归纳法的,当我们要计算 dp[n] 时,是可以利用 dp[n-1],dp[n-2]……dp[1],来推出 dp[n] 的,也就是可以利用历史数据来推出新的元素值,所以我们要找出数组元素之间的关系式,例如 dp[n] = dp[n-1] + dp[n-2],这个就是他们的关系式了。

学过动态规划的可能都经常听到最优子结构,把大的问题拆分成小的问题,说时候,最开始的时候,我是对最优子结构一梦懵逼的。估计你们也听多了,所以这一次,我将换一种形式来讲,不再是各种子问题,各种最优子结构。

第三步骤:找出初始值。学过数学归纳法的都知道,虽然我们知道了数组元素之间的关系式,例如 dp[n] = dp[n-1] + dp[n-2],我们可以通过 dp[n-1] 和 dp[n-2] 来计算 dp[n],但是,我们得知道初始值啊,例如一直推下去的话,会由 dp[3] = dp[2] + dp[1]。而 dp[2] 和 dp[1] 是不能再分解的了,所以我们必须要能够直接获得 dp[2] 和 dp[1] 的值,而这,就是所谓的初始值。

由了初始值,并且有了数组元素之间的关系式,那么我们就可以得到 dp[n] 的值了,而 dp[n] 的含义是由你来定义的,你想求什么,就定义它是什么,这样,这道题也就解出来了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值