如何在 Postgresql 中转换为 int 数组?

Léo*_* 준영 9 postgresql insert plpgsql cast array

我想从 ARGV[] 转换到 PostgreSQL 中的 int 数组,我在代码中用TODO标记了伪代码。x86_64-unknown-linux-gnu 上 PostgreSQL 9.4.3 中的代码,由 gcc (Debian 4.9.2-10) 4.9.2 编译,64 位:

CREATE TABLE measurements (
        measurement_id SERIAL PRIMARY KEY NOT NULL,
        measurement_size_in_bytes INTEGER NOT NULL
);

CREATE TABLE events (
        event_id SERIAL PRIMARY KEY NOT NULL, 
        measurement_id INTEGER NOT NULL, 
        event_index_start INTEGER NOT NULL,
        event_index_end INTEGER NOT NULL
);

CREATE OR REPLACE FUNCTION insaft_function() 
    RETURNS TRIGGER AS 
$func$
BEGIN 
  -- TODO Loop until TG_ARGV[0] empty
INSERT INTO events (measurement_id, event_index_start, event_index_end) 
SELECT NEW.measurement_id, TG_ARGV[0]::int[], TG_ARGV[1]::int[];
  -- END TODO
RETURN NULL; -- result ignored since this is an AFTER trigger
END 
$func$ LANGUAGE plpgsql;

CREATE TRIGGER insaft_measurement_ids
AFTER INSERT ON measurements 
FOR EACH ROW EXECUTE PROCEDURE insaft_function("{101, 111, 121}", "{101, 111, 121}"); 
Run Code Online (Sandbox Code Playgroud)

我可以从

INSERT INTO measurements (measurement_size_in_bytes) VALUES (888);
Run Code Online (Sandbox Code Playgroud)

我知道错误在于将这里TG_ARGV[0]::int[]转换为 intarray,我希望循环遍历直到 intarray 为空。可能存在更好的方法来执行此类循环插入。

ErwinBrandstetter 的代码输出

代码

DROP TABLE IF EXISTS measurements, events, file_headers;

CREATE TABLE measurements (
        measurement_id SERIAL PRIMARY KEY NOT NULL,
        measurement_size_in_bytes INTEGER NOT NULL
);

CREATE TABLE events (
        event_id SERIAL PRIMARY KEY NOT NULL, 
        measurement_id INTEGER NOT NULL, 
        event_index_start INTEGER NOT NULL,
        event_index_end INTEGER NOT NULL
);

DROP TRIGGER IF EXISTS insaft_ids ON measurements;
DROP FUNCTION IF EXISTS insaft_function();

CREATE OR REPLACE FUNCTION insaft_function() 
    RETURNS TRIGGER AS 
$func$
DECLARE
   m int[];
BEGIN 
   FOREACH m SLICE 1 IN ARRAY TG_ARGV::int[]
   LOOP
      INSERT INTO events (measurement_id, event_index_start, event_index_end) 
      SELECT NEW.measurement_id, m[1], m[2];  -- Postgres array starts with 1 !
   END LOOP;
   RETURN NULL; -- result ignored since this is an AFTER trigger
END 
$func$ LANGUAGE plpgsql;

CREATE TRIGGER insaft_ids
AFTER INSERT ON measurements 
FOR EACH ROW EXECUTE PROCEDURE insaft_function('{{101,201},{201,300}}'); 
Run Code Online (Sandbox Code Playgroud)

跑步

sudo -u postgres psql detector -c "INSERT INTO measurements (measurement_size_in_bytes) VALUES (77777);"
Run Code Online (Sandbox Code Playgroud)

得到

ERROR:  invalid input syntax for integer: "{{101,201},{201,300}}"
CONTEXT:  PL/pgSQL function insaft_function() line 5 at FOREACH over array
Run Code Online (Sandbox Code Playgroud)

如何将文本的 ARGV[] 转换为 PostgreSQL 中的 int 数组?

Erw*_*ter 13

在传递整数时,您可以转换整个数组:

TG_ARGV::int[]
Run Code Online (Sandbox Code Playgroud)

或者你可以转换一个element,那么它必须是元素类型:

TG_ARGV[0]::int
Run Code Online (Sandbox Code Playgroud)

我在回答你之前的问题时就是这样使用的:

但是,您传递的不是整数,而是整数数组的文本表示:整数数组文字 - 也有非法语法:值必须用单引号括起来,双引号用于标识符:

FOR EACH ROW EXECUTE PROCEDURE insaft_function("{101, 111, 121}", "{101, 111, 121}"); 
Run Code Online (Sandbox Code Playgroud)
FOR EACH ROW EXECUTE PROCEDURE insaft_function('{101, 111, 121}', '{101, 111, 121}'); 
Run Code Online (Sandbox Code Playgroud)

由于您传递的integer不是整数数组文字,因此您不能在代码中执行任何操作。考虑一下(使用具有不同数字的更清晰的示例):

SELECT (ARRAY['{101, 111, 121}', '{201, 211, 221}'])[1]::int[];
SELECT (ARRAY['{101, 111, 121}', '{201, 211, 221}'])::text[];
SELECT (ARRAY['{101, 111, 121}'::int[], '{201, 211, 221}'])::int[];
SELECT (ARRAY['{101, 111, 121}'::int[], '{201, 211, 221}'])[1][1];
SELECT (ARRAY['{{101, 111, 121},{201, 211, 221}}'])[1];
SELECT (ARRAY['{{101, 111, 121},{201, 211, 221}}'])[1]::int[];
Run Code Online (Sandbox Code Playgroud)

@Chris 为此提供了更多信息。

您的函数的解决方案

我建议你要么通过所有整数二维数组文本。后者的演示代码:

'{{101,111,121},{201,211,221}}'
Run Code Online (Sandbox Code Playgroud)

或者更确切地说(根据我有根据的猜测,数组旋转了):

'{{101,201},{111,211},{121,221}}'
Run Code Online (Sandbox Code Playgroud)

所以,这是一个参数:

CREATE TRIGGER ...
FOR EACH ROW EXECUTE PROCEDURE insaft_function('{{101,201},{111,211},{121,221}}'); 
Run Code Online (Sandbox Code Playgroud)

至于循环,使用FOREACH m SLICE 1 IN ARRAY. 看:

函数可能如下所示:

CREATE OR REPLACE FUNCTION insaft_function() 
  RETURNS TRIGGER
  LANGUAGE plpgsql AS 
$func$
DECLARE
   m int[];
BEGIN 
   FOREACH m SLICE 1 IN ARRAY TG_ARGV[0]::int[]  -- reference 1st param
   LOOP
      INSERT INTO events (measurement_id, event_index_start, event_index_end) 
      SELECT NEW.measurement_id, m[1], m[2];  -- Postgres array starts with 1 !
   END LOOP;
   RETURN NULL; -- result ignored since this is an AFTER trigger
END 
$func$;
Run Code Online (Sandbox Code Playgroud)

但我怀疑可能有更简单的整体方法。这仅在您有许多需要相同触发器的表时才有意义,只是具有不同的整数......