web-dev-qa-db-ja.com

Postgresqlでint配列にキャストする方法は?

テキストである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}"); 

最初にできること

INSERT INTO measurements (measurement_size_in_bytes) VALUES (888);

ここでのキャストに間違いがあることを知っています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}}'); 

ランニング

Sudo -u postgres psql detector -c "INSERT INTO measurements (measurement_size_in_bytes) VALUES (77777);"

取得

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

テキストであるARGV []からPostgreSQLのint配列にどのようにキャストできますか?

整数値を渡しながら、配列全体をキャストできます:

_TG_ARGV::int[]
_

または、elementをキャストできます。その場合は、要素タイプである必要があります。

_TG_ARGV[0]::int
_

私はあなたの前の質問への私の答えでそれをそのように使用しました:

ただし、整数を渡すのではなく、整数配列のテキスト表現:整数配列リテラル-不正な構文も使用:値を囲む必要があります一重引用符では、二重引用符は識別子用です:

FOR EACH ROW EXECUTE PROCEDURE insaft_function("{101, 111, 121}", "{101, 111, 121}"); 
_FOR EACH ROW EXECUTE PROCEDURE insaft_function('{101, 111, 121}', '{101, 111, 121}'); 
_

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[];
_

@ Chrisはそのための追加情報を提供しました。

あなたの機能のためのソリューション

どちらかすべての整数を渡すかまたは2次元配列リテラルをお勧めします。 latterのコードのデモ:

_'{{101,111,121},{201,211,221}}'
_

またはむしろ(私の知識に基づく推測に基づいて配列がピボットされます):

_'{{101,201},{111,211},{121,221}}'
_

したがって、これはoneパラメータです:

_CREATE TRIGGER ...
FOR EACH ROW EXECUTE PROCEDURE insaft_function('{{101,201},{111,211},{121,221}}'); 
_

ループについては、_FOREACH m SLICE 1 IN ARRAY_を使用します。

関数は次のようになります。

_CREATE OR REPLACE FUNCTION insaft_function() 
    RETURNS TRIGGER 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$ LANGUAGE plpgsql;
_

しかし、私は全体的にシンプルなアプローチがあるのではないかと疑っています。これは、整数が異なるだけで、同様のトリガーを必要とする多くのテーブルがある場合にのみ意味があります...

7

私には、私があなたの質問を明確に理解している場合、あなたは正しい方向に進んでいるように見えます(私がそうであるかどうかはわかりません。:P)

私には、DECLAREブロックが単に必要であるように見えます。このブロックでは、関数ブロック全体を通じて永続する変数値を宣言しています。

DECLAREを追加

CREATE OR REPLACE FUNCTION insaft_function() 
RETURNS TRIGGER AS
DECLARE
myarray_0 int[] := TG_ARGV[0]::int[]
myarray_1 int[] := TG_ARGV[1]::int[]
$func$
BEGIN 
-- TODO Loop until myarray_0 empty
-- Decide what you really want to do here!!
-- Is it a parallel loop through myarray_0 and myarray_1, or a Cartesian, or...
-- END TODO
RETURN NULL; -- result ignored since this is an AFTER trigger
END 
$func$ LANGUAGE plpgsql;

キャストは正しく行われていますが、関数内で簡単にループ処理したい場合は、DECLAREブロックを使用して前もって実行する必要があります。

ただし、これがあなたが望んでいる挿入を達成するための最良のアプローチである場合は、議論を別の時間に残すことができます...

1
Chris