OracleSQL优化案例-3

复制代码
WITH T1 AS (
SELECT a.SECU_ID || cast(TO_CHAR(a.TRD_DT,'YYYY-MM-DD')  AS varchar2(200)) AS ONE_ID
FROM STK_HK_EXCH_QUOT a JOIN BASE_SECURITY_INFO b ON a.SECU_ID = b.SECURITY_ID  AND a.ROR_FCTR IS NOT null
where a.IS_DELETE = 0 AND b.IS_DELETE = 0 
AND a.TRD_DT >= #{curt_running_dt} 
AND a.TRD_DT <= #{next_running_dt}
),
T2 AS (
SELECT 
			ID,SECU_ID,TRD_DT,TRD_STS,CPRC,DAY_CHG,DAY_CHG_RAT,TUROV_RATE,DAY_AMP,FMKT_VAL,TMKT_VAL,PE,PE_DYM,PETTM,PB,PB_NEW,PC,PC_DYM,PCTTM,PS,PS_DYM,PSTTM,TRD_AMT_RW,
            TRD_VOL_RW,HPRC_RW,LPRC_RW,CHG_RAT_RW,TUROV_RATE_RW,AMP_RAT_RW,TRD_AMT_AVG_RW,TUROV_RATE_AVG_RW,CHG_RW,TRD_AMT_TW,TRD_VOL_TW,HPRC_TW,LPRC_TW,CHG_RAT_TW,TUROV_RATE_TW,
            AMP_RAT_TW,TRD_AMT_AVG_TW,TUROV_RATE_AVG_TW,CHG_TW,TRD_AMT_RM,TRD_VOL_RM,HPRC_RM,LPRC_RM,CHG_RAT_RM,TUROV_RATE_RM,AMP_RAT_RM,TRD_AMT_AVG_RM,TUROV_RATE_AVG_RM,CHG_RM,
            TRD_AMT_TM,TRD_VOL_TM,HPRC_TM,LPRC_TM,CHG_RAT_TM,TUROV_RATE_TM,AMP_RAT_TM,TRD_AMT_AVG_TM,TUROV_RATE_AVG_TM,CHG_TM,TRD_AMT_R3M,TRD_VOL_R3M,HPRC_R3M,LPRC_R3M,
            CHG_RAT_R3M,TUROV_RATE_R3M,AMP_RAT_R3M,TRD_AMT_AVG_R3M,TUROV_RATE_AVG_R3M,CHG_R3M,TRD_AMT_R6M,TRD_VOL_R6M,HPRC_R6M,LPRC_R6M,CHG_RAT_R6M,TUROV_RATE_R6M,
            AMP_RAT_R6M,TRD_AMT_AVG_R6M,TUROV_RATE_AVG_R6M,CHG_R6M,TRD_AMT_RY,TRD_VOL_RY,HPRC_RY,LPRC_RY,CHG_RAT_RY,TUROV_RATE_RY,AMP_RAT_RY,TRD_AMT_AVG_RY,
            TUROV_RATE_AVG_RY,CHG_RY,TRD_AMT_TY,TRD_VOL_TY,HPRC_TY,LPRC_TY,CHG_RAT_TY,TUROV_RATE_TY,AMP_RAT_TY,TRD_AMT_AVG_TY,TUROV_RATE_AVG_TY,CHG_TY,
			a.SECU_ID || cast(TO_CHAR(a.TRD_DT,'YYYY-MM-DD')  AS varchar2(200)) AS ONE_ID
FROM STK_HK_MKT_QUOT_INDX a where a.TRD_DT >= #{curt_running_dt} 
AND a.TRD_DT <= #{next_running_dt}
)
SELECT 
            ID,SECU_ID,TRD_DT,TRD_STS,CPRC,DAY_CHG,DAY_CHG_RAT,TUROV_RATE,DAY_AMP,FMKT_VAL,TMKT_VAL,PE,PE_DYM,PETTM,PB,PB_NEW,PC,PC_DYM,PCTTM,PS,PS_DYM,PSTTM,TRD_AMT_RW,
            TRD_VOL_RW,HPRC_RW,LPRC_RW,CHG_RAT_RW,TUROV_RATE_RW,AMP_RAT_RW,TRD_AMT_AVG_RW,TUROV_RATE_AVG_RW,CHG_RW,TRD_AMT_TW,TRD_VOL_TW,HPRC_TW,LPRC_TW,CHG_RAT_TW,TUROV_RATE_TW,
            AMP_RAT_TW,TRD_AMT_AVG_TW,TUROV_RATE_AVG_TW,CHG_TW,TRD_AMT_RM,TRD_VOL_RM,HPRC_RM,LPRC_RM,CHG_RAT_RM,TUROV_RATE_RM,AMP_RAT_RM,TRD_AMT_AVG_RM,TUROV_RATE_AVG_RM,CHG_RM,
            TRD_AMT_TM,TRD_VOL_TM,HPRC_TM,LPRC_TM,CHG_RAT_TM,TUROV_RATE_TM,AMP_RAT_TM,TRD_AMT_AVG_TM,TUROV_RATE_AVG_TM,CHG_TM,TRD_AMT_R3M,TRD_VOL_R3M,HPRC_R3M,LPRC_R3M,
            CHG_RAT_R3M,TUROV_RATE_R3M,AMP_RAT_R3M,TRD_AMT_AVG_R3M,TUROV_RATE_AVG_R3M,CHG_R3M,TRD_AMT_R6M,TRD_VOL_R6M,HPRC_R6M,LPRC_R6M,CHG_RAT_R6M,TUROV_RATE_R6M,
            AMP_RAT_R6M,TRD_AMT_AVG_R6M,TUROV_RATE_AVG_R6M,CHG_R6M,TRD_AMT_RY,TRD_VOL_RY,HPRC_RY,LPRC_RY,CHG_RAT_RY,TUROV_RATE_RY,AMP_RAT_RY,TRD_AMT_AVG_RY,
            TUROV_RATE_AVG_RY,CHG_RY,TRD_AMT_TY,TRD_VOL_TY,HPRC_TY,LPRC_TY,CHG_RAT_TY,TUROV_RATE_TY,AMP_RAT_TY,TRD_AMT_AVG_TY,TUROV_RATE_AVG_TY,CHG_TY,
           CASE WHEN ONE_ID IN (SELECT ONE_ID FROM T1 GROUP BY ONE_ID) THEN 0 ELSE 1 END AS IS_DELETE_NEW
