【SQL】晨光咖啡馆,过滤聚合的微妙碰撞

sql · 浏览次数 : 17

小编点评

在一个阳光明媚的早晨,小悦走进了一家咖啡馆,意外地遇到了一个名叫逸尘的男子。逸尘刚结束了一夜的工作,身着简约而格调的衣服,显得英俊潇洒。他们的目光在空气中交汇,仿佛有一种默契,两人都朝着各自的方向走去。 小悦手里拿着刚出炉的拿铁,注意力完全集中在手机上的工作邮件上,对周围的一切毫无察觉。而逸尘则准备开始一天的工作,他的手不慎与小悦手中的拿铁发生了碰撞,导致一杯滚烫的咖啡泼在了他的白色衬衫上。小悦立刻道歉,脸上泛起了红晕,急忙在包里找纸巾擦拭。逸尘则表现出绅士风度,轻轻接过纸巾,从容地处理着衬衫上的咖啡渍。 尽管逸尘的语气中透露出一丝责备,但更多的却是温柔和宽容。小悦心中五味杂陈,既为自己的疏忽感到懊恼,又对逸尘那不经意间流露出的严厉和温柔交织在一起的情感感到复杂。她轻声说道:“我真的不是故意的。”这句话虽然微不足道,却清楚地传入了逸尘的耳中,两人的心似乎被某种莫名的力量轻轻触碰,营造出一种难以言喻的氛围。 此时,小悦的手机收到了一封邮件,内容要求根据公司名称和月份进行分组,统计2024年全年的订单表总数量,并进一步细分出已下单数量、送货中数量以及已收货数量。这个挑战性的任务让小悦陷入了沉思。她迅速构思出了初步的方案1,然后开始专注于方案的优化。 经过分析,小悦发现方案1存在一些问题,如性能问题、可读性和维护性、重复代码等。于是,她提出了优化后的方案2,采用了Oracle/MySql/Mssql的COUNT(CASE WHEN ...)语法。这种语法具有性能优化、简洁性和可读性等优点,减少了冗余代码,提高了代码的可维护性和可读性。 随后,小悦意识到了虽然方案2在PostgreSQL中更为简洁高效,但由于国产数据库大多兼容PostgreSQL,因此选择了这种选择不仅提升了查询性能,还确保了代码在国产数据库环境中的广泛适用性。 最后,小悦提到了SQL:2003标准引入的过滤聚合语法,这种语法可以让开发者在聚合函数中直接加上`FILTER (WHERE ...)`子句,对聚合的数据进行过滤,从而得到所需的统计结果。这种语法在SQL:2003标准中引入,PostgreSQL首先实现了这个语法。它极大地简化了SQL的编写,提高了代码的可读性和可维护性。 通过以上内容,我们可以看到,无论是对于小悦还是逸尘,生活中的每一个小插曲都是一次成长的机会。

正文

 这天,小悦懒洋洋地步入办公楼下的咖啡馆,意外地与一位男子不期而遇。他显然因前一晚的辛勤工作而略显疲惫,却仍选择早到此地,寻找一丝宁静与放松。他叫逸尘,身姿挺拔,衣着简约而不失格调,晨光下更显英俊不凡,吸引了周遭的目光。两人仿佛心有灵犀,不约而同地走向各自的位置。

小悦手中轻握着新出炉的拿铁,眼睛紧紧盯着手机上的工作邮件,心思全然沉浸在工作的海洋中,对前方即将发生的“小插曲”浑然未觉。而逸尘,正欲伸手取桌上的文件,两人的手在不经意间悄然相遇,伴随着一阵轻微的碰撞,小悦手中的拿铁微微倾斜,几滴热烫的咖啡瞬间在逸尘洁白的衬衫上绽放,如同从树上不经意间洒落的晨露,虽美却略显突兀。

“哎呀,真的非常抱歉!”小悦连忙道歉,脸颊上泛起了红晕,手忙脚乱地在包中搜寻纸巾,希望能为这突如其来的尴尬场面做些什么。逸尘则以他特有的绅士风度,轻轻接过纸巾,自行处理起那片不速之客。

“没关系,下次小心些便是。”逸尘的话语中虽带有一丝不易察觉的责备,但更多的是温柔与宽容。他皱眉的瞬间,非但没有减少魅力,反而增添了几分成熟与稳重。

