with temp as ( select case when apply_dept='0303' then '0303' else apply_dept_hs end VISIT_DEPT ,apply_doctor DOCTOR_CODE ,d.idcard idcard ,case when c.s_date is not null and charge_code<>'023758' and gh.ampm is null then a.charge_amount*a.herbal_amount end as 节假日门诊人次 ,case when c.s_date is not null and charge_code<>'023758' and gh.ampm is null then a.charge_price*a.charge_amount*a.herbal_amount end as 节假日诊查费 ,case when c.s_date is not null and charge_code='023758' and gh.ampm is null then a.charge_amount*a.herbal_amount end as 节假日急诊人次 ,case when c.s_date is not null and charge_code='023758' and gh.ampm is null then a.charge_price*a.charge_amount*a.herbal_amount end as 节假日急诊诊查费 ,case when c.s_date is null and charge_code<>'023758' and gh.ampm is null then a.charge_amount*a.herbal_amount end as 非节假日门诊人次 ,case when c.s_date is null and charge_code<>'023758' and gh.ampm is null then a.charge_price*a.charge_amount*a.herbal_amount end as 非节假日诊查费 ,case when c.s_date is null and charge_code='023758' and gh.ampm is null then a.charge_amount*a.herbal_amount end as 非节假日急诊人次 ,case when c.s_date is null and charge_code='023758' and gh.ampm is null then a.charge_price*a.charge_amount*a.herbal_amount end as 非节假日急诊诊查费 ,case when gh.ampm is not null then a.charge_amount*a.herbal_amount else 0 end as 夜诊人次 ,case when gh.ampm is not null then a.charge_price*a.charge_amount*a.herbal_amount else 0 end as 夜诊金额 --增加互联网病人就诊数 ,0 as 互联网人次数 ,0 as 互联网诊察费 from v_mz_detail a join VIEW_MZ_VISIT b on a.p_id=b.p_id and a.times=b.times left join sys_holiday c on trunc(charge_date)=trunc(s_date) left join gh_base gh on trunc(gh.request_date)=trunc(b.VISIT_DATE) and gh.doctor_code=apply_doctor and gh.ampm='y' left join dic_doctor d on a.APPLY_DOCTOR=d.doctsn where a.charge_code in (select charge_code from gh_zd_clinic_charge union all select code from wj_charge_item where code in ( '158285', '158288', '023745', '023746', '023747', '023748', '920738' ) ) and charge_status<>1 and a.bill_code='2' and report_date>=to_date('20250701','yyyymmdd') AND report_date<to_date('20250703','yyyymmdd') and (exists (select 1 from gh_base gh where trunc(gh.request_date)=trunc(b.VISIT_DATE) and gh.doctor_code=apply_doctor ) or apply_dept_hs like '0115%' ) union all select case when g.dept_sn='0303' then '0303' else g.dept_sn_hs end VISIT_DEPT ,y.DOCTOR_CODE DOCTOR_CODE ,d.idcard idcard ,case when c.s_date is not null and charge_code<>'023758' and g.AMPM<>'y' then x.charge_amount end as 节假日门诊人次 ,case when c.s_date is not null and charge_code<>'023758' and g.AMPM<>'y' then x.charge_price*x.charge_amount end as 节假日诊查费 ,case when c.s_date is not null and charge_code='023758' and g.AMPM<>'y' then x.charge_amount end as 节假日急诊人次 ,case when c.s_date is not null and charge_code='023758' and g.AMPM<>'y' then x.charge_price*x.charge_amount end as 节假日急诊诊查费 ,case when c.s_date is null and charge_code<>'023758' and g.AMPM<>'y' then x.charge_amount end as 非节假日门诊人次 ,case when c.s_date is null and charge_code<>'023758' and g.AMPM<>'y' then x.charge_price*x.charge_amount end as 非节假日诊查费 ,case when c.s_date is null and charge_code='023758' and g.AMPM<>'y' then x.charge_amount end as 非节假日急诊人次 ,case when c.s_date is null and charge_code='023758' and g.AMPM<>'y' then x.charge_price*x.charge_amount end as 非节假日急诊诊查费 ,case when g.AMPM='y' then x.charge_amount else 0 end as 夜诊人次 ,case when g.AMPM='y' then x.charge_price*x.charge_amount else 0 end as 夜诊金额 --增加互联网病人就诊数 ,CASE WHEN g. CLINIC_TYPE in ('0P','0Q','6P','2P','3P','4P','5P') THEN x.charge_amount end as 互联网人次数 ,CASE WHEN g. CLINIC_TYPE in ('0P','0Q','6P','2P','3P','4P','5P') THEN x.charge_price*x.charge_amount end as 互联网诊察费 from VIEW_GH_DETAIL x join v_mz_visit y on x.p_id=y.p_id and x.times=y.times and x.CHARGE_AMOUNT>0 join gh_base_child gb on x.P_ID=gb.p_id and x.TIMES=gb.times join v_gh_base_zh g on g.request_sn=gb.request_sn left join dic_doctor d on y.doctor_code=d.doctsn left join sys_holiday c on trunc(advice_time)=trunc(s_date) where x.bill_code='2' and x.charge_code in (select charge_code from gh_zd_clinic_charge union all select code from wj_charge_item where code in ( '158285', '158288', '023745', '023746', '023747', '023748', '920738' ) ) and gb.charge_flag='1' ------已缴费 and nvl(gb."COMMENT",' ') not LIKE '%转诊,作废原明细%' ------转诊有两条记录,排除转诊前原挂号记录 and g.dept_sn not IN ('0305','030501','030504','03060301','030601','070101','0707','9901','0616') and advice_time>=to_date('20250701','yyyymmdd') AND advice_time<to_date('20250703','yyyymmdd') and not exists ( select 1 from GH_DEPOSIT de where de.p_id=x.P_ID and de.times=x.TIMES and de.account_sn=x.ACCOUNT_SN and de.detail_sn=x.DETAIL_SN and de.jzdw='1386') union all select case when g.dept_sn='0303' then '0303' else g.dept_sn_hs end VISIT_DEPT ,y.DOCTOR_CODE DOCTOR_CODE ,d.idcard idcard ,case when c.s_date is not null and charge_code<>'023758' and g.AMPM<>'y' then x.charge_amount end as 节假日门诊人次 ,case when c.s_date is not null and charge_code<>'023758' and g.AMPM<>'y' then x.charge_price*x.charge_amount end as 节假日诊查费 ,case when c.s_date is not null and charge_code='023758' and g.AMPM<>'y' then x.charge_amount end as 节假日急诊人次 ,case when c.s_date is not null and charge_code='023758' and g.AMPM<>'y' then x.charge_price*x.charge_amount end as 节假日急诊诊查费 ,case when c.s_date is null and charge_code<>'023758' and g.AMPM<>'y' then x.charge_amount end as 非节假日门诊人次 ,case when c.s_date is null and charge_code<>'023758' and g.AMPM<>'y' then x.charge_price*x.charge_amount end as 非节假日诊查费 ,case when c.s_date is null and charge_code='023758' and g.AMPM<>'y' then x.charge_amount end as 非节假日急诊人次 ,case when c.s_date is null and charge_code='023758' and g.AMPM<>'y' then x.charge_price*x.charge_amount end as 非节假日急诊诊查费 ,case when g.AMPM='y' then x.charge_amount else 0 end as 夜诊人次 ,case when g.AMPM='y' then x.charge_price*x.charge_amount else 0 end as 夜诊金额 --增加互联网病人就诊数 ,CASE WHEN g. CLINIC_TYPE in ('0P','0Q','6P','2P','3P','4P','5P') THEN x.charge_amount end as 互联网人次数 ,CASE WHEN g. CLINIC_TYPE in ('0P','0Q','6P','2P','3P','4P','5P') THEN x.charge_price*x.charge_amount end as 互联网诊察费 from VIEW_GH_DETAIL x join v_mz_visit y on x.p_id=y.p_id and x.times=y.times and x.CHARGE_AMOUNT<0 join gh_base_child gb on x.P_ID=gb.p_id and x.TIMES=gb.times join v_gh_base_zh g on g.request_sn=gb.request_sn left join dic_doctor d on y.doctor_code=d.doctsn left join sys_holiday c on trunc(advice_time)=trunc(s_date) where x.bill_code='2' and x.charge_code in (select charge_code from gh_zd_clinic_charge union all select code from wj_charge_item where code in ( '158285', '158288', '023745', '023746', '023747', '023748', '920738' ) ) and gb.charge_flag='1' ------已缴费 and nvl(gb."COMMENT",' ') LIKE '%退号%' ------退号 and nvl(gb."COMMENT",' ') not LIKE '%转诊,作废原明细%' ------转诊有两条记录,排除转诊前原挂号记录 and g.dept_sn not IN ('0305','030501','030504','03060301','030601','070101','0707','9901','0616') and case when x.charge_date<ADVICE_TIME then advice_time else x.charge_date end>=to_date('20250701','yyyymmdd') AND case when x.charge_date<ADVICE_TIME then advice_time else x.charge_date end<to_date('20250703','yyyymmdd') and not exists ( select 1 from GH_DEPOSIT de where de.p_id=x.P_ID and de.times=x.TIMES and de.account_sn=x.ACCOUNT_SN and de.detail_sn=x.DETAIL_SN and de.jzdw='1386') ) select decode(grouping(aa.visit_dept),1,'',aa.visit_dept) as 科室编码, decode(grouping(aa.visit_dept)+grouping(bb.name),2,'总合计:',1,bb.name||'小计:',bb.name) as 科室, decode(grouping(cc.name),1,'',cc.name) as 开方医生, decode(grouping(aa.DOCTOR_CODE),1,'',aa.DOCTOR_CODE) as 医生工号, decode(grouping(aa.idcard),1,'',aa.idcard) as 医生身份证, sum(nvl(aa.节假日门诊人次,0)) as 节假日门诊人次, sum(nvl(aa.节假日诊查费,0)) as 节假日诊查费, sum(nvl(aa.节假日急诊人次,0)) as 节假日急诊人次, sum(nvl(aa.节假日急诊诊查费,0)) as 节假日急诊诊查费, sum(nvl(aa.非节假日门诊人次,0)) as 非节假日门诊人次, sum(nvl(aa.非节假日诊查费,0)) as 非节假日诊查费, sum(nvl(aa.非节假日急诊人次,0)) as 非节假日急诊人次, sum(nvl(aa.非节假日急诊诊查费,0)) as 非节假日急诊诊查费 ,sum(nvl(aa.夜诊人次,0)) as 夜诊人次 ,sum(nvl(aa.夜诊金额,0)) as 夜诊金额 --增加互联网病人就诊数 ,sum(nvl(aa.互联网人次数,0)) as 互联网人次数 ,sum(nvl(aa.互联网诊察费,0)) as 互联网诊察费 from temp aa left join dic_dept_code bb on aa.visit_dept=bb.dept_sn left join dic_employee cc on aa.DOCTOR_CODE=cc.emp_sn group by grouping sets((aa.visit_dept,aa.DOCTOR_CODE,idcard, bb.name, cc.name),(),(bb.name)) order by bb.name,aa.visit_dept nulls last; 查询缓慢
时间: 2025-07-21 18:02:23 浏览: 17
<think>首先,用户的问题是:“我想优化SQL查询性能 请问优化Oracle SQL查询性能,该查询涉及多个子查询、联合和复杂条件过滤但执行缓慢”。我需要基于系统级指令来构建回答。
系统级指令:
- 行内数学表达式使用$...$,例如:$x^2$。
- 独立公式使用$$...$$并单独成段。
- LaTeX语法正确。
- 使用中文回答。
- 生成相关问题。
- 回答中引用的段落末尾自然地添加引用标识,例如:[^1]
用户可见层:
- 回答结构清晰,帮助用户逐步解决问题。
- 尽量保证回答真实可靠。
参考站内引用:
- 引用[1]: 关于查询数据量大的表,分段分页查询。
- 引用[2]: COUNT(*)与COUNT(列)的性能比较。
- 引用[3]: Mysql与Oracle查询优化,包括执行计划和索引。
- 引用[4]: SQL优化成本层级:硬件>系统配置>数据库表结构>SQL及索引。
用户的问题是关于优化Oracle SQL查询性能,特别是涉及多个子查询、联合和复杂条件过滤导致执行缓慢。我需要提供实用的优化建议。
回答结构:
1. **引言**:简要介绍优化SQL查询的重要性,并确认用户的问题。
2. **逐步优化建议**:提供结构清晰的步骤,帮助用户逐步解决问题。
- 分析执行计划。
- 优化索引。
- 重写查询(如避免子查询、使用JOIN等)。
- 其他优化技巧。
3. **引用相关引用**:在适当的地方自然地添加引用标识。
4. **生成相关问题**:在回答末尾生成相关问题。
确保使用中文回答。
数学表达式:用户的问题是关于SQL的,可能没有数学公式,但如果有涉及计算的地方,使用LaTeX格式。例如,如果有性能计算,如$O(n \log n)$,但SQL优化中可能不直接相关。我可以提到算法复杂度,但优先专注于SQL优化。
相关问题生成:在回答末尾添加“相关问题”部分。
最终回答结构:
- 开头:直接回应问题。
- 主体:分步骤优化建议。
- 结尾:总结和生成相关问题。
现在,脑力 storm 优化建议:
1. **使用执行计划**:分析查询的执行计划来识别瓶颈。Oracle有EXPLAIN PLAN或SQL Developer的工具。
2. **索引优化**:确保相关列有索引,特别是WHERE、JOIN、ORDER BY子句中的列。
3. **避免不必要的子查询**:将子查询转换为JOIN,因为JOIN通常更高效。
4. **使用临时表或物化视图**:对于复杂查询,预先计算部分结果。
5. **分页查询优化**:如果查询大量数据,使用ROW_NUMBER()或ROWNUM进行分页,如引用[1]所示。
6. **函数和操作符优化**:避免在索引列上使用函数,以免索引失效。
7. **统计信息更新**:确保表的统计信息是最新的,以便优化器做出正确决策。
8. **查询重写**:简化条件,使用EXISTS代替IN,避免SELECT *,只选择需要的列。
9. **硬件和配置**:如引用[4]所述,优化成本层级,但优先SQL级优化。
引用整合:
- 在讨论分页时,引用[1]。
- 在讨论COUNT优化时,引用[2]。
- 在讨论执行计划和索引时,引用[3]。
- 在讨论优化层级时,引用[4]。
生成相关问题:
1. 如何获取和解读Oracle的执行计划?
2. 索引优化的最佳实践是什么?
3. 如何优化涉及子查询的SQL查询?
确保回答真实可靠:基于SQL优化最佳实践。</think>### Oracle SQL查询性能优化指南
您的查询涉及多个子查询、联合(JOIN)和复杂条件过滤,导致执行缓慢。优化这类查询的关键在于减少计算开销、利用索引和优化查询结构。根据SQL优化成本层级(硬件>系统配置>数据库表结构>SQL及索引),我们优先从SQL及索引层面入手,因为这是成本最低且见效最快的方式[^4]。以下我将以**逐步优化步骤**的形式,帮助您诊断和解决问题。每个步骤都基于Oracle数据库的最佳实践,确保建议真实可靠。
#### 步骤1: 分析执行计划,识别瓶颈
- **为什么重要?** 执行计划显示Oracle如何处理查询,包括操作顺序、索引使用和资源消耗。复杂查询可能涉及全表扫描(FULL SCAN)或低效的子查询执行。
- **如何操作?**
使用`EXPLAIN PLAN`命令生成执行计划:
```sql
EXPLAIN PLAN FOR
SELECT /* 您的原始查询 */ FROM ... WHERE ...;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
```
- **关键指标**:检查`COST`(成本估计)、`ROWS`(行数估计)和`OPERATION`(如`TABLE ACCESS FULL`表示全表扫描)。优化目标是减少高成本操作。
- **优化建议**:
- 如果出现全表扫描,添加索引(见步骤2)。
- 如果子查询导致嵌套循环(NESTED LOOPS),考虑重写为JOIN(见步骤3)。
- 引用[3]提到,查询类型(如`SUBQUERY`或`UNION`)会影响性能,优先优化标记为高成本的子部分[^3]。
#### 步骤2: 优化索引策略
- **为什么重要?** 索引能大幅加速数据检索,尤其对WHERE、JOIN和ORDER BY子句。但过多索引会降低写入性能,需平衡。
- **如何操作?**
- **添加缺失索引**:针对过滤条件中的列创建索引。例如,如果查询有`WHERE column1 = value`,创建单列索引:
```sql
CREATE INDEX idx_column1 ON your_table(column1);
```
- **使用复合索引**:如果多个列常一起使用(如`WHERE col1 AND col2`),创建复合索引:
```sql
CREATE INDEX idx_composite ON your_table(col1, col2);
```
- **避免索引失效**:不要在索引列上使用函数或计算(例如`WHERE UPPER(column) = 'VALUE'`会导致索引失效)。
- **维护索引**:定期重建碎片化索引(`ALTER INDEX ... REBUILD`),并更新统计信息:
```sql
EXEC DBMS_STATS.GATHER_TABLE_STATS('YOUR_SCHEMA', 'YOUR_TABLE');
```
- **性能对比**:如引用[2]所示,`COUNT(column)`可能比`COUNT(*)`快,因为前者跳过NULL值,减少I/O[^2]。测试不同索引对查询的影响。
#### 步骤3: 重写查询,简化结构
- **为什么重要?** 多个子查询和联合会增加解析和执行时间。重写查询可以减少嵌套层级,提升效率。
- **如何操作?**
- **将子查询转换为JOIN**:子查询(尤其是关联子查询)易导致性能问题。例如,将:
```sql
SELECT * FROM table1 WHERE id IN (SELECT id FROM table2 WHERE condition)
```
重写为:
```sql
SELECT table1.* FROM table1 JOIN table2 ON table1.id = table2.id WHERE table2.condition
```
JOIN通常更高效,因为优化器能更好地利用索引。
- **使用WITH子句(CTE)**:对于复杂子查询,用CTE预先计算中间结果,减少重复执行:
```sql
WITH temp AS (SELECT /* 子查询 */ FROM ...)
SELECT * FROM temp JOIN ... WHERE ...
```
- **优化条件过滤**:
- 避免`OR`条件,改用`UNION ALL`(如果逻辑允许),因为`OR`可能导致全表扫描。
- 使用`EXISTS`代替`IN`,尤其当子查询返回大量数据时,`EXISTS`在找到匹配后即停止扫描。
- 只选择必要列,避免`SELECT *`。
- **分页优化**:如果查询大量数据,使用`ROW_NUMBER()`分页,如引用[1]所示,避免全量扫描:
```sql
SELECT * FROM (
SELECT ROW_NUMBER() OVER (ORDER BY id) AS rowid, *
FROM your_table
) WHERE rowid > 100000 AND rowid <= 100050; -- 示例分页
```
这显著减少I/O开销[^1]。
#### 步骤4: 其他高级优化技巧
- **使用物化视图**:如果查询涉及聚合或复杂计算,创建物化视图预存结果:
```sql
CREATE MATERIALIZED VIEW mv_name AS SELECT /* 查询 */;
```
定期刷新视图,适合读多写少的场景。
- **调整配置参数**:
- 增大`SORT_AREA_SIZE`和`PGA_AGGREGATE_TARGET`,优化排序和哈希操作。
- 使用并行查询(`PARALLEL`提示)对大表加速,但需测试资源消耗。
- **分批处理**:对海量数据,将查询拆分为小批次(如循环处理10,000行),减少内存压力。
- **监控工具**:利用Oracle AWR报告或SQL Tuning Advisor自动诊断。
#### 总结
优化核心是:**先分析执行计划定位问题,再通过索引和查询重写减少开销**。对于您的场景,优先重写子查询为JOIN、添加复合索引,并使用分页策略。优化后,测试查询时间,如果仍慢,考虑数据库表结构优化(如分区表)或硬件升级[^4]。实际效果取决于数据量和环境,建议在测试环境验证变更。
如果您提供查询片段,我可以给出更具体的重写示例。
### 相关问题
1. 如何解读Oracle执行计划中的关键指标(如COST和ROWS)?
2. 在Oracle中,索引失效的常见原因有哪些?如何避免?
3. 物化视图与普通视图在性能优化中有何区别?何时使用?
[^1]: 引用[1]:3、查询数据量大的表 会造成查询缓慢。主要的原因是扫描行数过多。这个时候可以通过程序,分段分页进行查询,循环遍历,将结果合并处理进行展示。要查询100000到100050的数据,如下: SELECT * FROM (SELECT ROW_NUMBER() OVER(ORDER BY ID ASC) AS rowid,* FROM infoTab)t WHERE t.rowid > 100000 AND t.rowid <= 100050
[^2]: 引用[2]:最近在看梁敬彬老师关于Oracle性能优化的一些案例,在这里做一些简单的总结 1.COUNT(*)与COUNT(列)哪个更快 drop table t purge; create table t as ...
[^3]: 引用[3]:Mysql与Oracle查询优化(执行计划 ,索引)。查询的类型,主要是用于区分普通查询、联合查询、子查询等。 SIMPLE:简单的 select 查询,查询中不包含子查询或者 unionPRIMARY:查询中包含子部分,最外层查询则被标记为 primarySUBQUERY/MATERIALIZED:SUBQUERY 表示在 select 或 where 列表中包含了子查询,MATERIALIZED**:**表示 where 后面 in 条件的子查询UNION:表示 union 中的第二个或后面的 select 语句UNION RESULT:union 的结果 执行计划的 table
[^4]: 引用[4]:如果你的团队在 SQL 优化这方面搞得很优秀,对你们整个大型系统可用性方面无疑是一个质的跨越,真的能让你们老板省下不止几沓子钱。 优化成本:硬件>系统配置>数据库表结构>SQL 及索引。
阅读全文
相关推荐












