【问题标题】:Oracle Merge ignore duplicatesOracle Merge 忽略重复项
【发布时间】:2017-11-23 23:48:04
【问题描述】:

我有一个可通过 oracle 数据库链接访问的表,我正试图将其拉入本地数据库表,原因是。

MERGE INTO MEMBERSHIPS LOCAL
USING (
  SELECT DISTINCT
    REMOTE.GROUP_NAME        "GROUP_NAME",
    REMOTE.USER_ACCOUNT      "USERNAME",
    REMOTE.SOME_OTHER_COLUMN "COL3"
  FROM MEMBERSHIPS@link REMOTE
) REMOTE
ON (
  REMOTE.GROUP_NAME = LOCAL.GROUP_NAME AND
  REMOTE.USERNAME   = LOCAL.USERNAME
)
WHEN MATCHED THEN
  UPDATE SET
    LOCAL.COL3       = REMOTE.COL3
    LOCAL.UPDATED_AT = sysdate
WHEN NOT MATCHED THEN
  INSERT (ID, GROUP_NAME, USERNAME, COl3, CREATED_AT, UPDATED_AT)
  VALUES (MEMBERSHIPS_SEQ.NEXTVAL, REMOTE.GROUP_NAME, REMOTE.USERNAME, REMOTE.COl3, sysdate, sysdate)

最不幸的是,原始数据库的所有者并没有因为担心数据完整性而失眠,所以在 300 万行中,有 71 个重复项,这炸毁了我的组名、用户名的唯一索引。如果我删除唯一性约束,合并将处理,但是这些行将在随后使用ORA-30926: unable to get a stable set of rows in the source tables 执行查询时爆炸。

这是每天都会运行的事情,所以我需要找到一种方法来忽略重复项

编辑:

我原以为 distinct 会为我解决这个问题,但事实并非如此。我仍然得到重复:

SELECT DISTINCT
REMOTE.GROUP_NAME,
REMOTE.USER_ACCOUNT
COUNT(*)
FROM MEMBERSHIPS@link REMOTE
GROUP BY
REMOTE.GROUP_NAME,
REMOTE.USER_ACCOUNT
HAVING COUNT(*) > 1;

显示 71 个仍然重复的 GROUP_NAME/USER_ACCOUNT 组合

【问题讨论】:

  • 困惑:为什么 DISTINCT 不处理重复项?您是否在本地有重复但 ID 和日期中有其他值?
  • 本地表是空的,并且在遇到第一个重复项时违反了唯一性,不同的似乎没有像我希望的那样摆脱重复项。本地 ID 和时间戳是唯一不直接来自源表的字段。
  • 远程表上也有ID吗?也许选择 MIN ID 并按其他列分组以仅获得一行...
  • 很遗憾,没有。
  • 在您添加的查询中显示DISTINCT 不会删除重复项,您首先进行分组,因此根据定义,DISTINCT 什么都不做,因为每个分组都有一个唯一的查询。更好的测试是SELECT COUNT(*), COUNT(DISTINCT group_name || '.' || user_account) FROM memberships@link

标签: oracle merge


【解决方案1】:

在类似情况下,您始终可以尝试对行进行排名以避免重复,而不是 DISTINCT。 在这种情况下,它会是这样的:

MERGE INTO MEMBERSHIPS LOCAL
USING (
  SELECT rank() over(partition by REMOTE.GROUP_NAME
        ,REMOTE.USER_ACCOUNT
        order by NVL(REMOTE.UPDATED_AT,REMOTE.CREATED_AT) DESC NULLS LAST) r,
    REMOTE.GROUP_NAME        "GROUP_NAME",
    REMOTE.USER_ACCOUNT      "USERNAME",
    REMOTE.SOME_OTHER_COLUMN "COL3"`
  FROM MEMBERSHIPS@link REMOTE ) REMOTE
ON (
  REMOTE.GROUP_NAME = LOCAL.GROUP_NAME 
  AND REMOTE.USERNAME   = LOCAL.USERNAME
  AND REMOTE.r = 1
)
WHEN MATCHED THEN
  UPDATE SET
    LOCAL.COL3       = REMOTE.COL3
    LOCAL.UPDATED_AT = sysdate
WHEN NOT MATCHED THEN
  INSERT (ID, GROUP_NAME, USERNAME, COl3, CREATED_AT, UPDATED_AT)
  VALUES (MEMBERSHIPS_SEQ.NEXTVAL, REMOTE.GROUP_NAME, REMOTE.USERNAME, REMOTE.COl3, sysdate, sysdate);

【讨论】:

    【解决方案2】:

    我尝试使用两个本地表重现您描述的问题。使用您提供的合并,我没有因源表中的重复而导致的问题(在 Oracle 11.2.0.4 中)。如果我从 USING 子句中的子查询中删除 DISTINCT 关键字,那么我会得到您所描述的问题 - 第一次尝试违反约束,或者如果我删除唯一约束,则在第二次尝试中出现 ORA-30926。

    对此我能想到的两种解释是(a)您在 Oracle 中遇到了一些错误,可能涉及远程子查询中的 DISTINCT,或者(b)您实际运行的合并语句不包括 @ 987654323@。 (我还考虑了 NULL 值可能会导致 DISTINCT 操作产生意外结果的可能性,但我无法想出会发生这种情况的方法。)

    编辑: 另一个深思熟虑的解释 - 如果两个数据库使用不同的字符集,我想知道原始表中不同的值是否有可能在传输过程中转换为相同的值?

    【讨论】:

    • 我更新了我的示例。 distinct 的问题是我正在拉入其他专栏。在源表中,这些其他列中的大多数为 null,但 distinct 仍将 null 视为差异。
    猜你喜欢
    • 2012-09-01
    • 2023-01-10
    • 1970-01-01
    • 1970-01-01
    • 2014-04-25
    • 2013-06-23
    • 2017-06-21
    • 1970-01-01
    • 2013-08-13
    相关资源
    最近更新 更多