资讯动态

Oracle聚合函数本质:理解GROUP BY的数据粒度压缩逻辑

发布时间:2026/10/14 0:22:05 来源:尧图企业网站定制
1. 这不是“背函数”而是理解数据聚合的底层逻辑你打开Oracle文档看到AVG、SUM、COUNT、MAX、MIN这些函数第一反应是不是“又得记语法”我带过不少刚接触数据库的开发者和数据分析新人他们常把多行函数当成“SQL里的数学公式”——输入一列数字输出一个结果。但实际项目里真正卡住人的从来不是语法写错而是根本没想清楚这一行结果到底代表什么业务含义它背后的数据粒度发生了怎样的变化举个最典型的例子某电商后台要统计“每个品类的平均订单金额”。很多人直接写SELECT category, AVG(order_amount) FROM orders GROUP BY category;看起来没问题对吧但上线后业务方反馈“这个平均值怎么比我们Excel里算的低一大截”——问题出在哪不是函数用错了而是没意识到AVG默认对NULL值跳过而订单表里大量未支付成功的记录order_amount是NULL。你统计的是“已支付订单”的平均值而业务方想要的是“所有下单行为”的平均值含未支付甚至可能需要明确标注“支付率”这个衍生指标。这就是多行函数的本质它不是冷冰冰的数学运算而是一次显式的、可追溯的数据粒度压缩过程。每一行GROUP BY后的结果都对应着原始数据中满足某组条件的所有记录的“抽象代表”。你选AVG就是在说“我要用这组记录的算术平均数来代表它们”你选COUNT(*)就是在说“我只关心这组有多少条记录不关心内容”你选LISTAGG就是在说“我要把这组记录的某个字段拼成一个字符串保留全部信息”。所以这篇文章不讲“函数列表”也不堆砌“10个必须掌握的聚合技巧”。我会带你从真实场景出发拆解每一个关键决策背后的业务意图、技术约束和常见陷阱。比如为什么在复杂报表中COUNT(1)和COUNT(*)性能几乎一样但COUNT(column_name)却可能慢3倍为什么HAVING子句不能直接引用SELECT里的别名而WHERE却可以为什么在窗口函数普及的今天GROUP BY依然不可替代这些都不是语法题而是数据建模的基本功。如果你正在写报表SQL、做ETL清洗、或者准备Oracle OCP考试这篇文章会帮你把“会写”变成“写得明白”把“能跑通”变成“敢交付”。接下来的内容全部基于我过去十年在多个金融、制造、零售类项目中的真实踩坑记录和优化实践没有理论空谈只有可验证、可复现的操作逻辑。2. 核心设计思路为什么必须先分组再聚合2.1 分组是聚合的前提不是可选项很多初学者以为“聚合函数单行计算”这是最大的认知偏差。Oracle的多行函数官方称Aggregate Functions严格定义为对一组行group of rows进行计算返回单个值。注意关键词“一组行”。这意味着如果没有明确告诉Oracle“哪几行算作一组”它根本不知道该对谁聚合。你执行这条语句SELECT AVG(salary) FROM employees;表面看没写GROUP BY但它隐含了一个全局分组所有employees表的记录被自动归为1组。所以AVG(salary)返回的是全公司员工的平均薪资。这不是函数的“智能推断”而是SQL标准规定的默认行为——当没有显式GROUP BY时整个结果集被视为一个组。一旦你需要更细粒度的分析比如“各部门平均薪资”就必须显式声明分组依据SELECT department_id, AVG(salary) FROM employees GROUP BY department_id;这里department_id就是分组键Grouping Key。Oracle执行时会先扫描全表按department_id值把所有行划分为若干桶bucket每个桶内包含同部门的所有员工记录然后对每个桶单独应用AVG函数计算该部门的平均值。最终结果行数 不同department_id值的个数。提示GROUP BY子句中的列必须出现在SELECT列表中除非被聚合函数包裹。这是SQL的强制规则不是Oracle特有。原因很简单SELECT中出现的非聚合列必须能唯一确定其所属的分组。如果SELECT department_name, AVG(salary)但GROUP BYdepartment_id而department_name和department_id不是一一对应比如存在重名部门Oracle就无法确定某一行department_name该取哪个值——这会导致语义歧义。2.2 分组键的选择决定了分析的维度和颗粒度分组键不是随便选的。它直接定义了你的分析视角。同一个数据集换一个分组键产出的业务价值天差地别。来看一个制造业的真实案例某工厂有production_log表记录每台设备每班次的产量output_qty、故障时间downtime_min、操作员IDoperator_id。按设备分组GROUP BY machine_id→ 得到“每台设备的平均班产”、“每台设备平均故障时长”。用于设备健康度评估、采购新设备的ROI测算。按班次分组GROUP BY shift_code早/中/晚班→ 得到“各班次平均产量”、“各班次平均故障率”。用于排班优化、夜班补贴政策制定。按操作员分组GROUP BY operator_id→ 得到“每位操作员平均班产”、“每位操作员平均故障处理时长”。用于绩效考核、技能认证。按设备班次组合分组GROUP BY machine_id, shift_code→ 得到“XX设备在早班的平均产量”。这是精细化管理的关键能发现“某设备在夜班故障率飙升”的隐藏问题。关键点在于分组键越多结果越细但解读难度越高分组键越少结果越粗但通用性越强。没有“最好”的分组只有“最适合当前业务问题”的分组。我见过太多报表因为分组键选错导致管理层看到的是一堆无法行动的数字。比如销售分析中如果只按“省份”分组就看不到“省会城市vs地级市”的消费差异如果按“客户ID”分组上万行结果根本没法看。2.3 聚合函数的类型选择本质是业务语义的精确表达不是所有聚合函数都适合所有场景。选错函数等于用错尺子量身高。函数适用场景典型陷阱实测案例COUNT(*)统计总行数含NULL误用COUNT(column)替代导致NULL行被忽略某物流系统统计“当日发货单总数”用COUNT(ship_date)结果比实际少15%因部分单据ship_date为空待确认COUNT(column)统计某列非NULL值的数量在需要“总记录数”时误用造成漏计某教育平台统计“完成课程的学生数”用COUNT(complete_date)正确若用COUNT(*)则包含未开始学习的学生失真SUM()数值累加需确保无负值干扰对含负值的字段如库存变动直接SUM结果无业务意义某零售系统inventory_change字段含进货和退货-直接SUM得到净变动但业务方需要“总进货量”和“总退货量”两个独立指标AVG()计算算术平均对异常值敏感未剔除离群值平均值被拉偏某SaaS公司计算“客户月均使用时长”因3个超大客户1000小时拉高平均值掩盖了95%客户实际使用2小时的事实MEDIAN()计算中位数抗异常值Oracle 12c前不支持需用PERCENTILE_CONT模拟升级到12c后将核心报表的AVG替换为MEDIAN管理层终于看清了真实用户活跃水平注意AVG()和SUM()要求参数为数值类型MAX()/MIN()可作用于数值、日期、字符。对字符类型用MAX()返回字典序最大的字符串如MAX(Apple,Banana)Banana这在某些分类汇总中有奇效比如取每个客户的最新订单状态状态码按字母排序ShippedProcessingPending。3. 核心细节解析从语法到生产环境的硬核要点3.1 GROUP BY的深层规则与常见误区GROUP BY看似简单但生产环境中90%的报错和性能问题都源于对其规则理解不深。规则一SELECT列表中的非聚合列必须完全等价于GROUP BY中的列或其表达式。这是最常被违反的规则。看这个错误写法-- ❌ 错误department_name不在GROUP BY中且与department_id不构成函数依赖 SELECT department_name, AVG(salary) FROM employees GROUP BY department_id;Oracle会报错ORA-00979: not a GROUP BY expression。解决方法只有两个方案A把department_name也加入GROUP BY前提是department_name和department_id一一对应方案B用聚合函数包裹department_name如MAX(department_name)利用其函数依赖性规则二GROUP BY支持表达式和函数但必须与SELECT中的一致。比如按入职年份分组-- ✅ 正确GROUP BY和SELECT中都用EXTRACT(YEAR FROM hire_date) SELECT EXTRACT(YEAR FROM hire_date) AS hire_year, COUNT(*) FROM employees GROUP BY EXTRACT(YEAR FROM hire_date);但如果写成-- ❌ 错误SELECT中用TO_CHARGROUP BY中用EXTRACTOracle视为不同表达式 SELECT TO_CHAR(hire_date, YYYY) AS hire_year, COUNT(*) FROM employees GROUP BY EXTRACT(YEAR FROM hire_date);会报同样的ORA-00979错误。必须保持两边表达式字面完全一致。规则三GROUP BY NULL 是合法且有用的。GROUP BY NULL强制将所有行归为1组效果等同于不写GROUP BY但语义更清晰。常用于需要同时返回聚合值和明细的场景配合ROLLUP或CUBE时更明显-- 返回全公司平均薪资 每个员工薪资用窗口函数更优但GROUP BY NULL是基础 SELECT NULL AS dummy_group, AVG(salary) AS avg_company_salary FROM employees GROUP BY NULL;3.2 HAVING子句分组后的“二次筛选”不是WHERE的替代品新手最容易混淆HAVING和WHERE。记住这个铁律WHERE过滤行row-level filterHAVING过滤组group-level filter。WHERE salary 5000先从employees表中筛出薪资5000的员工再对这些员工分组聚合。HAVING AVG(salary) 8000先按部门分组计算每个部门平均薪资再筛出平均薪资8000的部门。执行顺序决定了它们的不可互换性。WHERE在分组前执行所以能用原始列HAVING在分组后执行所以只能用分组键和聚合函数结果。一个经典反模式是试图在WHERE中用聚合函数-- ❌ 语法错误WHERE不能用AVG() SELECT department_id, AVG(salary) FROM employees WHERE AVG(salary) 8000 GROUP BY department_id;正确写法必须用HAVING-- ✅ 正确 SELECT department_id, AVG(salary) FROM employees GROUP BY department_id HAVING AVG(salary) 8000;实操心得HAVING子句的性能往往被低估。因为它在分组计算完成后才执行所以HAVING COUNT(*) 100并不会减少分组计算量只是丢弃结果。如果目的是减少计算应该优先用WHERE预过滤如WHERE status ACTIVE把无效数据在分组前就剔除。我在某银行项目中将一个报表的WHERE条件从WHERE 11改为WHERE create_date ADD_MONTHS(SYSDATE, -6)分组计算耗时从42秒降到3.5秒——因为6个月前的数据根本不用参与分组。3.3 处理NULL值聚合函数的“隐形开关”NULL在聚合中不是“不存在”而是“未知值”。不同函数对NULL的处理策略完全不同这是数据质量的照妖镜。COUNT(*)统计所有行NULL也计1。COUNT(column)只统计该列非NULL的行。SUM(),AVG(),MAX(),MIN()自动忽略NULL值。SUM(1,2,NULL,4) 7不是NULL。这带来一个严重问题当一列大部分是NULL时COUNT(column)会远小于COUNT(*)而SUM()可能返回0如果全NULL或一个看似合理但实际覆盖范围极小的值。解决方案不是回避NULL而是主动声明意图-- 方案1用NVL/COALESCE将NULL转为默认值慎用需业务确认 SELECT department_id, AVG(NVL(salary, 0)) AS avg_salary_with_zero FROM employees GROUP BY department_id; -- 方案2用CASE WHEN显式区分NULL和非NULL推荐语义清晰 SELECT department_id, COUNT(*) AS total_employees, COUNT(salary) AS employees_with_salary, COUNT(*) - COUNT(salary) AS employees_without_salary, AVG(salary) AS avg_salary_of_paid_employees FROM employees GROUP BY department_id;我在某医疗系统项目中patient_weight字段有40%缺失。业务方最初要求“计算平均体重”我们坚持先输出缺失率报表最终确认缺失值集中在新生儿科体重未测量于是业务规则改为“仅计算成人科室age18的平均体重”并用WHERE age 18预过滤而非用NVL(weight, 0)——后者会让平均值失真。3.4 性能关键索引如何影响GROUP BY效率GROUP BY本身不走索引但它的执行计划高度依赖WHERE条件和分组键。Oracle优化器会根据统计信息选择最优路径场景A小表或无WHERE条件→ 直接全表扫描 内存哈希分组HASH GROUP BY。场景B有高效WHERE过滤 分组键有索引→ 先索引范围扫描过滤数据再对结果集分组。场景C分组键是主键或唯一索引→ 可能触发SORT GROUP BY但通常更快。关键洞察为分组键创建索引只在以下情况有效查询有WHERE条件且WHERE条件能大幅减少数据量分组键本身是查询的过滤条件之一如WHERE dept_id 10 AND ... GROUP BY dept_id表数据量极大千万级以上且分组维度固定如按日期分组每天数据独立。反例为employees表的gender列建索引用于GROUP BY gender几乎无收益。因为gender只有M/F/O几个值索引选择性极低全表扫描反而更快。实测数据Oracle 19cemployees表120万行SELECT gender, COUNT(*) FROM employees GROUP BY gender全表扫描耗时0.18秒。同样语句但gender列上有位图索引Bitmap Index耗时0.21秒位图索引维护开销抵消了扫描优势。加WHEREWHERE hire_date DATE 2020-01-01筛选出30万行此时hire_date索引gender索引组合耗时降至0.09秒。注意位图索引Bitmap Index在OLAP场景下对低基数列如状态、性别的GROUP BY有加速效果但绝对禁止在OLTP高并发DML表上使用会引发严重的锁等待。这是DBA和开发必须共同守住的红线。4. 实操过程从一条SQL到稳定交付的完整链路4.1 构建可验证的测试数据集在写任何GROUP BY语句前我强制自己做三件事明确业务问题用一句话写下“这个SQL要回答什么问题”例“各产品线Q3销售额Top 3的销售员是谁”手绘预期结果在纸上画出3-5行示例结果标出每行的业务含义。构造最小化测试数据用WITH子句创建不超过10行的模拟数据覆盖边界情况NULL、重复值、空组。-- 示例验证“各地区平均订单额排除测试订单” WITH test_orders AS ( SELECT 1 id, North region, 1000 amount, PROD order_type FROM DUAL UNION ALL SELECT 2, North, 1500, PROD FROM DUAL UNION ALL SELECT 3, South, 800, TEST FROM DUAL UNION ALL -- 测试订单应排除 SELECT 4, South, NULL, PROD FROM DUAL UNION ALL -- 金额NULLAVG应忽略 SELECT 5, East, 2000, PROD FROM DUAL ) SELECT region, COUNT(*) AS total_orders, COUNT(amount) AS paid_orders, -- 显式统计非NULL AVG(amount) AS avg_paid_amount FROM test_orders WHERE order_type PROD -- 先过滤测试订单 GROUP BY region;预期结果REGIONTOTAL_ORDERSPAID_ORDERSAVG_PAID_AMOUNTNorth221250South10(NULL)East112000运行后立刻验证South的PAID_ORDERS0是否符合预期AVG_PAID_AMOUNT是否为NULL这比直接在百万行生产表上调试高效十倍。4.2 分步调试法把GROUP BY“拆开看”当复杂GROUP BY语句报错或结果异常我从不直接改SQL而是用分步法隔离问题步骤1剥离所有聚合只看分组键分布-- 查看分组键有哪些值各有多少行快速发现数据质量问题 SELECT region, COUNT(*) FROM orders GROUP BY region ORDER BY COUNT(*) DESC;→ 如果发现regionUNKNOWN有5000行说明数据清洗没到位需先处理。步骤2添加一个最简单的聚合验证分组逻辑-- 用COUNT(*)确认分组是否按预期工作 SELECT region, COUNT(*) FROM orders WHERE order_typePROD GROUP BY region;→ 结果行数是否等于步骤1如果不等WHERE条件可能误删了某些region的行。步骤3逐个添加聚合函数观察变化-- 加入AVG对比COUNT(*)和COUNT(amount) SELECT region, COUNT(*), COUNT(amount), AVG(amount) FROM orders WHERE order_typePROD GROUP BY region;→ 如果COUNT(*) COUNT(amount)说明该region存在amount为NULL的订单AVG结果只基于非NULL行计算。步骤4用HAVING验证业务规则-- 筛出平均订单额1000的地区 SELECT region, AVG(amount) FROM orders WHERE order_typePROD GROUP BY region HAVING AVG(amount) 1000;→ 结果是否符合业务直觉如果North平均1250但没出现检查是否有隐式类型转换如region存为CHAR(10)前后有空格。这套方法让我在某电商项目中30分钟内定位到一个困扰团队两天的BUGregion字段在ETL过程中被RTRIM截断导致North 和North被视为两个不同值分组后数据分散平均值被严重稀释。4.3 处理复杂分组ROLLUP、CUBE与GROUPING()当业务需要多维分析如“按年、按季度、按产品线”手动写多个GROUP BY太繁琐。Oracle提供高级分组扩展GROUP BY ROLLUP(a,b,c)生成(a,b,c),(a,b),(a),()四个层级的分组小计、总计。GROUP BY CUBE(a,b,c)生成所有可能的组合分组2^38个。GROUPING()函数标识某列在当前行是否为ROLLUP/CUBE生成的“合计行”。实战案例销售报表需同时展示“各产品线Q3销售额”、“各产品线Q3销售额小计”、“Q3总销售额”。SELECT CASE WHEN GROUPING(product_line) 1 THEN ALL PRODUCTS ELSE product_line END AS product_line, CASE WHEN GROUPING(quarter) 1 THEN TOTAL Q3 ELSE quarter END AS quarter, SUM(sales_amount) AS total_sales FROM sales_fact WHERE year 2023 AND quarter Q3 GROUP BY ROLLUP(product_line, quarter);结果PRODUCT_LINEQUARTERTOTAL_SALESLaptopQ3120000PhoneQ385000LaptopTOTAL Q3120000PhoneTOTAL Q385000ALL PRODUCTSTOTAL Q3205000关键技巧GROUPING()返回1表示该列是“虚汇总行”返回0表示真实数据。用CASE WHEN GROUPING(col)1 THEN Total ELSE col END是美化报表的标准做法。但注意GROUPING()只能在SELECT和HAVING中使用不能在WHERE中——因为WHERE在分组前执行。4.4 与窗口函数协同GROUP BY做不到的事GROUP BY把多行压成一行有时我们需要“既看组内聚合又看原始明细”。这时窗口函数Window Function是GROUP BY的黄金搭档。典型场景找出“每个部门薪资最高的员工”。错误做法GROUP BY MAX-- ❌ 只能返回部门和最高薪资找不到是哪个员工 SELECT department_id, MAX(salary) FROM employees GROUP BY department_id;正确做法窗口函数-- ✅ 返回每个员工的薪资以及其所在部门的最高薪资 SELECT employee_id, department_id, salary, MAX(salary) OVER (PARTITION BY department_id) AS dept_max_salary FROM employees;再进一步用RANK()或ROW_NUMBER()-- 找出每个部门薪资排名前3的员工处理并列 SELECT * FROM ( SELECT employee_id, department_id, salary, RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) AS rank_in_dept FROM employees ) WHERE rank_in_dept 3;实操心得不要陷入“GROUP BY or Window Function”的二选一。真实项目中它们是互补的。我常这样组合先用GROUP BY计算部门级指标如部门平均薪资、员工数再用窗口函数将这些指标“广播”回每个员工行用于个性化分析如“员工薪资 vs 部门平均”。这种混合模式在人力资源BI系统中几乎是标配。5. 常见问题与排查技巧实录5.1 “ORA-00979: not a GROUP BY expression” —— 最高频报错现象SQL在本地测试库能跑在生产库报此错或反之。根因分析统计信息不同生产库表统计信息陈旧优化器选择了不同的执行计划触发了更严格的语义检查。Oracle版本差异老版本如10g对表达式一致性检查更宽松。NLS设置不同字符集或排序规则NLS_SORT影响字符串比较导致GROUP BY UPPER(name)和SELECT name被视为不一致。速查表检查项操作说明1. 列名完全匹配SELECT emp_name, COUNT(*) FROM emp GROUP BY emp_name;确保SELECT和GROUP BY中列名或别名字面完全相同无空格、大小写差异2. 表达式完全一致SELECT SUBSTR(phone,1,3), COUNT(*) FROM customers GROUP BY SUBSTR(phone,1,3);不能一边用SUBSTR一边用REGEXP_SUBSTR3. 使用列别名谨慎SELECT e.name AS ename, COUNT(*) FROM emp e GROUP BY e.name;别名ename不能用于GROUP BY必须用原始表达式e.name4. 检查NLS设置SELECT * FROM NLS_SESSION_PARAMETERS WHERE PARAMETER IN (NLS_SORT,NLS_COMP);生产库若设为BINARY_CI大小写不敏感可能导致表达式匹配失败终极方案在GROUP BY中始终使用原始列或确定的表达式绝不依赖别名。这是最安全、最易维护的写法。5.2 “结果行数比预期少” —— 数据丢失的隐形杀手现象业务方说“明明有20个部门怎么报表只显示15个”排查路径检查WHERE条件是否过度过滤-- 先运行这个看有多少部门有数据 SELECT COUNT(DISTINCT department_id) FROM employees WHERE status ACTIVE;如果结果是15说明5个部门没有ACTIVE员工不是GROUP BY的问题而是业务数据状态问题。检查分组键是否存在NULL值-- NULL值会被分到单独一组但常被业务方忽略 SELECT department_id, COUNT(*) FROM employees GROUP BY department_id ORDER BY department_id NULLS FIRST;如果第一行是NULL, 50说明50个员工department_id为空他们被分在“未知部门”组但报表可能没展示NULL组。检查JOIN是否导致数据过滤-- 错误LEFT JOIN后在WHERE中过滤右表字段变相转为INNER JOIN SELECT d.department_name, COUNT(e.employee_id) FROM departments d LEFT JOIN employees e ON d.dept_id e.department_id WHERE e.hire_date DATE 2020-01-01 -- ❌ 这里e.hire_date为NULL时整行被过滤 GROUP BY d.department_name;正确写法把过滤条件移到ON子句SELECT d.department_name, COUNT(e.employee_id) FROM departments d LEFT JOIN employees e ON d.dept_id e.department_id AND e.hire_date DATE 2020-01-01 GROUP BY d.department_name;5.3 “聚合结果明显偏高/偏低” —— 数据质量警报现象平均薪资显示8万但HR系统显示全员平均5万。系统性排查清单步骤操作目的工具/命令Step 1: 抽样验证SELECT * FROM employees WHERE ROWNUM 10 ORDER BY salary DESC;快速看最高几薪是否异常如CEO薪资1000万拉高平均值SQL*Plus / SQL DeveloperStep 2: 计算分布SELECT MIN(salary), MEDIAN(salary), MAX(salary), STDDEV(salary) FROM employees;看标准差是否巨大平均值的50%提示存在离群值Oracle内置函数Step 3: 分位数分析SELECT PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY salary) AS median FROM employees;中位数比平均值更能反映典型值PERCENTILE_CONTStep 4: 分层抽样SELECT department_id, COUNT(*), AVG(salary), STDDEV(salary) FROM employees GROUP BY department_id HAVING STDDEV(salary) 10000;定位哪些部门内部薪资差异巨大需单独分析HAVING 聚合函数我的经验当平均值和中位数相差超过30%必须怀疑数据分布。在某金融科技项目中我们发现交易表的amount字段存在少量“测试数据”金额为999999999导致全表平均值失真。解决方案不是删除而是在ETL层增加WHERE amount BETWEEN 0 AND 10000000的硬性过滤并建立数据质量监控告警。5.4 性能瓶颈诊断从执行计划读懂GROUP BY当GROUP BY变慢不要猜要看执行计划Execution Plan。关键指标解读Operation: HASH GROUP BY内存哈希分组理想状态速度快内存足。Operation: SORT GROUP BY需要排序可能磁盘临时段Temp TablespaceIO慢。Bytes和Cost列数值越大消耗资源越多。TempSpc列非零表示用了临时表空间是性能红灯。优化动作增加内存调大PGA_AGGREGATE_TARGET专用于排序/哈希的内存。优化WHERE确保WHERE条件能走索引减少输入分组的数据量。避免SELECT * in GROUP BY只SELECT必要的列减少内存占用。考虑物化视图对固定维度的汇总如每日销售汇总创建物化视图查询时自动重写。实操命令-- 查看执行计划 EXPLAIN PLAN FOR SELECT department_id, AVG(salary) FROM employees GROUP BY department_id; SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY); -- 查看实时执行统计需有权限 SELECT * FROM V$SQL_PLAN_STATISTICS_ALL WHERE SQL_ID your_sql_id AND OPERATION LIKE %GROUP%;最后分享一个血泪教训在某电信项目中一个报表GROUP BY耗时从2秒涨到45秒。执行计划显示TempSpc2GB。排查发现开发人员在WHERE中写了WHERE to_char(create_date,YYYYMMDD) 20231001导致create_date索引失效全表扫描后2000万行数据进GROUP BY。改成WHERE create_date DATE 2023-10-01 AND create_date DATE 2023-10-02耗时回到1.8秒。永远优先让过滤条件走索引这是GROUP BY性能的基石。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