Cum se face cast la int array în Postgresql? (Administrarea bazelor de date, Postgresql, Insert, Plpgsql, Matrice, Cast)

Léo Léopold Hertz 준영 a intrebat.
a intrebat.

Aș dori să fac cast de la ARGV[] care este text la int array în PostgreSQL unde am marcat pseudocodul prin TODO în cod. Codul în PostgreSQL 9.4.3 pe x86_64-unknown-linux-gnu, compilat de gcc (Debian 4.9.2-10) 4.9.2, 64 de biți:

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}"); 

pe care îl pot începe prin

INSERT INTO measurements (measurement_size_in_bytes) VALUES (888);

Știu că greșeala este în turnarea de aici TG_ARGV[0]::int[] în intarray prin care aș vrea să fac o buclă până când intarray-ul este gol. Este posibil să existe o modalitate mai bună de a face astfel de inserții în buclă.

Rezultatul codului lui ErwinBrandstetter

Cod

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}}'); 

Rulează

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

obține

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

Cum se poate face cast de la ARGV[] care este text la int array în PostgreSQL?

2 răspunsuri
Erwin Brandstetter

În timp ce treceți numere întregi, , puteți fie să distribuiți întregul array:

TG_ARGV::int[]

fie puteți arunca un element, , atunci acesta trebuie să fie tipul elementului:

TG_ARGV[0]::int

Am folosit acest mod în răspunsul meu la întrebarea anterioară:

  • SELECT în funcția de declanșare în două tabele

Cu toate acestea,, , nu treceți numere întregi, ci reprezentarea textuală a tablourilor de numere întregi: un literal de tablou de numere întregi – cu o sintaxă ilegală, de asemenea: valorile trebuie să fie incluse în ghilimele simple, , ghilimelele duble sunt pentru identificatori:

<strike>FOR EACH ROW EXECUTE PROCEDURE insaft_function("{101, 111, 121}", "{101, 111, 121}"); </strike>
FOR EACH ROW EXECUTE PROCEDURE insaft_function('{101, 111, 121}', '{101, 111, 121}'); 

Din moment ce nu treceți integer ci literale de matrice de numere întregi, nu puteți face niciuna dintre acestea în codul dumneavoastră. Luați în considerare (folosind un exemplu mai clar cu numere distincte):

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 a furnizat mai multe pentru asta.

Soluția pentru funcția dvs.

Vă sugerez fie treceți toate numerele întregi fie un literal de matrice bidimensională. Cod demonstrativ pentru acesta din urmă:

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

Sau mai degrabă (array-ul pivotat, pe baza presupunerilor mele):

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

Deci, aceasta este unul parametru:

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

În ceea ce privește bucla, folosiți FOREACH m SLICE 1 IN ARRAY:

  • Postgres – matrice pentru buclă

Funcția ar putea arăta astfel:

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;

Dar am bănuielile mele că ar putea exista o abordare generală mai simplă. Acest lucru are sens doar dacă aveți mai multe tabele care au nevoie de un declanșator similar, dar cu numere întregi diferite …

Comentarii

  • @Masi: De fapt, am testat funcția pe care am furnizat-o și funcționează pentru mine. Sunteți conștient de faptul că int denotă tipul de număr întreg și int[] denotă un array de numere întregi? Câteva noțiuni de bază aici: stackoverflow.com/a/1241414884/939860.  > Por Erwin Brandstetter.
  • @Masi: Îmi pare rău, trebuie să fie TG_ARGV [0] ::int[]. Am testat două versiuni (either pass all integer numbers or a 2-dimensional array literal) și am amestecat declanșatorul și definiția funcției din ambele din greșeală. Sintaxa TG_ARGV::int[] funcționează doar „toate numerele întregi” – mai multe parametri de genul: insaft_function(101,201,201,300). Din moment ce treceți un literal de text pentru o matrice int (2-dimensională) ca unul trebuie să vă referiți la TG_ARGV[0]. Am adaptat codul de mai sus. –  > Por Erwin Brandstetter.
  • events.event_index_start și events.event_index_end trebuie să fie definite ca bigint în consecință. –  > Por Erwin Brandstetter.
  • Da, deci folosiți bigint if unsigned int dar aici signed int deci păstrați pentru a folosi doar int. Urmăriți thread-ul aici stackoverflow.com/questions/31533496/… –  > Por Léo Léopold Hertz 준영.
Chris

Mie mi se pare că ești pe drumul cel bun, dacă înțeleg clar întrebarea ta (ceea ce nu sunt sigur că sunt. 😛 )

Pentru mine, se pare că ai nevoie pur și simplu de un DECLARE bloc, în care să declarați valori variabile care vor persista pe tot parcursul blocului de funcții.

Adăugați 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;

Ai făcut distribuția corect, dar dacă vrei să le treci în buclă cu ușurință în funcție, trebuie doar să o faci în avans cu un bloc DECLARE bloc.

Putem lăsa dezbaterea pentru altă dată, însă, dacă aceasta este cea mai bună abordare pentru realizarea inserțiilor pe care sperați să le faceți…