小悦心中五味杂陈,既有对自己疏忽的懊恼,也有对逸尘那不经意间流露出的严厉与温柔交织的复杂情感。她低声细语:“我真的不是故意的。”这句话虽轻如蚊蚋,却清晰地传入了逸尘的耳中,两人的心间仿佛被一股莫名的力量轻轻触碰,营造出一种难以言喻的微妙氛围。


当时,小悦手机上的邮件内容深深吸引了她的注意,邮件中详细列出了一项sql任务:要求根据公司名称和月份进行分组,统计出2024年全年的订单表总数量,并进一步细分出已下单数量(状态1)、送货中数量(状态2)以及已收货数量(状态3)。这一挑战性的任务让小悦不禁陷入了沉思,她迅速地在脑海中构想出了初步的方案1:

SELECT
    o.company,
    EXTRACT(MONTH FROM o.order_date) AS month,
    (SELECT COUNT(*) FROM orders o2 WHERE o2.company = o.company AND EXTRACT(MONTH FROM o2.order_date) = EXTRACT(MONTH FROM o.order_date) AND EXTRACT(YEAR FROM o2.order_date) = 2024) AS total_orders,
    (SELECT COUNT(*) FROM orders o2 WHERE o2.company = o.company AND EXTRACT(MONTH FROM o2.order_date) = EXTRACT(MONTH FROM o.order_date) AND o2.order_status = 1 AND EXTRACT(YEAR FROM o2.order_date) = 2024) AS ordered_count,
    (SELECT COUNT(*) FROM orders o2 WHERE o2.company = o.company AND EXTRACT(MONTH FROM o2.order_date) = EXTRACT(MONTH FROM o.order_date) AND o2.order_status = 2 AND EXTRACT(YEAR FROM o2.order_date) = 2024) AS delivering_count,
    (SELECT COUNT(*) FROM orders o2 WHERE o2.company = o.company AND EXTRACT(MONTH FROM o2.order_date) = EXTRACT(MONTH FROM o.order_date) AND o2.order_status = 3 AND EXTRACT(YEAR FROM o2.order_date) = 2024) AS received_count
FROM
    orders o
WHERE
    EXTRACT(YEAR FROM o.order_date) = 2024
GROUP BY
    o.company,
    EXTRACT(MONTH FROM o.order_date)
ORDER BY
    o.company,
    month;

方案1查询语句使用了多个子查询来计算每个公司和月份的订单数量,虽然可以实现所需的功能,但也存在一些缺点:

  1. 性能问题

    • 每个子查询都需要对orders表进行独立的扫描,这会导致多次重复的数据库查询,增加了数据库的负担。
    • 对于大型数据集,这种多次扫描和查询的方式会导致性能显著下降。
  2. 可读性和维护性

    • 使用多个子查询使得SQL语句变得复杂,难以阅读和理解。
    • 如果需要修改或调试,需要逐个检查每个子查询,增加了维护的难度。
  3. 重复代码

    • 相同的条件(如公司、月份、年份)在每个子查询中重复出现,导致代码冗余。
    • 如果需要修改这些条件,必须在每个子查询中逐一修改,容易遗漏或出错。
  4. 索引利用

    • 子查询可能无法有效利用索引,尤其是在没有合适的索引情况下,查询性能会进一步下降。

 随后,小悦没有放弃,反而更加专注地投入到方案一的优化中。她仔细分析了初步方案的可行性,并考虑到了性能优化和数据处理效率的问题。于是,她提出了优化后的方案2(Oracle/MySql/Mssql):

SELECT
    company,
    EXTRACT(MONTH FROM order_date) AS month,
    COUNT(*) AS total_orders,
    COUNT(CASE WHEN status = 1 THEN 1 END) AS ordered_count,
    COUNT(CASE WHEN status = 2 THEN 1 END) AS delivering_count,
    COUNT(CASE WHEN status = 3 THEN 1 END) AS received_count
FROM
    orders
WHERE
    EXTRACT(YEAR FROM order_date) = 2024
GROUP BY
    company,
    EXTRACT(MONTH FROM order_date)
ORDER BY
    company,
    month;

