我有一个联结表,它只包含外键,如下所示:
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
+-------+-----------+---------+---------+
被引用的表之一是modules
,如下所示:
MODULES
+-----------+---------+---------+
| id | name | version |
+-----------+---------+---------+
| 1 | default | 1 |
| 2 | bar | 1 |
| 3 | foo | 1 |
| 4 | foo | 3 |
| 5 | foo | 2 |
+-----------+---------+---------+
SQL中是否有一种方法可以更新此表,以便将联结表中联结ID comp_id
的{{1}}更新为4
(与模块3相同的名称,但版本更高),联结ID comp_id 4
获得comp_id 6
,它是先前模块版本(2)的42
,因为它们具有匹配的模块名称和 ins_id ,以此类推,对于具有相同条件的所有记录?
散文:
更新联结表中所有具有comp_id 1的模块引用,使用具有匹配模块名称的模块的comp_id更新它们,将其设置为具有最小版本的模块的comp_id,并匹配comp_id
(和可以受联结表中的其他列约束
或者,在业务逻辑中,这可以通过编程更好地解决吗?
答案 0 :(得分:0)
我相信我已经解决了它:答案是window functions(尽管联接的某种组合也许可以做到。我已经接近了,但认为这样做会更优雅和更容易理解)
首先,我必须创建一个CTE,它将模块名称和版本映射到联结表中的值:
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,然后我可以创建一个窗口函数,该函数将选择具有相同模块的记录:
, 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;