【问题标题】:Postgres merge two rows with common array elementsPostgres 用公共数组元素合并两行
【发布时间】:2021-01-10 18:20:11
【问题描述】:

我有一个列名为“ids”的 postgres 表。

+----+--------------+
| id |     ids      |
+----+--------------+
|  1 | {1, 2, 3}    |
|  2 | {2, 7, 10}   |
|  3 | {14, 11, 1}  |
|  4 | {12, 13}     |
|  5 | {15, 16, 12} |
+----+--------------+

我想将行与至少一个公共数组元素合并,并从中创建一个新行(或合并到一个现有行中)。所以最终表格如下所示:

+----+--------------------------+
| id |           ids            |
+----+--------------------------+
|  6 | {1, 2, 3, 7, 10, 14, 11} |
|  7 | {12, 13, 15, 16}         |
+----+--------------------------+

结果表中数组元素的顺序并不重要,但它们必须是唯一的。


这些行是独立于另一个系统添加的。例如,我们可以添加一个新行,其中 id 为 {16, 18, 1}

现在,为了确保我们将所有行与至少一个公共数组元素组合在一起,我正在我的服务器 (Node.js) 中进行计算。

因此,在创建新行之前,我会使用以下方法提取数据库中至少有一个共同项的所有现有行:

await t.any('SELECT * FROM arraytable WHERE $1 && ids', [16, 18, 1])

这给了我所有至少有 16、18 或 1 的行。然后我将这些行与 [16,18,1] 合并并删除重复项。

有了这个新数组,我删除了上面提取的所有现有行,并将这个新行插入到数据库中。如您所见,大部分工作都是在 Node.js 中完成的。

我正在尝试创建一个触发器,而不是这个触发器,它会在我添加新行后立即为我执行所有这些步骤。我该如何使用触发器来执行此操作。另外,有没有更好的方法?

【问题讨论】:

    标签: postgresql


    【解决方案1】:

    程序就够了吗?

    CREATE OR REPLACE PROCEDURE add_ids(new_ids INT[])
    AS $$
        DECLARE sum_array INT[];
        BEGIN
            SELECT ARRAY (SELECT UNNEST(ids) FROM table1 WHERE table1.ids && new_ids) INTO sum_array;
            sum_array := sum_array || new_ids;
            SELECT ARRAY(SELECT DISTINCT UNNEST(sum_array)) INTO sum_array;
            DELETE FROM table1 WHERE table1.ids && sum_array;
            INSERT INTO table1(ids) SELECT sum_array;
        END;
    $$
    LANGUAGE plpgsql;
    

    不幸的是,在触发器中插入行会调用另一个触发器,从而导致无限循环。我不知道如何解决这个问题。

    PS。抱歉,如果创建另一个答案是不好的做法。我想暂时留下它以供参考。问题解决后我会删除。

    由 pewpewlasers 编辑:

    为了防止循环,可能需要另一个表。我创建了一个新的临时table2。可以将新数组添加到此表中。该表将有一个触发器来执行计算并将其保存到table1。它还会删除这个临时创建的行。

    CREATE OR REPLACE FUNCTION  on_insert_temp() RETURNS TRIGGER AS $f$
    DECLARE sum_array BIGINT[];
    BEGIN
        SELECT ARRAY (SELECT UNNEST(ids) FROM table1 WHERE table1.ids && NEW.ids) INTO sum_array;
        sum_array := sum_array || NEW.ids;
        SELECT ARRAY(SELECT DISTINCT UNNEST(sum_array)) INTO sum_array;
        DELETE FROM table1 WHERE table1.ids && sum_array;
        INSERT INTO table1(ids) SELECT sum_array;
        DELETE FROM table2 WHERE id = NEW.id;
        RETURN OLD;
    END
    $f$ LANGUAGE plpgsql;
    
    CREATE TRIGGER on_insert_temp AFTER INSERT ON table2 FOR EACH ROW EXECUTE PROCEDURE  on_insert_temp();
    

    【讨论】:

    • 非常感谢罗伯特,使用您的两个答案,我创建了一个可以解决我的问题的编辑。再次感谢!
    【解决方案2】:

    给定表格

    CREATE TABLE table1(id serial, ids INT [] )
    CREATE TABLE table2(id serial, ids INT [] )
    

    触发器看起来像这样

    
    CREATE OR REPLACE FUNCTION sum_tables_trigger() RETURNS TRIGGER AS $table1$
       BEGIN
          INSERT INTO table2(ids) SELECT ARRAY(SELECT DISTINCT UNNEST(table1.ids || new.ids) ORDER BY 1) FROM table1 WHERE table1.ids && new.ids;
          RETURN NEW;
       END;
    $table1$ LANGUAGE plpgsql;
    
    CREATE TRIGGER sum_tables_trigger_ BEFORE INSERT ON table1
    FOR EACH ROW EXECUTE PROCEDURE sum_tables_trigger();
    

    tableA.ids && tableB.ids 返回 true,如果表有共同元素。

    tableA.ids || tableB.ids 添加元素。

    ARRAY(SELECT DISTINCT UNNEST(table1.ids || new.ids) ORDER BY 1) 删除重复项。

    【讨论】:

    • 如果没有表1怎么办?我将修改问题以避免混淆。
    猜你喜欢
    • 1970-01-01
    • 2012-03-04
    • 1970-01-01
    • 2022-11-16
    • 2017-05-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多