【问题标题】:Delete millions of records based on other tables根据其他表删除数百万条记录
【发布时间】:2016-08-17 06:11:30
【问题描述】:

我有一个从外部源加载数据的主表。主表-PROD_MAIN的表结构是

         PROD_ROW_ID | PROD_VALUE | PROD_TYPE | PROD_DATE

数据从主表加载到另外两个表中。这两个表是:

         PROD, PROD_ENT 
  • PROD_ROW_ID,PROD_TYPE 被加载到 PROD 表中并
  • PROD_VALUE,PROD_DATE 已加载到 PROD_ENT

PROD 和 PROD_ENT 表使用以下条件连接。

        PROD.PROD_ROW_ID = PROD_ENT.PROD_PAR_ID

每天都会将数据从 PROD_MAIN 插入到这两个表 PROD 和 PROD_ENT 表中。由于一些数据库问题,许多记录错过了加载到 PROD 和 PROD_ENT 表中。

所以,我需要检查 3 个月的缺失记录,即从 11 月 19 日到 2 月 19 日;没有加载到 PROD 和 PROD_ENT 表中。 所有这些表都有大约 2 亿条记录。

所以,我编写了以下查询来获取结果。但是它给了我零记录。你能帮忙吗?

SELECT /*+ PARALLEL (PROD_MAIN,15) */ MH.*
FROM   PROD PMN, 
       PROD_ENT PCH, 
       PROD_MAIN MH
WHERE PMN.PROD_ROW_ID = PCH.PROD_PAR_ID
AND   MH.PROD_ROW_ID(+) = PMN.PROD_ROW_ID
AND   MH.PROD_VALUE(+) = PCH.PROD_VALUE 
AND   MH.PROD_TYPE(+) = PMN.PROD_TYPE
AND   MH.PROD_DATE (+) = PCH.PROD_DATE 
AND   MH.PROD_ROW_ID IS NULL
AND   MH.PROD_VALUE IS NULL
AND   MH.PROD_TYPE IS NULL
AND   MH.PROD_DATE  IS NULL
AND   MH.PROD_DATE  BETWEEN '19-NOV-2015' AND '19-FEB-2016'

***** 编辑代码 *****

万一, 1. 如果我需要获取在 PROD_MAIN 表中存在且在 PROD 中不存在的记录。 2. 如果我需要获取 PROD_MAIN 中存在的记录 表和 PROD_ENT 表中的 NOT PRESENT 分开,我需要编写如下的联合查询还是有其他简单的方法可以这样做?

    SELECT MH.*
FROM PROD_MAIN AS MH
LEFT JOIN PROD AS PMN
    ON (PMN.PROD_ROW_ID = MH.PROD_ROW_ID
    AND PMN.PROD_TYPE = MH.PROD_TYPE)
LEFT JOIN PROD_ENT AS PCH
    ON (PCH.PROD_PAR_ID = PMN.PROD_ROW_ID
    AND PCH.PROD_DATE = MH.PROD_DATE
    AND PCH.PROD_VALUE = MH.PROD_VALUE)
WHERE PMN.PROD_ROW_ID IS NULL
AND MH.PROD_DATE  BETWEEN '19-NOV-2015' AND '19-FEB-2016'

UNION

    SELECT MH.*
FROM PROD_MAIN AS MH
LEFT JOIN PROD AS PMN
    ON (PMN.PROD_ROW_ID = MH.PROD_ROW_ID
    AND PMN.PROD_TYPE = MH.PROD_TYPE)
LEFT JOIN PROD_ENT AS PCH
    ON (PCH.PROD_PAR_ID = PMN.PROD_ROW_ID
    AND PCH.PROD_DATE = MH.PROD_DATE
    AND PCH.PROD_VALUE = MH.PROD_VALUE)
WHERE PCH.PROD_PAR_ID IS NULL
AND MH.PROD_DATE  BETWEEN '19-NOV-2015' AND '19-FEB-2016'

【问题讨论】:

  • 请使用显式连接语法,这是 ANSI-92 标准的一部分。老实说,我什至不明白您的联接是如何工作的,因为您使用了古怪的(+) 表示法。但是我怀疑join可能有问题。
  • 今日提示:切换到现代的、显式的 JOIN/OUTER JOIN 语法。更容易编写(没有错误)并且更容易阅读。也更容易转换为外连接。
  • 在你的 where: MH.PROD_DATE 被要求为空并且在两个日期之间。不能两者都是 => 没有结果。
  • AND (MH.PROD_DATE IS NULL OR MH.PROD_DATE BETWEEN DATE '2015-11-19' AND DATE '2016-02-19')
  • @Cool_Oracle:取决于您是否希望包含空值(参见 Maheswarans 评论)或排除。如果您想排除它,只需删除对 null 的检查。

