从多个来源高效地更新 table
Efficiently updating a table from multiple sources
我正在努力改进 Oracle 中现有 ETL 层的一部分。
- 文件已加载到临时 table。
- 执行许多 MERGE 语句来解析代理键。
- 应用了其他一些业务逻辑(需要那些代理键)。
- 结果已合并
进入 table (同时具有代理键和业务逻辑
结果)
我想改进的是第 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 服务器上写这个,我会做如下的事情:
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 (而我周围的每个人都在尖叫我 'must' 使用 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 中实际有效?
我认为您的 SQL 服务器更新相当于:
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 不允许您加入作为更新本身的一部分。通常您不需要(或不想)在子查询中再次引用目标外部 table,但是您需要针对此处 outer-join 的内容。
您还可以将 left-join 方法与合并结合起来,将本质上相同的子查询放入 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 列中的所有现有值,但听起来您并不希望有任何值。
我相信这可能比亚历克斯的答案更有效——只需要一次访问 temp
table,而不是两次。在我对一百万行的快速测试中,性能大致相同,但计划更好,因为没有第二次访问 temp
table。在您的数据集上可能值得一试。
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);
需要注意的是,每个维度 table 中的 natural_key
列需要 UNIQUE
约束。有了这些约束,Oracle 知道 temp
在您正在更新的视图中是 key-preserved,这就是使上述语法正常的原因。
另一个警告:我曾经遇到过 SELECT
视图中的行与 table 中的行顺序不同的情况。结果是性能 下降 ,因为更新必须多次重新访问每个块。 SELECT
视图中的 ORDER BY temp.rowid
可以解决这个问题。
我正在努力改进 Oracle 中现有 ETL 层的一部分。
- 文件已加载到临时 table。
- 执行许多 MERGE 语句来解析代理键。
- 应用了其他一些业务逻辑(需要那些代理键)。
- 结果已合并 进入 table (同时具有代理键和业务逻辑 结果)
我想改进的是第 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 服务器上写这个,我会做如下的事情:
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 (而我周围的每个人都在尖叫我 'must' 使用 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 中实际有效?
我认为您的 SQL 服务器更新相当于:
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 不允许您加入作为更新本身的一部分。通常您不需要(或不想)在子查询中再次引用目标外部 table,但是您需要针对此处 outer-join 的内容。
您还可以将 left-join 方法与合并结合起来,将本质上相同的子查询放入 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 列中的所有现有值,但听起来您并不希望有任何值。
我相信这可能比亚历克斯的答案更有效——只需要一次访问 temp
table,而不是两次。在我对一百万行的快速测试中,性能大致相同,但计划更好,因为没有第二次访问 temp
table。在您的数据集上可能值得一试。
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);
需要注意的是,每个维度 table 中的 natural_key
列需要 UNIQUE
约束。有了这些约束,Oracle 知道 temp
在您正在更新的视图中是 key-preserved,这就是使上述语法正常的原因。
另一个警告:我曾经遇到过 SELECT
视图中的行与 table 中的行顺序不同的情况。结果是性能 下降 ,因为更新必须多次重新访问每个块。 SELECT
视图中的 ORDER BY temp.rowid
可以解决这个问题。