【问题标题】:PostgreSQL SQL Problem with Triggers and duplicatesPostgreSQL SQL 的触发器和重复问题
【发布时间】:2021-10-05 07:19:54
【问题描述】:

我有一个问题,我想使用 SQL 查询和触发器消除不相关的信息

表是

create table z_test(id, fyear smallint, byear text);

要插入的数据。

insert into z_test  (fyear, byear) values (Null, '2018 2019 2020');
insert into z_test  (fyear, byear) values (null, '2018 2019 2020');
insert into z_test  (fyear, byear) values (null,  '2018 2019 2020');

问题定义:我希望第一行的 fyear 为 2018,第二行的 fyear 为 2019, 2020 年为第三排。如果我插入的行数等于年数 在byear,它应该被插入。如果不相等,则可以将 Null 插入 fyear

另外,我也可以获得这样的数据。

insert into z_test  (fyear, byear) values (Null, '2018 2019 ');
insert into z_test  (fyear, byear) values (null, '2018 2019 ');

在这种情况下,2018 应该是第一行,2019 应该是第二行。

到目前为止我已经尝试过。试图在插入 z_test 的触发器中创建 CTE。里面 触发器,我使用 regexp_split_to_table 将 byear 拆分为行。然后我尝试把第一个 第一行是一年,第二行是第二年,以此类推。我正在使用事务和 psycopg2。不幸的是,问题是触发器无法在表中找到以前未提交的行,我被困在这里。请帮忙。所以所有值都为空。有没有办法使用 SQL 查询,最好是触发器?

CREATE TRIGGER z_test_trigger
    AFTER INSERT
    ON public.z_test
    FOR EACH ROW
    EXECUTE PROCEDURE public.z_test_procedure();

CREATE FUNCTION public.z_test_procedure()
    RETURNS trigger
    LANGUAGE 'plpgsql'
    COST 100
    VOLATILE NOT LEAKPROOF
AS $BODY$
BEGIN 
    
        WITH cte AS(
        select numyr::SMALLINT,byear from 
        (select  trim(regexp_split_to_table(NEW.ner_text, '\s+')) AS numyr 
         )x where is_numeric(numyr) =true and length(numyr)=4 and numyr like '20%' )
        SELECT numyr INTO NEW.fyear FROM cte  WHERE numyr NOT IN (SELECT byear FROM z_test WHERE  
         NEW.byear =  cte.byear  ) LIMIT 1  ;
    
    RETURN NEW;
END;
$BODY$;

【问题讨论】:

  • 解决方案是清理INSERT之前的数据。一个函数在它自己的事务中运行,它不会像你发现的那样在其他事务中看到未提交的信息。
  • 它是单个事务的一部分。我将行作为单个事务的一部分插入到表中。我可以忽略我没有插入的其他交易,因为这将是一个完全不同的过程。我的问题是,为什么触发器没有“看到”我之前插入的数据,即使它是同一个事务? Postgre 文档“Read Committed 是 PostgreSQL 中的默认隔离级别。...但是,SELECT 确实会看到在其自己的事务中执行的先前更新的影响,即使它们尚未提交。”
  • 对于初学者来说,它是AFTER TRIGGER,所以RETURN NEW; 无效。见plpgsql trigger

标签: sql postgresql triggers


【解决方案1】:

我完全不知道你所说的是什么意思“如果我插入的行数等于byear的年数,应该插入它。如果不相等,那么可以插入Null into fyear" 这似乎意味着您的触发器必须记住插入语句的处理方式。这不可以;所以整件事毫无意义——除非你能解释清楚。
无论如何,触发器实现是错误的方法。您可以使用单个插入语句来执行此操作并完全摆脱触发器,而不是多次插入来生成多行。使用 CTE 从 byear 中提取值,然后 Insert ... select.. 为 CTE 中的每个值(您可以同时处理重复项):

alter table z_test add constraint fyear_uk unique(fyear);

with cte (fyear, byear) as  
     ( select  trim(regexp_split_to_table('2018 2019 2020', '\s+')), '2018 2019 2020')
insert into z_testfyear, byear)
     select cte.fyear::smallint, cte.byear 
       from cte 
      where cte.fyear ~ '^20\d\d$'
 on conflict (fyear) do nothing; 

为避免重复byear 字符串,您可以将查询包装在带有byear 值参数的SQL 过程中。

create or replace 
procedure generate_z_test(byear_in text)
  language sql
as $$
     with cte (fyear, byear) as  
          ( select trim(regexp_split_to_table(byear_in, '\s+')), byear_in )
     insert into z_test(fyear, byear)
          select cte.fyear::smallint, cte.byear 
            from cte 
           where cte.fyear ~ '^20\d\d$' 
     on conflict (fyear) do nothing;
$$;       

demo here

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-08-06
    • 2012-02-28
    • 1970-01-01
    • 1970-01-01
    • 2013-03-14
    • 1970-01-01
    • 2020-12-24
    • 2020-12-15
    相关资源
    最近更新 更多