2016-01-21 26 views
3

我正在改进Oracle中现有ETL层的一部分。从多个来源有效更新表格

  1. 将文件加载到临时表中。
  2. 执行许多MERGE语句来解析代理键。
  3. 应用了其他一些业务逻辑(需要这些代理键)。
  4. 结果合并 在表中(同时与代理键和业务逻辑 结果)

这是第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中的明智的选择。我已经能够解决的最好的办法是使用更新(而我周围的每个人都在尖叫我必须使用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

你为什么拒绝Oracle的左连接方法?语法会略有不同,但这个想法会起作用。 –

+0

@AlexPoole - 因为我没有解释'SET(x,y,z)=(SELECT x,y,z FROM blah)'语法来使'UPDATE'工作,并且因为使'MERGE'工作需要表更新以加入自身。 – MatBailie

回答

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 

我没有看到使用合并了更新在这种情况下任何实际的好处虽然。

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

+0

谢谢你,更新一个集合的能力并不是我遇到的。由于临时表本身可能相当大(数十万至数百万),你会期望自加入('t2.id = t1.id')会产生很大的成本吗?在我试图让MERGE工作的时候,我确实得到了你所拥有的东西,但是这种自我加入似乎确实是不必要的成本。 – MatBailie

+0

无论如何,你正在触及每一行。因为它是一个临时表(我假设你的意思是一个实际的表,一个全局的临时表,而不是你创建的永久表然后放下),它全部存储在内存中,所以成本可能很高,但对于这两者来说。对于GTT,您的三次单独合并可能会更有效率;你需要试验一下,看看执行计划是什么样的。 –

+0

我至少现在有几个不同的选项,并确认我没有失去任何盲目显而易见的*(除了我的SET(x,y,z)=')*的盲点,谢谢。 – MatBailie

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); 

需要注意的是,你需要在每个维度表上natural_keyUNIQUE约束。有了这些限制,Oracle知道temp在您更新的视图中保留了密钥,这正是上述语法确定的原因。

另一个警告:我曾经遇到过一种情况,SELECT视图中的行与表格的顺序不同。结果是性能因为更新不得不几次重新访问每个块而损耗了SELECT视图中的ORDER BY temp.rowid将解决此问题。

+0

如果temp获取其维度中不存在的自然键,则插入将因FK而失败 - 而不是通过更新/合并将匹配ID设置为-1。我假设左边的连接是为了这个场景,但是当然它们可能只是空键,这没关系。 (我不认为你必须让FKs保存这个密钥,但是如果他们在那里并且编入索引,它会表现得更好)。 –

+0

我只是假定'LEFT JOIN'是'NULL'键。如果未知的自然键是一个真正的可能性,那么你是对的 - 外键是没有必要的。维表中的自然键列上的'UNIQUE'约束将足以保持'temp'键保留。我会在这一点上澄清我的答案。感谢您的评论。 –