当前位置:首页 > 问答 > 正文

随便聊聊那些用得上的随机SQL函数,举几个简单例子看看效果

给数据排排坐,标个号:ROW_NUMBER() 和它的兄弟们

这个功能在数据分析的时候特别有用,想象一下,你有一张学生成绩表,你想知道每个学生在自己班级里的排名,或者你想对一组数据按顺序标上1、2、3……这时候ROW_NUMBER()就登场了。

它通常和OVER这个关键字一起用。OVER后面跟着的,就是告诉数据库怎么给数据“分组”和怎么“排序”。

  • 例子1:简单的顺序编号 假设我们有一张orders订单表,我们想按照订单创建的时间先后,给所有订单编个号。

    SELECT
        order_id,
        order_date,
        ROW_NUMBER() OVER (ORDER BY order_date ASC) as order_sequence
    FROM orders;

    这样查询结果就会多出一列order_sequence,从1开始,按照order_date从早到晚依次递增。

  • 例子2:组内排名 现在需求升级了,我们有一张sales销售表,里面有销售员、销售部门和销售额,我们想看看每个销售部门里,谁的业绩最好。 这里就要用到PARTITION BY,它是在OVER里面用来分组的。

    随便聊聊那些用得上的随机SQL函数,举几个简单例子看看效果

    SELECT
        salesperson,
        department,
        sales_amount,
        ROW_NUMBER() OVER (PARTITION BY department ORDER BY sales_amount DESC) as dept_rank
    FROM sales;

    这个查询的意思就是:先按department部门进行分组,然后在每个部门内部,按照sales_amount销售额从高到低排序,最后给每个人标上排名,排名第一的,就是部门的销售冠军。

    除了ROW_NUMBER(),它还有两个兄弟:RANK()DENSE_RANK(),它们的区别在于处理并列名次的方式:

    • ROW_NUMBER():即使成绩相同,也会给出连续的号码(1,2,3,4)。
    • RANK():如果两人销售额相同,并列第一,那么下一个名次就是第三(1,1,3,4)。
    • DENSE_RANK():如果两人销售额相同,并列第一,那么下一个名次是第二(1,1,2,3)。

看一眼上一行或下一行的数据:LAG() 和 LEAD()

这两个函数非常强大,可以用来做环比、同比分析,它们能在当前行,直接获取到前面一行或者后面一行的数据,而不用去写复杂的自连接查询。

  • 例子:计算月度销售额的环比增长 假设有张monthly_sales表,有月份和当月销售额,我们想计算每个月的销售额比上个月增长了多少。
    SELECT
        month,
        sales_amount,
        LAG(sales_amount, 1) OVER (ORDER BY month) as previous_month_sales, -- 获取上个月的数据
        (sales_amount - LAG(sales_amount, 1) OVER (ORDER BY month)) / LAG(sales_amount, 1) OVER (ORDER BY month) * 100 as growth_rate
    FROM monthly_sales;

    LAG(sales_amount, 1)中的1表示向前看1行,同样,LEAD()就是向后看,这样我们就能轻松地拿到相邻月份的数据进行计算了,对于第一个月,LAG()会返回NULL,所以增长率也是NULL,这很合理。

    随便聊聊那些用得上的随机SQL函数,举几个简单例子看看效果

把多行数据“捏”到一起:STRING_AGG() (或 GROUP_CONCAT())

这个函数特别直观,有时候你分组查询后,不想只显示一个汇总值,而是想把组内的某些文本值都罗列出来,用逗号或者其他符号隔开。

  • 例子:查看每个部门的所有员工姓名 有一张employees表,有员工姓名和部门,我们想得到一个结果,第一列是部门名,第二列是这个部门所有员工的名字,用顿号隔开。

    -- 在MySQL中可能是GROUP_CONCAT
    -- SELECT department, GROUP_CONCAT(employee_name SEPARATOR '、') FROM employees GROUP BY department;
    -- 在SQL Server或PostgreSQL中常用STRING_AGG
    SELECT
        department,
        STRING_AGG(employee_name, '、') WITHIN GROUP (ORDER BY employee_name) as employee_list
    FROM employees
    GROUP BY department;

    结果可能就是这样: | department | employee_list | |------------|----------------| | 技术部 | 张三、李四、王五 | | 市场部 | 赵六、孙七 |

    这样看起来就非常清晰明了。

    随便聊聊那些用得上的随机SQL函数,举几个简单例子看看效果

处理空值的“替补队员”:COALESCE()

这个函数非常简单实用,它接受多个参数,返回第一个不是NULL的值,就像准备了好几个替补队员,第一个不上第二个上。

  • 例子:显示用户的联系方式 用户表users中,可能有手机号phone和备用邮箱backup_email,但有些字段是空的,我们想优先显示手机号,如果手机号为空,就显示备用邮箱,如果都为空,就显示“暂无联系方式”。
    SELECT
        user_name,
        COALESCE(phone, backup_email, '暂无联系方式') as contact_info
    FROM users;

    这样就能保证contact_info这一列永远有一个可读的值,而不是令人困惑的NULL。

条件判断:CASE WHEN ... THEN ... END

这大概是SQL里最像编程语言中if-else语句的结构了,用途极其广泛,它允许你根据不同的条件,返回不同的值。

  • 例子:给销售额分级 还是销售表,我们想根据销售额的大小,给每笔销售打上一个标签。
    SELECT
        salesperson,
        sales_amount,
        CASE
            WHEN sales_amount > 10000 THEN '优秀'
            WHEN sales_amount BETWEEN 5000 AND 10000 THEN '良好'
            WHEN sales_amount BETWEEN 1000 AND 4999 THEN '及格'
            ELSE '需努力'
        END as performance_level
    FROM sales;

    这种灵活的判断能力,在数据清洗、打标签、生成报表等场景下简直是神器。

这些函数只是SQL强大功能的一个缩影,它们的特点都是“想法很直接,效果很显著”,平时多了解一下,等真正遇到合适的场景时,你就会想起来:“哎,好像有个函数正好能搞定这个!”,然后事半功倍,希望这几个简单的例子能让你觉得有点意思。