方案2查询语句使用了COUNT(CASE WHEN ...)语法,具有以下优点:

  1. 性能优化

    • 通过在一个查询中完成所有计算,避免了多次扫描和查询数据库,从而提高了查询性能。
    • 数据库引擎可以更好地优化查询计划,利用索引和缓存来加速查询。
  2. 简洁性和可读性

    • 使用COUNT(CASE WHEN ...)语法使得SQL语句更加简洁,减少了冗余代码。
    • 查询逻辑清晰,易于阅读和理解,便于维护和调试。
  3. 减少重复代码

    • 相同的条件(如公司、月份、年份)只需要在WHERE子句中写一次,避免了在多个子查询中重复书写相同的条件。
    • 如果需要修改查询条件,只需在一个地方进行修改,减少了出错的可能性。
  4. 灵活性

    • COUNT(CASE WHEN ...)语法非常灵活,可以轻松地添加或修改条件,以适应不同的查询需求。
    • 可以很容易地扩展到其他状态或条件,而不需要重构整个查询。
  5. 索引利用

    • 这种查询方式可以更好地利用索引,尤其是在有合适的索引情况下,查询性能会得到进一步提升。

小悦意识到虽然方案2的CASE语法可以实现需求,但使用COUNT FILTER语法在PostgreSQL中更为简洁高效,而且由于国产数据库大多兼容PostgreSQL,这种选择不仅提升了查询性能,还确保了代码在国产数据库环境中的广泛适用性。方案3(PostgreSQL语法):,

SELECT
    company,
    EXTRACT(MONTH FROM order_date) AS month,
    COUNT(*) AS total_orders,
    COUNT(*) FILTER (WHERE status = 1) AS ordered_count,
    COUNT(*) FILTER (WHERE status = 2) AS delivering_count,
    COUNT(*) FILTER (WHERE status = 3) AS received_count
FROM
    orders
WHERE
    EXTRACT(YEAR FROM order_date) = 2024
GROUP BY
    company,
    EXTRACT(MONTH FROM order_date)
ORDER BY
    company,
    month;

