【问题标题】:Postgresql update specific fields based on a value (trigger)Postgresql 根据值(触发器)更新特定字段
【发布时间】:2018-12-17 00:30:56
【问题描述】:

我正在 Postgresql 中编写一个触发器,以根据 咨询表上的插入动态更新 收入表的特定列的内容。 [edit]这是我的表格的样子

SELECT * FROM psy_revenues;
id | owner_id | January | February | March | April | etc | etc
---+----------+---------+----------+-------+-------+-----+----
2  | 1        |0        | 0        | 0     | 0     |     |

SELECT * FROM psy_consultations;
id | first_name | last_name |        date                 | payed | owner_id
---+------------+-----------+-----------------------------+-------+---------
3  | Gérard     | Bouchard  |2018-12-05 04:49:26.064397+01| 80    |1
4  | Pasti      | Lami      |2018-12-05 23:23:52.454849+01| 60    |2

我正在寻找一种简单的解决方案,只设置与 consultations 上添加的 NEW 行的时间戳相对应的列(月份)。这很容易根据 psql 中的时间戳获取月份名称:to_char(2018-12-05 04:42:11.66867+01, 'Month') ==> December

[问题] 但是,我不知道如何使用它来指示psql 应该更新哪一列。这是我正在寻找的内容的一般概念。主要问题是我使用 value 来表示 field(参见 SET 行)。是否有解决方法,以便我可以直接选择相关字段? (我尝试了几件事,但都不是很成功)

CREATE OR REPLACE FUNCTION update_revenues()
RETURNS trigger AS
$BODY$
BEGIN
    UPDATE psy_revenues
    SET to_char(NEW.date, 'Month') = (
        SELECT SUM(payed) 
        FROM psy_consultations
        WHERE (
            owner_id = NEW.owner_id 
            AND to_char(date, 'Month')=to_char(NEW.date, 'Month')
        )
    );

RETURN NEW;
END;
$BODY$ LANGUAGE plpgsql;

CREATE TRIGGER update_revenues
AFTER INSERT 
ON psy_consultations
FOR EACH ROW 
EXECUTE PROCEDURE update_revenues();

【问题讨论】:

    标签: sql postgresql plpgsql database-trigger


    【解决方案1】:

    您应该使用动态 SQL。请避免串联以避免SQL注入,使用format函数和位置参数

    CREATE OR REPLACE FUNCTION update_revenues()
    RETURNS trigger AS
    $BODY$
        DECLARE 
        BEGIN
            EXECUTE format (
                'UPDATE psy_revenues 
                  SET  %I = (
                    SELECT SUM(payed) FROM psy_consultations WHERE 
                        owner_id =  $1 AND to_char(date, ''MM-YYYY'') = $2 
                        )'
                 ,to_char(NEW.date, 'FMMonth') )      --column name to update(%I)
     USING NEW.owner_id , to_char(NEW.date, 'MM-YYYY');--values for owner_id & month
        RETURN NEW;
        END;
    $BODY$ LANGUAGE plpgsql;
    

    编辑:似乎TO_CHAR() 为月份字符串填充了空格,我添加了 FM 修饰符(如 @horse 建议的那样)将其删除。

    看这个:

    knayak=# select '|'||to_char(current_date,'Month')||'|' as mon ;
         mon
    -------------
     |December |
    (1 row)
    

    Demo

    【讨论】:

    • 错误:«psy_revenues»第 2 行不存在 «12 月»:SET "December" = (
    • 看起来 12 月仍然算作 str 而不是字段名称
    • @DavidZarebski :这不是问题。我将其更改为“月”而不是“月”,现在检查吗?我希望您的月份列不是使用不同的大小写(使用引号)创建的?
    • 同样的问题。我的列名是一月、二月等。不确定我的列是如何创建的,因为它们是由 django 模型生成的(这可能是我如此挣扎的原因之一)。除了 SELECT * FROM psy_revenues; ,如何确定列名的生成方式?
    • @DavidZarebski :编辑您的问题并向我们展示您所涉及的表的表定义以及每个表的一些示例数据。不亲自测试很难判断。
    【解决方案2】:

    您需要在触发函数中使用动态 sql:生成包含正确 SQL 代码的字符串,并使用 EXECUTE 关键字来运行它。

    下面的代码应该可以解决问题:

    CREATE OR REPLACE FUNCTION update_revenues()
    RETURNS trigger AS
    $BODY$
        DECLARE 
            new_month text;
            new_owner_id integer;
        BEGIN
            new_month    := to_char(NEW.date, 'Month');
            new_owner_id := NEW.owner_id;
            EXECUTE 
                'UPDATE psy_revenues 
                SET ' || new_month || ' = (
                    SELECT SUM(payed) FROM psy_consultations 
                    WHERE (
                        owner_id = ' || new_owner_id || ' 
                        AND to_char(date, ''Month'') = ' || new_month || '
                    )
                )';
    
        RETURN NEW;
        END;
    $BODY$ LANGUAGE plpgsql;
    

    【讨论】:

      猜你喜欢
      • 2017-09-20
      • 1970-01-01
      • 2017-08-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-08
      • 2018-03-22
      相关资源
      最近更新 更多