【问题标题】:Truncating table before insert in postgres在 postgres 中插入之前截断表
【发布时间】:2016-12-31 09:41:15
【问题描述】:

我正在尝试创建一种机制,每次调用插入命令时都会截断表。

首先我尝试创建一个函数,该函数获取行数组并在插入之前截断表格:

CREATE OR REPLACE FUNCTION insert_customers(customers customer[])
    RETURNS VOID AS 
$$  
BEGIN
    TRUNCATE TABLE Customers;
    INSERT INTO Customers (CustomerID, CustomerName) SELECT * FROM UNNEST(customers::Customer[]);
END
$$
LANGUAGE plpgsql;

其中 Customer 是用户定义的类型,表示 Customers 表中的一行:

CREATE TYPE Customer AS (
  customerid VARCHAR(100),
  customername VARCHAR(100));

当我从 psql 调用函数时,它起作用了:

SELECT * FROM insert_customers(ARRAY[ROW('ID1', 'Name1'),ROW('ID2', 'Name2')]::Customer[]);

但我还没有找到从我的 java 代码中执行此插入的方法:

String[][] customers = new String[2][];
customers[0] = new String[] { "ID1", "Name1" };
customers[1] = new String[] { "ID2", "Name2" };    

Array sqlArray = connection.createArrayOf("customer", customers);    

CallableStatement cstmt = connection.prepareCall("{ call insert_customers(?) }");
cstmt.setArray(1, sqlArray);
cstmt.execute();

创建了以下语句:

select * from insert_customers ('{{"ID1", "Name1"},{"ID2", "Name2"}}') as result

并抛出以下异常:

org.postgresql.util.PSQLException: ERROR: malformed record literal: "ID1"\n Detail: Missing left parenthesis.

然后我尝试创建一个触发器,该触发器将在插入事件时截断表:

CREATE TRIGGER on_insert_customers
    BEFORE INSERT ON Customers
    EXECUTE PROCEDURE truncate_customers();

truncate_customers 是:

CREATE OR REPLACE FUNCTION truncate_customers()
    RETURNS TRIGGER AS 
$$  
BEGIN
    TRUNCATE TABLE Customers;   
END
$$
LANGUAGE plpgsql;

这引发了异常:

cannot TRUNCATE "customers" because it is being used by active queries in this session

有什么想法???

【问题讨论】:

    标签: java postgresql function stored-procedures truncate


    【解决方案1】:

    修改函数

    查询一个文本数组的非嵌套元素对:

    select cols[1], cols[2]
    from (
        select (ordinality- 1)/2, array_agg(unnest) cols
        from unnest('{"id1", "name1", "id2", "name2"}'::text[]) with ordinality
        group by 1
        ) s
    
     cols | cols  
    ------+-------
     id1  | name1
     id2  | name2
    (2 rows)
    

    在你的函数中使用它,这样它就可以有一个文本数组作为参数:

    create or replace function insert_customers(text[])
    returns void language plpgsql as $$  
    begin
        truncate table customers;
        insert into customers (customerid, customername)
        select cols[1], cols[2]
        from (
            select (ordinality- 1)/2, array_agg(unnest) cols
            from unnest($1) with ordinality
            group by 1
            ) s;
    end $$;
    
    select insert_customers('{"id1", "name1", "id2", "name2"}');
    

    修改触发函数

    您不能在触发器中截断表,但可以删除所有行:

    ...
        delete from Customers;
        return new;
    ...
    

    【讨论】:

    • 很棒的答案@klin!我仍然想弄清楚的一件事是如何使函数对每个列号都通用。特别是这部分:“选择 cols[1], cols[2] from”。当将其更改为“选择列来自”时,它会引发错误,因为选择将数组作为一个字段返回。如何将数组分解为单独的值?
    • 该函数预计插入两列,您无法更改它而不更改整个逻辑。但是,您可以将包含任意偶数个元素的数组传递给它。
    • 要概括算法,您需要一个动态sql(根据列数创建查询文本并使用execute运行)。
    • 非常感谢@klin!
    猜你喜欢
    • 2018-04-15
    • 1970-01-01
    • 1970-01-01
    • 2019-02-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-23
    相关资源
    最近更新 更多