方案3中的COUNT(*) FILTER (WHERE status = 1)` 这种语法是 SQL:2003 标准引入的一个新特性,称为"过滤聚合"(Filtered Aggregation)。

过滤聚合的出现是为了解决一些常见的 SQL 分析需求,例如:

1. 在统计订单总数的同时,也统计已完成订单的数量。
2. 在统计销售总额的同时,也统计已付款订单的销售额。
3. 在统计某个商品的总销量中,也统计该商品的正常销量和退货销量。

在传统的 SQL 中,解决这类需求通常需要使用多个子查询或者分组之后进行过滤,代码会比较复杂。

过滤聚合的出现,让这类需求的实现变得更加简单和优雅。开发者可以在聚合函数中直接加上 `FILTER (WHERE ...)` 子句,对聚合的数据进行过滤,从而得到所需的统计结果。

比如上面的例子中,`COUNT(*) FILTER (WHERE status = 1)` 就可以直接统计状态为 1 的订单数量,无需再额外添加子查询。

这种语法在 SQL:2003 标准中引入,PostgreSQL首先实现了这个语法。它极大地简化了 SQL 的编写,提高了代码的可读性和可维护性。

Oracle /MySql/MsSql,对于这个 SQL 标准的新特性,并没有直接支持,只能通过case when的形式实现。


示例,在Having中使用过滤聚合语法:

--case语法示例
SELECT
    company,
    EXTRACT(MONTH FROM order_date) AS month,
    COUNT(*) AS total_orders,
    COUNT(CASE WHEN status = 1 THEN 1 END) AS ordered_count,
    COUNT(CASE WHEN status = 2 THEN 1 END) AS delivering_count,
    COUNT(CASE WHEN status = 3 THEN 1 END) AS received_count
FROM
    orders
WHERE
    EXTRACT(YEAR FROM order_date) = 2024
GROUP BY
    company,
    EXTRACT(MONTH FROM order_date)
Having
    COUNT(CASE WHEN status = 1 THEN 1 END)>0
ORDER BY
    company,
    month;

--filter语法示例
SELECT
    company,
    EXTRACT(MONTH FROM order_date) AS month,
    COUNT(*) AS total_orders,
    COUNT(*) FILTER (WHERE status = 1) AS ordered_count,
    COUNT(*) FILTER (WHERE status = 2) AS delivering_count,
    COUNT(*) FILTER (WHERE status = 3) AS received_count
FROM
    orders
WHERE
    EXTRACT(YEAR FROM order_date) = 2024
GROUP BY
    company,
    EXTRACT(MONTH FROM order_date)
Having
    COUNT(*) FILTER (WHERE status = 1)>0
ORDER BY
    company,
    month;

 

与【SQL】晨光咖啡馆,过滤聚合的微妙碰撞相似的内容:

【SQL】晨光咖啡馆,过滤聚合的微妙碰撞

这天,小悦懒洋洋地步入办公楼下的咖啡馆,意外地与一位男子不期而遇。他显然因前一晚的辛勤工作而略显疲惫,却仍选择早到此地,寻找一丝宁静与放松。他叫逸尘,身姿挺拔,衣着简约而不失格调,晨光下更显英俊不凡,吸引了周遭的目光。两人仿佛心有灵犀,不约而同地走向各自的位置。 小悦手中轻握着新出炉的拿铁,眼睛紧紧

SQL查询语句汇总

SQL查询语句汇总 students表 idclass_idnamegenderscore 1 1 小明 M 90 2 1 小红 F 95 class表 idname 1 一班 2 二班 3 三班 4 四班 1.基本查询 -- 查询students表的所有数据 SELECT * FROM stude

SQL注入方法

目录前言如何测试与利用注入点手工注入思路工具sqlmap-r-u-m--level--risk-v-p--threads-batch-smart--os-shell--mobiletamper插件获取数据的相关参数 前言 记录一些注入思路和经常使用的工具,后续有用到新的工具和总结新的方法再继续补充。

性能分析: 快速定位SQL问题

在数据库性能调优的实践中,SQL性能分析是至关重要的一环。一个执行效率低下的SQL语句可能会导致整个系统的性能瓶颈。 为了快速定位并解决这些问题,我们需要对SQL进行性能分析。本文将介绍一些常用的方法和技术,帮助大家快速定位SQL问题。 1、找出执行时间最长的SQL 首先,我们需要找到执行时间最长的

大数据面试SQL每日一题系列:最高峰同时在线主播人数。字节,快手等大厂高频面试题

大数据面试SQL每日一题系列:最高峰同时在线主播人数。字节,快手等大厂高频面试题 之后会不定期更新每日一题sql系列。 SQL面试题每日一题系列内容均来自于网络以及实际使用情况收集,如有雷同,纯属巧合。 1.题目 问题1:如下为某直播平台各主播的开播及关播时间数据明细,现在需要计算该平台最高峰期同时

什么是SQL 语句中相关子查询与非相关子查询

要理解相关子查询和非相关子查询,我们得首先理解什么是子查询,子查询是指在一个查询语句中嵌套的另一个查询语句。

SQL窗口分析函数使用详解系列三之偏移量类窗口函数

1.综述 本文以HiveSQL语法进行代码演示。 对于其他数据库来说同样也适用,比如SparkSQL,FlinkSQL以及Mysql8,Oracle,SqlServer等传统的关系型数据库。 已更新第一类聚合函数类,点击这里阅读 ①SQL窗口函数系列一之聚合函数类 ②SQL窗口函数系列二之分组排序窗

SQL调优

**1. 索引优化:** 确保适当的索引在数据库表上创建,以加快查询性能。分析查询语句,确定可能需要的列和联合索引,并避免过多或不必要的索引。 **2. 优化查询语句:** 优化查询语句的写法,避免**全表扫描**和不必要的数据检索。使用合适的WHERE子句、JOIN语句和子查询,以提高查询效率。

SQL 注入漏洞详解 - Union 注入

1)漏洞简介 SQL 注入简介 SQL 注入 即是指 Web 应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在 Web 应用程序中事先定义好的查询语句的结尾上添加额外的 SQL 语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应

拯救SQL Server数据库事务日志文件损坏的终极大招

拯救SQL Server数据库事务日志文件损坏的终极大招 在数据库的日常管理中,我们不可避免的会遇到服务器突然断电(没有进行电源冗余),服务器故障或者 SQL Server 服务突然停掉, 头大的是ldf事务日志文件也损毁了,SQL Server服务器起来之后,发现数据库处于"Recovery Pe