【问题标题】:Oracle: Fastest way to compare tables containing CLOB and get diffOracle:比较包含 CLOB 的表并获取差异的最快方法
【发布时间】:2017-09-22 12:15:07
【问题描述】:

假设我有两个表,其列 Col1、Col2 和 Col3 分别是 VARCHAR2、CLOB 和 NUMBER 类型。

如何获得这些表的差异? (即表B中存在但表A中不存在的记录列表)

Table A:
╔═══════╦═════════════════╦══════╗
║ Col1  ║      Col2       ║ Col3 ║
╠═══════╬═════════════════╬══════╣
║ P1111 ║ some_long_text1 ║ 1234 ║
║ P1111 ║ some_long_text1 ║ 1233 ║
║ P1111 ║ some_long_text2 ║ 1233 ║
╚═══════╩═════════════════╩══════╝

Table B:
╔═══════╦═════════════════╦══════╗
║ Col1  ║      Col2       ║ Col3 ║
╠═══════╬═════════════════╬══════╣
║ P1111 ║ some_long_text1 ║ 1234 ║
║ P1111 ║ some_long_text1 ║ 1235 ║
║ P1112 ║ some_long_text2 ║ 1233 ║
╚═══════╩═════════════════╩══════╝

Expected results:
╔═══════╦═════════════════╦══════╗
║ Col1  ║      Col2       ║ Col3 ║
╠═══════╬═════════════════╬══════╣
║ P1111 ║ some_long_text1 ║ 1235 ║
║ P1112 ║ some_long_text2 ║ 1233 ║
╚═══════╩═════════════════╩══════╝

【问题讨论】:

  • 您可以查看MINUS 运算符 - LINK。不确定 CLOB 变量会如何表现。
  • MINUS 不适用于 LOB 类型。

标签: oracle plsql oracle12c


【解决方案1】:

要比较LOB 类型,您可以使用DBMS_LOB.COMPARE 函数。

SELECT table_b.* 
  FROM table_b
  LEFT JOIN table_a
    ON table_b.col1 = table_a.col1
   AND DBMS_LOB.COMPARE(table_b.col2, table_a.col2) = 0
   AND table_b.col3 = table_a.col3
 WHERE table_a.col1 IS NULL;

【讨论】:

    【解决方案2】:

    您可以使用如下所示的 dbms_lob.substr() 函数来使用减号运算符

    select co11,dbms_lob.substr(col2),col3 from a
    minus
    select co11,dbms_lob.substr(col2),col3 from b;
    

    【讨论】:

    【解决方案3】:

    特别是在 Oracle 中,我认为你可以做到这一点

    SELECT * FROM TableB WHERE (Col1, Col2, Col3) NOT IN (SELECT Col1, Col2, Col3 from TABLEA)
    

    您想加入的其他星展银行:

    SELECT * FROM TableB left outer join TableA 
    on (a.Col1=b.Col1 and a.Col2=b.Col2 and a.Col3=b.Col3)
    WHERE a.col1 is null
    

    如果要将其包含在比较中,您可能需要对 CLOB 列进行校验和/哈希。

    【讨论】:

    • CLOB 列必须包含在比较中。您将如何使用校验和/哈希比较 CLOB 列?
    • 您可以在代码中或通过触发器计算插入/更新时的哈希值。也许是 DBMS_SQLHASH.GETHASH 函数。根据大小,直接比较可能会很慢。为此使用DBMS_LOB.COMPARE(a.col1,b.col1)=0
    【解决方案4】:

    根据您的预期结果,Left Join 似乎可以正常工作。

    大意是这样的:

    Select B.Col1
          ,B.Col2
          ,B,Col3
      FROM TableB B
      LEFT OUTER JOIN TableA A
        ON B.Col1 = A.Col1
     WHERE B.Col2 = A.Col2
       AND B.Col3 = A.Col3
       AND A.Col1 IS NULL
    

    由于WHERE 条件过滤了数据集,因此匹配所有内容是有意义的,包括与表A 匹配的NULL 值。只需在WHERE 中包含A.Col1 IS NULL,那么您'确保只看到 TableB 中没有对应 TableA 值的值。

    Join 很昂贵,Left Join 更是如此。仅加入一个键值应该有助于提高效率(尤其是因为无论如何您都想匹配所有记录)。通过在 WHERE 子句中放置其他连接谓词,您可以过滤这些谓词。

    现在 - 只要匹配您的 CLOB - 散列这些值可能有也可能没有任何好处。这取决于数据的大小和使用的散列算法。 Oracle 优化器可能会自动选择对列进行散列以进行比较,或者您可以使用函数强制它。

    我认为应该允许引擎做出这个选择——我相信会有其他人不同意我的观点,他们都有正当的理由。我的论点是:当优化器可以自行做出决定时,如果没有必要,为什么还要强制执行额外的步骤?

    如果这将是一个经常被调用的频繁使用的查询(例如,作为存储过程),那么创建一个存储CLOB 的预先计算的哈希值的列可能会很方便比较。这种更改几乎肯定会消除在执行期间要求对每个 CLOB 进行散列处理的开销,这可能是一个 CPU 密集型操作。我个人不建议索引散列列,因为我希望每个 CLOB 条目都可能是唯一值。如果是这种情况,那么表的 PK 应该足以根据行唯一性进行匹配。

    【讨论】:

      【解决方案5】:

      1) 您必须为 lob 对象创建 UDT。

      create or replace type lob_wrapper is object 
      ( x clob,
        hash varchar2(100),
       constructor function lob_wrapper(p_x clob)  return self as result,
       MAP MEMBER FUNCTION get_hash  RETURN varchar2
      
       )
       ;
      
      create  or replace type body lob_wrapper  
      as 
       constructor function lob_wrapper(p_x clob)
          return self as result
        as
         temp_ varchar(1000) :=  p_x;
        begin 
             self.x := p_x;
      -- add here better implementation of hashing clob. 
             select ora_hash(temp_) into self.hash from dual;   
         return;
        end; 
       MAP MEMBER FUNCTION get_hash  RETURN varchar2 is 
         v_hash varchar2(4000);
       begin    
        return hash;
       end;
      end;
      

      对象构造计算 clob 的哈希值。在示例中,我使用的是ora_hash,但您应该选择更好的解决方案 (dbms_crypto.hash)。

      当 db 尝试比较两个对象时,调用对象中的映射函数 get_hash

      select col1,lob_wrapper(col2) col2 ,col3 from test_clob_b
      minus 
      select col1,lob_wrapper(col2) col2 ,col3 from test_clob_a
      

      要从对象中获取原始值,请添加另一个选择。

      select col1,t.col2.x oringal_value,col3, t.col2.hash hash_value from (
      select col1,lob_wrapper(col2) col2 ,col3 from test_clob_b
      minus 
      select col1,lob_wrapper(col2) col2 ,col3 from test_clob_a
      ) t;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-11-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多