标签: sql oracle


【解决方案1】:

看起来你的逻辑有缺陷。您说数据是从 PROD_MAIN 加载到其他表中的。因此,您需要查找该表中但不在 PROD 和 PROD_ENT 中的行。

但是,您的反连接正在过滤 PROD_MAIN 列。这两个过滤器永远不可能同时为真:

MH.PROD_DATE  IS NULL
AND   MH.PROD_DATE  BETWEEN '19-NOV-2015' AND '19-FEB-2016'

同样,如果您在 PROD 和 PROD_ENT 中查找不存在的行,那么此连接条件永远不会为真:

PMN.PROD_ROW_ID = PCH.PROD_PAR_ID

几乎可以肯定,您需要检查 PROD 和 PROD_ENT 中的连接列是否为空。我使用的是 ANSI 92 语法,因为它使外连接更容易理解。

SELECT /*+ PARALLEL (PROD_MAIN,15) */ MH.*
FROM   PROD_MAIN MH
    left outer join PROD PMN
        on MH.PROD_ROW_ID = PMN.PROD_ROW_ID   
        and MH.PROD_TYPE = PMN.PROD_TYPE
    left outer join PROD_ENT PCH
        on MH.PROD_ROW_ID = PCH.PROD_PAR_ID 
        and MH.PROD_VALUE = PCH.PROD_VALUE 
        and    MH.PROD_DATE = PCH.PROD_DATE 
where MH.PROD_DATE  BETWEEN date '2015-11-19' AND date '2016-02-19'
AND   PCH.PROD_PAR_ID IS NULL
AND   PMN.PROD_ROW_ID  IS NULL

不确定连接子句中是否需要所有这些列:我只是复制了您的连接逻辑。


“我又遇到了一个小场景……我可以写一个联合查询,就像在主要问题中更新的那样吗?”

您编写查询的方式会产生结果,但您将无法区分这三个类别(在 PROD 但不在 PROD_ENT 中,在 PROD_ENT 但不在 PROD 中,两者都没有)。这将是一条有用的信息,您需要稍微修改查询:

SELECT 'PROD' as tgt_table, MH.*
FROM PROD_MAIN AS MH
LEFT JOIN PROD AS PMN
    ON (PMN.PROD_ROW_ID = MH.PROD_ROW_ID
    AND PMN.PROD_TYPE = MH.PROD_TYPE)
WHERE PMN.PROD_ROW_ID IS NULL
AND MH.PROD_DATE  BETWEEN '19-NOV-2015' AND '19-FEB-2016'

UNION ALL

SELECT 'PROD_ENT' as tgt_table, MH.*
FROM PROD_MAIN AS MH
LEFT JOIN PROD AS PMN
    ON (PMN.PROD_ROW_ID = MH.PROD_ROW_ID
    AND PMN.PROD_TYPE = MH.PROD_TYPE)
LEFT JOIN PROD_ENT AS PCH
    ON (PCH.PROD_PAR_ID = PMN.PROD_ROW_ID
    AND PCH.PROD_DATE = MH.PROD_DATE
    AND PCH.PROD_VALUE = MH.PROD_VALUE)
WHERE PCH.PROD_PAR_ID IS NULL
AND MH.PROD_DATE  BETWEEN '19-NOV-2015' AND '19-FEB-2016'

使用 UNION ALL 而不是 UNION 以避免不必要的排序。

您在 PROD_ENT 上的外部联接版本与我的不同。您的版本在 PCH.PROD_PAR_ID = PMN.PROD_ROW_ID 上加入,因此它会对 PROD_ENT 记录给出误报,这些记录实际上存在但在 PROD 中缺少所有者记录。如果这种情况永远不会发生,那也没关系,但由于您似乎正在调查加载过程中的问题,您可能应该尽可能精确。

