根据其他表删除数百万条记录

时间:2016-08-17 06:11:30

标签: sql oracle

我有一个主表,其中数据从外部源加载。主表的表结构 - 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表中的PRESENT记录而不是PROD中的PRESENT记录。  2.如果我需要获取PROD_MAIN中的PRESENT记录     表和NOT PRESENT分别在PROD_ENT表中,我是否需要编写如下所示的联合查询,还是有其他简单方法可以这样做?

    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'

3 个答案:

答案 0 :(得分: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

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

  

“我又有一个小场景......我可以写一个像主要问题中更新的联合查询吗?”

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

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 :(得分:0)

  

我需要获取那些在PROD_MAIN表中是PRESENT并且在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列是所有三个表中的主键

答案 2 :(得分:0)

你必须使用左连接 -

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来获取与右表相匹配的行,即PROD。简单)。

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

如果PROD_MAIN中存在PROD_ROW_ID(再次忽略PROD_TYPE)但该PROD中没有PROD,那么PROD的所有列都将包含null。因此,在WHERE子句中,您只需检查右表中的任何列为空

WHERE PMN.PROD_ROW_ID IS NULL