使用自身的值更新表,但从其他表中选择条件

时间:2019-02-20 01:00:49

标签: postgresql postgresql-9.5

我有一个联结表,它只包含外键,如下所示:

 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(和可以受联结表中的其他列约束

或者,在业务逻辑中,这可以通过编程更好地解决吗?

1 个答案:

答案 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;