【讨论】:

  • 非常感谢!您的查询完全有效!对了,我又有一个小场景。 1. 如果我需要获取在 PROD_MAIN 表中存在且在 PROD 中不存在的记录。 2. 如果我需要分别获取 PROD_MAIN 表中 PRESENT 和 PROD_ENT 表中 NOT PRESENT 的记录,我可以编写一个联合查询,如主要问题中更新的那个吗?
  • 是的,这确实是一堆数据。如果我只需要验证 PROD_ENT 中不存在的记录,那么这个查询就足够了吗?选择 'PROD_ENT' 作为 tgt_table, MH.* 从 PROD_MAIN 作为 MH 左加入 PROD 作为 PMN ON (PMN.PROD_ROW_ID = MH.PROD_ROW_ID 和 PMN.PROD_TYPE = MH.PROD_TYPE) LEFT JOIN PROD_ENT 作为 PCH ON (PCH.PROD_PAR_ID = PMN. PROD_ROW_ID AND PCH.PROD_DATE = MH.PROD_DATE AND PCH.PROD_VALUE = MH.PROD_VALUE) 其中 PCH.PROD_PAR_ID 为 NULL 且 MH.PROD_DATE 在 '19-NOV-2015' 和 '19-FEB-2016' 之间
【解决方案2】:

我需要获取那些在 PROD_MAIN 表中存在但在 PROD 和 PROD_ENT 表中不存在的记录

试试:

SELECT PROD_ROW_ID -- DELETE -- To realy delete remove 'SELECT PROD_ROW_ID -- '
FROM PROD_MAIN 
WHERE PROD_DATE BETWEEN '19-NOV-2015' AND '19-FEB-2016' AND 
     (PROD_ROW_ID NOT IN (SELECT PROD_PAR_ID FROM PROD_ENT) 
      AND -- or OR if the record should be deleted if not present in one of the two tables
      PROD_ROW_ID NOT IN (SELECT PROD_ROW_ID FROM PROD))

请注意,我猜 ..._ID 列是您在所有三个表中的主键

【讨论】:

    【解决方案3】:

    你必须使用左连接-

    SELECT MH.*
    FROM PROD_MAIN AS MH
    LEFT JOIN PROD AS PMN
        ON (PMN.PROD_ROW_ID = MH.PROD_ROW_ID
        AND PMN.PROD_TYPE = MH.PROD_TYPE)
    LEFT JOIN PROD_ENT AS PCH
        ON (PCH.PROD_PAR_ID = PMN.PROD_ROW_ID
        AND PCH.PROD_DATE = MH.PROD_DATE
        AND PCH.PROD_VALUE = MH.PROD_VALUE)
    WHERE PMN.PROD_ROW_ID IS NULL OR PCH.PROD_PAR_ID IS NULL
    AND MH.PROD_DATE  BETWEEN '19-NOV-2015' AND '19-FEB-2016'
    

    请注意,以下行是分隔 PROD_MAIN 中但不在 PROD 或 PROD_ENT 中的条目的关键

    WHERE PMN.PROD_ROW_ID IS NULL OR PCH.PROD_PAR_ID IS NULL 
    

    通过使用左连接,您首先考虑左表中的所有行,即 PROD_MAIN,然后您还通过比较 PROD_ROW_ID 获取与右表匹配的行(忽略 PROD_TYPE 为简单起见) .

    LEFT JOIN PROD AS PMN ON (PMN.PROD_ROW_ID = MH.PROD_ROW_ID)
    

    如果在 PROD_MAIN 中有一个 PROD_ROW_ID(再次忽略 PROD_TYPE),但该行不在 PROD 中,则 PROD 的所有列都将包含 null。因此,在WHERE 子句中,您只需检查右表的任何列是否为空

    WHERE PMN.PROD_ROW_ID IS NULL
    

    【讨论】:

    • 非常感谢!您的查询完全有效!对了,我又有一个小场景。 1. 如果我需要获取在 PROD_MAIN 表中存在且在 PROD 中不存在的记录。 2. 如果我需要分别获取 PROD_MAIN 表中 PRESENT 和 PROD_ENT 表中 NOT PRESENT 的记录,我可以编写一个联合查询,如主要问题中更新的那个吗?
    • 不客气。我不确定您到底在寻找什么。您想将结果(不在 PROD 中的记录和不在 PROD_ENT 中的记录)合并到一个 SELECT 中,还是希望它们在两个单独的结果集中,即两个单独的 SQL 查询?如果查询对您有用,您可以选择它作为接受的答案。谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-11
    • 2023-01-07
    • 2012-10-12
    • 2021-11-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多