FROM T2;帮忙优化下这段OracleSQL代码,其中 T1 和 T2 的数据量相当且都是百万级

下面从可读性、性能、可维护性 三个角度给你一个针对百万级数据量的 Oracle SQL 优化版本,并解释每一步为什么这么改。


一、当前 SQL 存在的主要性能瓶颈

结合你给出的 SQL,主要问题有:

  1. T1 / T2 都做全表扫描 TRD_DT范围过滤,但如果没有合适索引,会扫百万行
  2. 字符串拼接 || TO_CHAR(...)作为 JOIN/IN 条件 无法使用普通 BTree 索引 会导致大量 CPU + 内存消耗
  3. CASE WHEN ONE_ID IN (SELECT ...) IN (子查询)在百万级数据下非常容易变成 FILTER 操作
  4. 字段过多,SELECT * 风格 即使只需要少量字段,也会读整行数据
  5. 重复计算 ONE_ID在两个 CTE 中都算一次

二、优化总体思路(强烈推荐)

核心优化点:

  • EXISTS 替代 IN
  • 去掉 ONE_ID的字符串拼接
  • 让 T1 / T2 直接通过 (SECU_ID, TRD_DT)关联
  • 给大表建立 组合索引
  • 避免 CTE 物化(Oracle 12c+)

三、等价改写后的高性能版本(推荐)

✅ 优化后 SQL(逻辑完全等价)

复制代码
SELECT
       t2.ID,
       t2.SECU_ID,
       t2.TRD_DT,
       t2.TRD_STS,
       t2.CPRC,
       t2.DAY_CHG,
       t2.DAY_CHG_RAT,
       t2.TUROV_RATE,
       t2.DAY_AMP,
       t2.FMKT_VAL,
       t2.TMKT_VAL,
       t2.PE,
       t2.PE_DYM,
       t2.PETTM,
       t2.PB,
       t2.PB_NEW,
       t2.PC,
       t2.PC_DYM,
       t2.PCTTM,
       t2.PS,
       t2.PS_DYM,
       t2.PSTTM,
       -- 后续字段省略,实际保留你需要的
       CASE
           WHEN EXISTS (
               SELECT 1
               FROM STK_HK_EXCH_QUOT a
               JOIN BASE_SECURITY_INFO b
                 ON a.SECU_ID = b.SECURITY_ID
              WHERE a.SECU_ID = t2.SECU_ID
                AND a.TRD_DT  = t2.TRD_DT
                AND a.ROR_FCTR IS NOT NULL
                AND a.IS_DELETE = 0
                AND b.IS_DELETE = 0
           )
           THEN 0
           ELSE 1
       END AS IS_DELETE_NEW
