从多个来源有效地更新表格

时间:2016-01-21 15:28:44

标签: sql oracle oracle12c

我正在努力改进Oracle中现有ETL层的一部分。

  1. 将文件加载到临时表中。
  2. 执行许多MERGE语句来解析代理键。
  3. 应用了一些其他业务逻辑(需要这些代理键)。
  4. 结果是MERGEd 在表中(包含代理键和业务逻辑) 结果)
  5. 这是我想要改进的第2步,似乎不太理想这样做几步。

    MERGE INTO temp t
    USING dimension_1 d1 ON (d1.natural_key = t.d1_natural_key)
    WHEN MATCHED THEN UPDATE t.d1_id = d1.id
    
    MERGE INTO temp t
    USING dimension_2 d2 ON (d2.natural_key = t.d2_natural_key)
    WHEN MATCHED THEN UPDATE t.d2_id = d2.id
    
    MERGE INTO temp t
    USING dimension_3 d3 ON (d3.natural_key = t.d3_natural_key)
    WHEN MATCHED THEN UPDATE t.d3_id = d3.id
    

    如果我在SQL Server中写这个,我会做类似以下的事情:

    UPDATE
        t
    SET
        d1_id = COALESCE(d1.id, -1),
        d2_id = COALESCE(d2.id, -1),
        d3_id = COALESCE(d3.id, -1)
    FROM
        temp  t
    LEFT JOIN
        dimension_1   d1
            ON d1.natural_key = t.d1_natural_key
    LEFT JOIN
        dimension_2   d2
            ON d2.natural_key = t.d2_natural_key
    LEFT JOIN
        dimension_3   d3
            ON d3.natural_key = t.d3_natural_key
    

    对于我的生活,我无法在Oracle中找到合理的选择。我能够解决的最好方法是使用UPDATE (当我周围的每个人都在尖叫我必须'使用MERGE)和相关的子查询;类似......

    UPDATE
        temp t
    SET
        d1_id = COALESCE((SELECT id FROM dimension_1 d1 WHERE d1.natural_key = t.d1_natural_key), -1),
        d2_id = COALESCE((SELECT id FROM dimension_2 d2 WHERE d2.natural_key = t.d2_natural_key), -1),
        d3_id = COALESCE((SELECT id FROM dimension_3 d3 WHERE d3.natural_key = t.d3_natural_key), -1)
    

    还有更好的选择吗?或者相关的子查询方法在Oracle中实际上是否具有效果?

2 个答案:

答案 0 :(得分:3)

我认为SQL Server更新的等价物是:

UPDATE
    temp t1
SET
    (d1_id, d2_id, d3_id) = (
SELECT
    COALESCE(d1.id, -1),
    COALESCE(d2.id, -1),
    COALESCE(d3.id, -1)
FROM
    temp  t2
LEFT JOIN
    dimension_1   d1
        ON d1.natural_key = t2.d1_natural_key
LEFT JOIN
    dimension_2   d2
        ON d2.natural_key = t2.d2_natural_key
LEFT JOIN
    dimension_3   d3
        ON d3.natural_key = t2.d3_natural_key
WHERE
    t2.id = t1.id 
)

它仍然是一个相关的更新;加入发生在子查询中,因为Oracle不允许您作为更新本身的一部分加入。通常,您不需要(或想要)在子查询中再次引用目标外部表,但是您需要在此处进行外部连接。

您还可以将左连接方法与合并相结合,将基本相同的子查询放入using子句中:

MERGE INTO temp t
USING (
  SELECT t.id,
    COALESCE(d1.id, -1) AS d1_id,
    COALESCE(d2.id, -1) AS d2_id,
    COALESCE(d3.id, -1) AS d3_id
  FROM
    temp  t
  LEFT JOIN
    dimension_1   d1
      ON d1.natural_key = t.d1_natural_key
  LEFT JOIN
    dimension_2   d2
      ON d2.natural_key = t.d2_natural_key
  LEFT JOIN
    dimension_3   d3
      ON d3.natural_key = t.d3_natural_key
) d
ON (d.id = t.id)
WHEN MATCHED THEN UPDATE SET
  t.d1_id = d.d1_id,
  t.d2_id = d.d2_id,
  t.d3_id = d.d3_id

在这种情况下,我没有看到使用merge over update的任何真正好处。

两者都会覆盖三个ID列中的任何现有值,但听起来你并不期望有任何值。

答案 1 :(得分:2)

我相信这可能比Alex的答案更有效 - 只需要访问temp表,而不是两个。在我对一百万行的快速测试中,性能大致相同,但计划更好,因为temp表没有第二次访问。可能值得尝试一下您的数据集。

UPDATE
 ( SELECT      d1.id s_d1_id,
               d2.id s_d2_id,
               d3.id s_d3_id,
               mt.d1_id,
               mt.d2_id,
               mt.d3_id
        FROM   temp mt
               LEFT JOIN dimension_1 d1 ON d1.natural_key = mt.d1_natural_key
               LEFT JOIN dimension_2 d2 ON d2.natural_key = mt.d2_natural_key
               LEFT JOIN dimension_3 d3 ON d3.natural_key = mt.d3_natural_key )
SET d1_id =    COALESCE (s_d1_id, -1), d2_id = COALESCE (s_d2_id, -1), d3_id = COALESCE (s_d3_id, -1);

需要注意的是,每个维度表中的UNIQUE列需要natural_key个约束。通过这些约束,Oracle知道在您正在更新的视图中temp是密钥保留的,这使得上述语法正常。

另一个警告:我曾经遇到SELECT视图中的行与表的顺序不同的情况。结果是性能 tanked ,因为更新必须多次重新访问每个块。 ORDER BY temp.rowid视图中的SELECT可以解决此问题。

相关问题