使用来自自身的值更新 table,但根据来自不同 table 的条件进行选择
Update a table with values from itself, but selecting on criteria from a different table
我有一个仅由外键组成的联结 table,如下所示:
JUNCTION
+-------+-----------+---------+---------+
| id | module_id | comp_id | ins_id |
+-------+-----------+---------+---------+
| 1 | 1 | 2 | 8 |
| 2 | 2 | 4 | 9 |
| 3 | 3 | 4 | 10 |
| 4 | 4 | 1 | 10 |
| 5 | 3 | 5 | 11 |
| 6 | 4 | 1 | 11 |
| 7 | 5 | 42 | 11
+-------+-----------+---------+---------+
引用的 table 之一是 modules
,如下所示:
MODULES
+-----------+---------+---------+
| id | name | version |
+-----------+---------+---------+
| 1 | default | 1 |
| 2 | bar | 1 |
| 3 | foo | 1 |
| 4 | foo | 3 |
| 5 | foo | 2 |
+-----------+---------+---------+
SQL 中是否有更新此 table 的方法,以便更新结点 table 中结点 ID 4
的 comp_id
到 comp_id 4
(与模块 3 同名,但版本更高),结点 ID 6
得到 comp_id 42
,这是 comp_id
前面的模块版本 (2),因为它们具有匹配的模块 names 和 ins_id,对于具有相同的所有记录,依此类推标准?
在散文中:
更新结点 table 中具有 comp_id 1 的所有模块引用,使用具有匹配模块名称的模块的 comp_id 更新它们,将其设置为 comp_id具有下一个最小版本的模块,并匹配 ins_id
(并且可以受到连接 table 中其他列的约束)
或者,这在业务逻辑中以编程方式处理会更好吗?
我相信我已经解决了:答案是 window functions(尽管某些连接组合可能能够做到这一点。我已经接近了,但认为这会更优雅和更容易理解)
首先,我必须创建一个 CTE,将模块名称和版本映射到联结 table:
中的值
WITH module_mapping AS (SELECT j.module_id, j.ins_id, j.comp_id, m.name, m.version
FROM junction AS j
JOIN modules AS m ON m.id = j.module_id
)
使用该 CTE,我可以创建一个 window 函数,该函数将 select 具有相同模块的记录 :
, win AS (SELECT name, version, module_id, ins_id, comp_id, first_value(comp_id)
OVER (PARTITION BY name, ins_id ORDER BY version DESC
ROWS BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING)
FROM module_mapping)
所以,当我进行以下查询时,我可以看到 first_value
列有我要查找的记录:
WITH module_mapping AS (SELECT j.module_id, j.ins_id, j.comp_id, m.name, m.version
FROM junction AS j
JOIN modules AS m ON m.id = j.module_id
)
, win AS (SELECT name, version, module_id, ins_id, comp_id, first_value(comp_id)
OVER (PARTITION BY name, ins_id ORDER BY version DESC
ROWS BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING) -- this key part excludes the current row, since it will have the largest (newest) module version
FROM module_mapping)
SELECT * FROM win WHERE comp_id = 1;
+-------+---------+-----------+--------+---------+-------------+
| name | version | module_id | ins_id | comp_id | first_value |
+-------+---------+-----------+--------+---------+-------------+
| "foo" | 3 | 4 | 10 | 1 | 4 |
| "foo" | 3 | 4 | 11 | 1 | 42 |
+-------+---------+-----------+--------+---------+-------------+
所以有了这两个 CTE,我可以调用我的更新 sql:
UPDATE junction AS j
SET comp_id = win.new_comp
FROM win
WHERE j.comp_id = 1 -- only change the default ones
AND j.module_id = win.module_id
AND j.ins_id = win.ins_id;
我有一个仅由外键组成的联结 table,如下所示:
JUNCTION
+-------+-----------+---------+---------+
| id | module_id | comp_id | ins_id |
+-------+-----------+---------+---------+
| 1 | 1 | 2 | 8 |
| 2 | 2 | 4 | 9 |
| 3 | 3 | 4 | 10 |
| 4 | 4 | 1 | 10 |
| 5 | 3 | 5 | 11 |
| 6 | 4 | 1 | 11 |
| 7 | 5 | 42 | 11
+-------+-----------+---------+---------+
引用的 table 之一是 modules
,如下所示:
MODULES
+-----------+---------+---------+
| id | name | version |
+-----------+---------+---------+
| 1 | default | 1 |
| 2 | bar | 1 |
| 3 | foo | 1 |
| 4 | foo | 3 |
| 5 | foo | 2 |
+-----------+---------+---------+
SQL 中是否有更新此 table 的方法,以便更新结点 table 中结点 ID 4
的 comp_id
到 comp_id 4
(与模块 3 同名,但版本更高),结点 ID 6
得到 comp_id 42
,这是 comp_id
前面的模块版本 (2),因为它们具有匹配的模块 names 和 ins_id,对于具有相同的所有记录,依此类推标准?
在散文中:
更新结点 table 中具有 comp_id 1 的所有模块引用,使用具有匹配模块名称的模块的 comp_id 更新它们,将其设置为 comp_id具有下一个最小版本的模块,并匹配 ins_id
(并且可以受到连接 table 中其他列的约束)
或者,这在业务逻辑中以编程方式处理会更好吗?
我相信我已经解决了:答案是 window functions(尽管某些连接组合可能能够做到这一点。我已经接近了,但认为这会更优雅和更容易理解)
首先,我必须创建一个 CTE,将模块名称和版本映射到联结 table:
中的值WITH module_mapping AS (SELECT j.module_id, j.ins_id, j.comp_id, m.name, m.version
FROM junction AS j
JOIN modules AS m ON m.id = j.module_id
)
使用该 CTE,我可以创建一个 window 函数,该函数将 select 具有相同模块的记录 :
, win AS (SELECT name, version, module_id, ins_id, comp_id, first_value(comp_id)
OVER (PARTITION BY name, ins_id ORDER BY version DESC
ROWS BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING)
FROM module_mapping)
所以,当我进行以下查询时,我可以看到 first_value
列有我要查找的记录:
WITH module_mapping AS (SELECT j.module_id, j.ins_id, j.comp_id, m.name, m.version
FROM junction AS j
JOIN modules AS m ON m.id = j.module_id
)
, win AS (SELECT name, version, module_id, ins_id, comp_id, first_value(comp_id)
OVER (PARTITION BY name, ins_id ORDER BY version DESC
ROWS BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING) -- this key part excludes the current row, since it will have the largest (newest) module version
FROM module_mapping)
SELECT * FROM win WHERE comp_id = 1;
+-------+---------+-----------+--------+---------+-------------+
| name | version | module_id | ins_id | comp_id | first_value |
+-------+---------+-----------+--------+---------+-------------+
| "foo" | 3 | 4 | 10 | 1 | 4 |
| "foo" | 3 | 4 | 11 | 1 | 42 |
+-------+---------+-----------+--------+---------+-------------+
所以有了这两个 CTE,我可以调用我的更新 sql:
UPDATE junction AS j
SET comp_id = win.new_comp
FROM win
WHERE j.comp_id = 1 -- only change the default ones
AND j.module_id = win.module_id
AND j.ins_id = win.ins_id;