我正在努力改进Oracle中现有ETL层的一部分。
这是我想要改进的第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中实际上是否具有效果?
答案 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
可以解决此问题。