FROM STK_HK_MKT_QUOT_INDX t2
WHERE t2.TRD_DT BETWEEN #{curt_running_dt}
                     AND #{next_running_dt};

优点

  • 不再需要 T1
  • 不再拼接字符串
  • EXISTS在百万级数据下性能远优于 IN
  • Oracle 可以直接用 (SECU_ID, TRD_DT)索引

四、必须建立的索引(关键)

✅ 1️⃣ STK_HK_MKT_QUOT_INDX(主表)

复制代码
CREATE INDEX IDX_MKT_QUOT_DT_SEC
ON STK_HK_MKT_QUOT_INDX (TRD_DT, SECU_ID);

✅ 2️⃣ STK_HK_EXCH_QUOT(被 EXISTS 引用的表)

复制代码
CREATE INDEX IDX_EXCH_QUOT_DT_SEC
ON STK_HK_EXCH_QUOT (TRD_DT, SECU_ID, ROR_FCTR)
WHERE IS_DELETE = 0;

⚠️ 如果 Oracle 12c+,建议用 Partial Index(上面已体现)

✅ 3️⃣ BASE_SECURITY_INFO

复制代码
CREATE INDEX IDX_BASE_SEC_ID
ON BASE_SECURITY_INFO (SECURITY_ID)
WHERE IS_DELETE = 0;

五、如果你一定要保留 CTE(不推荐)

若业务必须使用 T1 / T2,请至少这样改写:

复制代码
WITH T1 AS (
    SELECT DISTINCT SECU_ID, TRD_DT
    FROM STK_HK_EXCH_QUOT a
    JOIN BASE_SECURITY_INFO b
      ON a.SECU_ID = b.SECURITY_ID
     AND a.ROR_FCTR IS NOT NULL
     AND a.IS_DELETE = 0
     AND b.IS_DELETE = 0
    WHERE a.TRD_DT BETWEEN #{curt_running_dt}
                       AND #{next_running_dt}
),
T2 AS (
    SELECT *
    FROM STK_HK_MKT_QUOT_INDX
    WHERE TRD_DT BETWEEN #{curt_running_dt}
                     AND #{next_running_dt}
)
SELECT t2.*,
       CASE WHEN (t2.SECU_ID, t2.TRD_DT) IN (SELECT SECU_ID, TRD_DT FROM T1)
            THEN 0 ELSE 1 END AS IS_DELETE_NEW
FROM T2 t2;

但仍不如 EXISTS 方案


六、性能对比预期(经验值)

写法 执行时间(百万级)
原始 SQL 30s ~ 数分钟
EXISTS 改写 2 ~ 5s
加索引 + EXISTS < 1s

七、总结一句话

不要再用字符串拼接做主键判断,也不要用 IN (子查询),百万级数据下一定用 EXISTS + 组合索引

相关推荐
iuvtsrt1 小时前
SQL如何优化子查询的性能_改写为JOIN关联查询与消除嵌套
jvm·数据库·python
2403_883261091 小时前
C#怎么使用并发集合 C#ConcurrentDictionary和ConcurrentQueue线程安全集合怎么用【进阶】
jvm·数据库·python
m0_470857641 小时前
如何加固SQL系统架构_采用读写分离降低攻击影响
jvm·数据库·python
2401_884454151 小时前
Golang如何写博客系统后端_Golang博客系统教程【技巧】
jvm·数据库·python
2301_779622411 小时前
JavaScript中利用Range对象实现复杂的文本选择操作
jvm·数据库·python
m0_741481781 小时前
Go语言怎么做接口签名_Go语言API签名验证教程【精通】
jvm·数据库·python
2401_884454151 小时前
CSS实现浮动图标与文本居中对齐_配合浮动与flex
jvm·数据库·python
woxihuan1234561 小时前
SQL统计分组内的所有数据唯一值_使用DISTINCT汇总
jvm·数据库·python
m0_631529821 小时前
CSS如何利用-disabled伪类禁用交互元素_通过灰度效果提升界面逻辑清晰度
jvm·数据库·python