用于更新列的递归 PostgreSQL 查询

Recursive PostgreSQL Query for Updating Column

您好,我正在尝试使用 SQL 查询递归更新数据库列。

我有 2 tables modulemodule_dependency.

第一个 table module 有列(带有示例数据):

id, name, state,
1, "website", "installed"
2, "purchase", "installed"
3, "crm", "installed"
4, "sale", "uninstalled"
5, "account", "installed"
6, "website_sale", "installed"
7, "purchase_bonus", "installed"
8, "website_blog", "installed"
9, "sale_discount", "installed"
10, "website_online", "installed"

第二个 table module_dependency 有列(带有示例数据):

 id | dependency_name | module_id 
----+-----------------+-----------
  1 | website_sale    |         1
  2 | sale_bonus      |         4
  3 | website_blog    |         1
  4 | sale_discount   |         4
  5 | website_online  |         1
  6 | crm             |         10
  7 | account         |         3

我需要将网站模块的状态更改为“升级”及其所有相关模块。

我可以检查当前模块的状态:

SELECT * FROM module WHERE name = 'website' AND state = 'installed';

 id |  name   |   state   
----+---------+-----------
  1 | website | installed

要获取网站模块的所有依赖项,我使用查询:

SELECT m.name, md.dependency_name , md.module_id
FROM module m JOIN module_dependency md ON (m.id = md.module_id)
WHERE m.name = 'website' AND m.state = 'installed';

  name   | dependency_name | module_id 
---------+-----------------+-----------
 website | website_sale    |         1
 website | website_blog    |         1
 website | website_online  |         1

表示website_salewebsite_blogwebsite_online 取决于 module_id 1 即网站。

主要问题是 website_online 依赖于 crm 和 crm on account 模块。

SELECT m.name, md.dependency_name , md.module_id
FROM module m JOIN module_dependency md ON (m.id = md.module_id)
WHERE m.name = 'website_online' AND m.state = 'installed';

      name      | dependency_name | module_id 
----------------+-----------------+-----------
 website_online | crm             |        10

SELECT m.name, md.dependency_name , md.module_id
FROM module m JOIN module_dependency md ON (m.id = md.module_id)
WHERE m.name = 'crm' AND m.state = 'installed';

 name | dependency_name | module_id 
------+-----------------+-----------
 crm  | account         |         3

我需要递归地将所有网站依赖项的状态更改为“升级”。更新后的行应如下所示:

id, name, state,
1, "website", "to upgrade"
3, "crm", "to upgrade"
5, "account", "to upgrade"
6, "website_sale", "to upgrade"
8, "website_blog", "to upgrade"
10, "website_online", "to upgrade"

您可以使用测试数据创建 tables:

CREATE TABLE module(id integer, name text, state text);
INSERT INTO module VALUES
(1, 'website', 'installed'),
(2, 'purchase', 'installed'),
(3, 'crm', 'installed'),
(4, 'sale', 'uninstalled'),
(5, 'account', 'installed'),
(6, 'website_sale', 'installed'),
(7, 'purchase_bonus', 'installed'),
(8, 'website_blog', 'installed'),
(9, 'sale_discount', 'installed'),
(10, 'website_online', 'installed');


CREATE TABLE module_dependency(id integer, dependency_name text, module_id integer);
INSERT INTO module_dependency VALUES
(1, 'website_sale', 1),
(2, 'sale_bonus', 4),
(3, 'website_blog', 1),
(4, 'sale_discount', 4),
(5, 'website_online', 1),
(6, 'crm', 10),
(7, 'account', 3);

我尝试编写递归查询,但没有成功。看起来像无限循环。如果我可以获得模块 table 的所有模块 ID,我可以简单地更新选定的条目。

WITH RECURSIVE modules_to_upgrade AS
(
    SELECT id
    FROM module
    WHERE name = 'website'

        UNION ALL

    SELECT md.module_id
    FROM module_dependency md JOIN modules_to_upgrade mtu on mtu.id = md.module_id
)
select * from modules_to_upgrade;

为了 link 递归部分回到模块,你似乎不能使用依赖 ID。

所以它必须在名字上。

WITH RECURSIVE rcte_modules_to_upgrade AS
(
    SELECT 
      m.id as module_id
    , m.name as module_name
    , md.id as dependency_id
    , md.dependency_name
    , m.id as base_module_id
    , 1 as depth
    FROM module m
    JOIN module_dependency md 
      ON md.module_id = m.id
    WHERE m.name = 'website'

    UNION ALL

    SELECT 
      m.id
    , m.name
    , md.id
    , md.dependency_name
    , rcte.base_module_id
    , rcte.depth + 1
    FROM rcte_modules_to_upgrade rcte
    JOIN module m
      ON m.name = rcte.dependency_name
    LEFT JOIN module_dependency md 
      ON md.module_id = m.id
)
select * 
from rcte_modules_to_upgrade;
module_id | module_name    | dependency_id | dependency_name | base_module_id | depth
--------: | :------------- | ------------: | :-------------- | -------------: | ----:
        1 | website        |             1 | website_sale    |              1 |     1
        1 | website        |             3 | website_blog    |              1 |     1
        1 | website        |             5 | website_online  |              1 |     1
        6 | website_sale   |          null | null            |              1 |     2
        8 | website_blog   |          null | null            |              1 |     2
       10 | website_online |             6 | crm             |              1 |     2
        3 | crm            |             7 | account         |              1 |     3
        5 | account        |          null | null            |              1 |     4
WITH RECURSIVE rcte_modules_to_upgrade AS
(
    SELECT 
      m.id as module_id
    , md.dependency_name
    FROM module m
    JOIN module_dependency md 
      ON md.module_id = m.id
    WHERE m.name = 'website'

    UNION ALL

    SELECT 
      m.id
    , md.dependency_name
    FROM rcte_modules_to_upgrade rcte
    JOIN module m
      ON m.name = rcte.dependency_name
    LEFT JOIN module_dependency md 
      ON md.module_id = m.id
)
update module
set state = 'to upgrade'
where id in (select distinct module_id 
             from rcte_modules_to_upgrade);
6 rows affected
select * from module order by id;
id | name           | state      
-: | :------------- | :----------
 1 | website        | to upgrade 
 2 | purchase       | installed  
 3 | crm            | to upgrade 
 4 | sale           | uninstalled
 5 | account        | to upgrade 
 6 | website_sale   | to upgrade 
 7 | purchase_bonus | installed  
 8 | website_blog   | to upgrade 
 9 | sale_discount  | installed  
10 | website_online | to upgrade 

db<>fiddle here