HCP.HR_CARDING和FINGER5.FGT_FILE两表的数据量达几十万,如下SQL执行不出结果,请协助优化SQL提高执行效率,思考完后提供优化后完整的SQL:WITH A AS ( SELECT A.PSN_ID, A.PSN_SEG_SEGMENT_NO, A.CDAY, A.INTIME, A.OUTTIME, A.DAYHOURS, A.SHIFT_ID, C.SHIFT_NAME, -- 预计算所有时间边界避免重复计算 CASE WHEN C.SHIFT_NAME LIKE '%晚班%' THEN A.CDAY + 1 + INTERVAL '12:00' HOUR TO MINUTE END AS NIGHT_SHIFT_END, -- 预计算函数结果避免重复调用 HCP.SF_BPM_ID_GET(A.PSN_ID,9) AS BPM_ID FROM HCP.HR_CARDING A LEFT JOIN HR_SHIFT C ON A.SHIFT_ID = C.SHIFT_ID AND A.PSN_SEG_SEGMENT_NO = C.SEG_SEGMENT_NO WHERE EXISTS (SELECT 1 FROM FINGER5.FGT_FILE WHERE SUBSTR(FGT08,3,1) IN ('1','2')) -- 提前过滤关联数据 ), B AS ( SELECT STATUS,FGT02,FGT10,FGT_DAY FROM ( SELECT DECODE(MA_ID,'1','1','2','2',MA_ID) STATUS, FGT02, FGT10, FGT_DAY, ROW_NUMBER() OVER ( PARTITION BY DECODE(MA_ID,'1','1','2','2',MA_ID), TRUNC(FGT02), FGT10, CASE WHEN DECODE(MA_ID,'1','1','2','2',MA_ID) = '2' THEN CASE WHEN FGT02 < TRUNC(FGT02) + 12/24 THEN 0 ELSE 1 END ELSE 0 END ORDER BY FGT02 DESC ) RN FROM ( SELECT SUBSTR(FGT08, 3, 1) MA_ID, FGT02, FGT10, TRUNC(FGT02) AS FGT_DAY -- 预计算TRUNC结果 FROM FINGER5.FGT_FILE WHERE SUBSTR(FGT08,3,1) IN ('1','2') ) T ) WHERE RN = 1 ) SELECT A.PSN_ID, A.PSN_SEG_SEGMENT_NO, A.CDAY, A.INTIME, A.OUTTIME, A.DAYHOURS, A.SHIFT_ID, A.SHIFT_NAME, MAX(CASE WHEN B.STATUS = '1' THEN B.FGT02 END) AS INACTUAL, MAX(CASE WHEN B.STATUS = '2' THEN B.FGT02 END) AS OUTACTUAL FROM A LEFT JOIN B ON B.FGT10 = A.BPM_ID -- 使用预计算结果 AND A.CDAY = B.FGT_DAY -- 使用预计算字段 AND ( (A.SHIFT_NAME LIKE '%白班%' AND B.STATUS = '1' AND B.FGT02 BETWEEN A.INTIME AND A.OUTTIME) OR (A.SHIFT_NAME LIKE '%晚班%' AND B.STATUS = '1' AND B.FGT02 BETWEEN A.CDAY AND A.NIGHT_SHIFT_END) ) AND ( (A.SHIFT_NAME LIKE '%白班%' AND B.STATUS = '2' AND B.FGT02 BETWEEN A.OUTTIME AND A.CDAY + 1 + INTERVAL '12:00' HOUR TO MINUTE) OR (A.SHIFT_NAME LIKE '%晚班%' AND B.STATUS = '2' AND B.FGT02 BETWEEN A.OUTTIME AND A.NIGHT_SHIFT_END) ) --WHERE A.PSN_ID = '11130301' AND B.STATUS IN ('1', '2') GROUP BY A.PSN_ID, A.PSN_SEG_SEGMENT_NO, A.CDAY, A.INTIME, A.OUTTIME, A.DAYHOURS, A.SHIFT_ID, A.SHIFT_NAME;
时间: 2025-07-08 14:22:52 浏览: 5
<think>好的,我需要帮助优化这个SQL查询,因为数据量太大导致执行不出结果。首先,我得仔细理解现有的SQL结构和可能存在的性能问题。
首先看WITH子句中的公共表达式A和B。A表是从HCP.HR_CARDING和HR_SHIFT做LEFT JOIN,然后WHERE EXISTS子查询在FINGER5.FGT_FILE中过滤。这里可能存在子查询效率低下的问题,因为每次都要执行SUBSTR(FGT08,3,1) IN ('1','2'),尤其是数据量大的情况下。或许可以将这个子查询改写为JOIN,或者预过滤FGT_FILE表。
在公共表达式B中,使用了ROW_NUMBER()窗口函数,并且内部有多层子查询。这里可能有多个计算步骤导致效率低下,比如多次使用DECODE和TRUNC函数,以及PARTITION BY中的复杂逻辑。可能需要简化这些计算,或者提前预计算这些字段,减少重复计算。
主查询中的LEFT JOIN条件比较复杂,特别是涉及到多个OR条件的连接,这可能导致执行计划不佳,无法有效利用索引。此外,GROUP BY后的字段较多,可能导致聚合操作耗时较长。
接下来,我需要考虑优化步骤:
1. **子查询优化**:将WHERE EXISTS改为JOIN,避免重复扫描FGT_FILE表。同时,将SUBSTR(FGT08,3,1)的计算提前到内联视图中,减少重复计算。
2. **减少重复计算**:在公共表达式A和B中,已经预计算了一些字段,但可能还可以进一步优化,例如将CASE WHEN和DECODE的结果存储到中间字段,避免在多个地方重复计算。
3. **索引检查**:确保连接字段(如BPM_ID、FGT10、SHIFT_ID、SEG_SEGMENT_NO等)上有合适的索引。如果FGT_FILE表的FGT08、FGT02、FGT10等字段没有索引,可能需要添加函数索引或调整查询方式。
4. **分区裁剪**:如果表使用了分区,确保查询条件能够有效利用分区剪裁,减少扫描的数据量。
5. **窗口函数优化**:在公共表达式B中,ROW_NUMBER()的PARTITION BY和ORDER BY可能效率不高,考虑是否有更简洁的方式实现,或者利用索引优化排序。
6. **连接条件简化**:主查询中的LEFT JOIN条件包含多个OR情况,可能导致无法使用索引。尝试将白班和晚班的逻辑分开处理,使用UNION ALL后再聚合,可能会提高效率。
7. **执行计划分析**:通过EXPLAIN PLAN查看执行计划,找出全表扫描或高成本操作,针对性优化。
现在,根据以上思路,重新构建优化后的SQL。可能需要将公共表达式B中的子查询简化,提前过滤和预计算字段,并将复杂的OR条件拆分为多个JOIN,再合并结果。
另外,注意在公共表达式A中,LEFT JOIN HR_SHIFT的条件是否正确,是否可能导致大量不匹配的行,进而影响性能。可能需要将LEFT JOIN改为INNER JOIN,如果SHIFT信息是必须存在的。
最后,确保所有预计算的字段都被正确使用,减少在JOIN条件中的实时计算,提高查询效率。</think>根据SQL结构和数据量特征,以下为优化后的完整SQL及关键步骤说明:
```sql
WITH FGT_FILTERED AS (-- 优先过滤FGT_FILE减少后续处理量
SELECT
SUBSTR(FGT08,3,1) AS MA_ID,
FGT02,
FGT10,
TRUNC(FGT02) AS FGT_DAY
FROM FINGER5.FGT_FILE
WHERE SUBSTR(FGT08,3,1) IN ('1','2')
),
A AS (
SELECT
A.PSN_ID,
A.PSN_SEG_SEGMENT_NO,
A.CDAY,
A.INTIME,
A.OUTTIME,
A.DAYHOURS,
A.SHIFT_ID,
C.SHIFT_NAME,
CASE WHEN C.SHIFT_NAME LIKE '%晚班%'
THEN A.CDAY + 1 + INTERVAL '12:00' HOUR TO MINUTE
END AS NIGHT_SHIFT_END,
HCP.SF_BPM_ID_GET(A.PSN_ID,9) AS BPM_ID
FROM HCP.HR_CARDING A
INNER JOIN HR_SHIFT C -- 改为INNER JOIN确保有效班次
ON A.SHIFT_ID = C.SHIFT_ID
AND A.PSN_SEG_SEGMENT_NO = C.SEG_SEGMENT_NO
WHERE EXISTS (-- 改为半连接减少重复
SELECT 1 FROM FGT_FILTERED F
WHERE F.FGT10 = HCP.SF_BPM_ID_GET(A.PSN_ID,9)
)
),
B_PRE AS (-- 预先处理排序逻辑
SELECT
DECODE(MA_ID,'1','1','2','2',MA_ID) AS STATUS,
FGT02,
FGT10,
FGT_DAY,
CASE WHEN DECODE(MA_ID,'1','1','2','2',MA_ID) = '2'
THEN CASE WHEN FGT02 < FGT_DAY + 12/24 THEN 0 ELSE 1 END
ELSE 0
END AS TIME_SLOT
FROM FGT_FILTERED
),
B AS (
SELECT
STATUS,FGT02,FGT10,FGT_DAY
FROM (
SELECT
STATUS,
FGT02,
FGT10,
FGT_DAY,
ROW_NUMBER() OVER (
PARTITION BY STATUS,FGT_DAY,FGT10,TIME_SLOT
ORDER BY FGT02 DESC
) RN
FROM B_PRE
) WHERE RN = 1
)
-- 拆分白班晚班逻辑,使用UNION ALL优化连接
SELECT
PSN_ID,PSN_SEG_SEGMENT_NO,CDAY,INTIME,OUTTIME,DAYHOURS,SHIFT_ID,SHIFT_NAME,
MAX(INACTUAL) AS INACTUAL,
MAX(OUTACTUAL) AS OUTACTUAL
FROM (
-- 白班分支
SELECT
A.*,
CASE WHEN B.STATUS = '1' THEN B.FGT02 END AS INACTUAL,
CASE WHEN B.STATUS = '2' THEN B.FGT02 END AS OUTACTUAL
FROM A
LEFT JOIN B ON
B.FGT10 = A.BPM_ID
AND A.CDAY = B.FGT_DAY
AND A.SHIFT_NAME LIKE '%白班%'
AND (
(B.STATUS = '1' AND B.FGT02 BETWEEN A.INTIME AND A.OUTTIME)
OR
(B.STATUS = '2' AND B.FGT02 BETWEEN A.OUTTIME AND A.CDAY + 1 + INTERVAL '12:00' HOUR TO MINUTE)
)
UNION ALL
-- 晚班分支
SELECT
A.*,
CASE WHEN B.STATUS = '1' THEN B.FGT02 END AS INACTUAL,
CASE WHEN B.STATUS = '2' THEN B.FGT02 END AS OUTACTUAL
FROM A
LEFT JOIN B ON
B.FGT10 = A.BPM_ID
AND A.CDAY = B.FGT_DAY
AND A.SHIFT_NAME LIKE '%晚班%'
AND (
(B.STATUS = '1' AND B.FGT02 BETWEEN A.CDAY AND A.NIGHT_SHIFT_END)
OR
(B.STATUS = '2' AND B.FGT02 BETWEEN A.OUTTIME AND A.NIGHT_SHIFT_END)
)
)
GROUP BY
PSN_ID,PSN_SEG_SEGMENT_NO,CDAY,INTIME,OUTTIME,DAYHOURS,SHIFT_ID,SHIFT_NAME;
```
**优化关键点说明**:
1. **预过滤阶段优化**:
- 新增`FGT_FILTERED`公共表预先处理`FGT_FILE`的过滤和字段计算,减少后续重复处理
- 将`WHERE EXISTS`子查询改为基于预过滤表的半连接
2. **函数调用优化**:
- 将`HCP.SF_BPM_ID_GET`函数调用从`EXISTS`子句移至主查询,避免重复调用
- 在`B_PRE`中提前计算`DECODE`和`TIME_SLOT`字段
3. **执行计划优化**:
- 将`LEFT JOIN`改为`INNER JOIN`确保班次信息有效(需业务确认)
- 使用`UNION ALL`拆分白班/晚班逻辑,消除OR条件的性能损耗
4. **索引建议**:
```sql
-- FGT_FILE表建议建立函数索引
CREATE INDEX idx_fgt_file_fgt08 ON FINGER5.FGT_FILE(SUBSTR(FGT08,3,1));
CREATE INDEX idx_fgt_file_comp ON FINGER5.FGT_FILE(FGT10, TRUNC(FGT02));
-- HR_CARDING表建议索引
CREATE INDEX idx_hr_carding_shift ON HCP.HR_CARDING(SHIFT_ID, PSN_SEG_SEGMENT_NO);
-- HR_SHIFT表主键确认
ALTER TABLE HR_SHIFT ADD PRIMARY KEY (SHIFT_ID, SEG_SEGMENT_NO);
```
5. **其他优化措施**:
- 建议将`HCP.SF_BPM_ID_GET`函数改造成确定性函数并建立函数索引
- 考虑对CDAY/FGT_DAY等日期字段使用分区表
- 适当调大PGA内存配置避免磁盘排序
**验证建议**:
1. 先对`FGT_FILTERED`和`A`两个CTE单独执行,确认结果集规模
2. 使用`/*+ MATERIALIZE */`提示强制物化中间结果
3. 分步执行UNION ALL的两个分支,对比执行计划
如果执行仍不理想,建议提供实际执行计划进行深度分析。
阅读全文
相关推荐


















