含函数的多维数组

来自 PostgreSQL wiki
跳转到导航跳转到搜索

作者:Emanuel Calvo Franco、Alvaro Herrera、Carlos Bazán、Miguel Villa Gomez。

本文基于如下邮件列表主题撰写:http://archives.postgresql.org/message-id/86D81900A3164F978746F096076C5F53@pc13

尽管数组主题理解起来很容易,但可以考虑一些技巧。


以下代码对读者来说似乎既合乎逻辑又实操,但执行此函数会返回以下错误

ERROR: 无效的数组下标 CONTEXT: PL/pgSQL 函数“pepe”第 7 行的赋值

但是,编译不会带来任何错误。

CREATE OR REPLACE FUNCTION pepe(tamano integer) RETURNS integer
    AS $_$
declare 
 matriz integer[][];
 i integer;
BEGIN
  for i in 1..tamano loop
   matriz[i][1] = 1;
   matriz[i][2] = 2;
  end loop;
  return 0;
END;
$_$ LANGUAGE plpgsql;


从其他语言(如 PL/Perl)来看,这是更加简单和实用的一种方式

CREATE OR REPLACE FUNCTION matriz(integer) returns integer language
'plperl' as $_$
$array = ();
my $numero = 0;
for ($numero;$numero < $_[0]; $numero++){
  $array[$numero][1] = 1;
  $array[$numero][2] = 2;
}
return 0;
$_$;


但出于各种原因,这种语言可能并不适合读者。

以下代码由 Miguel Angel Villa Gomez 提出,其建议创建一个动态函数,将数组维度作为参数传递给它。

CREATE OR REPLACE FUNCTION pepe_master (integer) RETURNS VOID AS $BODY$
DECLARE
 nombre_funcion VARCHAR:= 'pepe_child';
 notificacion VARCHAR:= $$RAISE NOTICE 'Elemento:%
(%)',elemento,array[elemento];$$;
 funcion VARCHAR:=
                 '
                 CREATE OR REPLACE FUNCTION ' || nombre_funcion || ' ()
RETURNS VOID AS $$
                 DECLARE
                  array INTEGER[' || $1 || '];
                  elemento INTEGER;
                 BEGIN
                  FOR elemento IN 1..' || $1 || ' LOOP
                    array[elemento]:=elemento;
                    ' || notificacion || '
                  END LOOP;
                 END;
                 $$ LANGUAGE PLPGSQL;
                ';
 garbage VARCHAR;
BEGIN
 EXECUTE funcion;
 EXECUTE 'SELECT ' || nombre_funcion || '()';
END;
$BODY$ LANGUAGE PLPGSQL;
SELECT pepe_master(10);


此函数由 Alvaro Herrera 提出作为解决方案。

CREATE OR REPLACE FUNCTION pepe(tamano integer) RETURNS integer AS $_$
DECLARE
   matriz integer[] = '{{1,2}}';
   i integer;
BEGIN
   for i in 1 .. tamano loop
      matriz := matriz || '{1,2}'::integer[];
      -- matriz[i] := 1;
      RAISE NOTICE 'matriz: %', matriz[i];
   end loop;
   return matriz[1];
END;
$_$ LANGUAGE plpgsql;

以下为执行时的结果

ubuntu=# select pepe(8);
NOTICE:  matriz: NULL
NOTICE:  matriz: NULL
NOTICE:  matriz: NULL
NOTICE:  matriz: NULL
NOTICE:  matriz: NULL
NOTICE:  matriz: NULL
NOTICE:  matriz: NULL
NOTICE:  matriz: NULL

pepe
--

(1 row)

是有道理的!尚未为该数组的任何位置分配任何值...


Carlos Bazán 提出了

DECLARE
       mi_arreglo TEXT[] := '{}';
BEGIN
       ...
       ...
       -- y lo alimento con
       mi_arreglo := mi_arreglo || ARRAY[[rec.dato1::text, rec.dato2::text]];
       ...

显然,必须根据数组类型(整数、文本等)关注数据类型。


Alvaro 利用 plpgsql 提出以下解决方案

CREATE OR REPLACE FUNCTION pepe(tamano integer) RETURNS integer AS $_$
declare
 matriz integer[] = '{{1,2}}';
 i integer;
begin
 for i in 1 .. tamano loop
  matriz := matriz || '{1,2}'::integer[];
  -- matriz[i] := 1;
  RAISE NOTICE 'matriz: % %', matriz[i][1], matriz[i][2];
 end loop;
 return matriz[1];
end;
$_$ LANGUAGE plpgsql;


或者,如果你想查看整个第 i 个元素,

      RAISE NOTICE 'matriz: %', matriz[i:i][1:2];

(这被称为数组的“切片”。)

如果你不知道第二个维度的长度,可以使用 array_upper() 查找

      RAISE NOTICE 'matriz: %', matriz[i:i][array_upper(matriz, 2)];

请注意,这等效于

      RAISE NOTICE 'matriz: %', matriz[i:i][1:array_upper(matriz, 2)];

参考手册

      si cada dimensión está escrita en partes (slice), por ejemplo:
      que contenga un :, entonces todas las dimensiones deben ser
      tratadas igual. Si alguna dimensión tiene un solo número (no :)
      será tratado como 1 hasta el número especificado. 
      

通常,元素从 1 开始,但你可以保存起始下标不同的奇数数组,此时你需要的是 array_lower()

      RAISE NOTICE 'matriz: %', matriz[i:i][array_lower(matriz, 2):array_upper(matriz, 2)];

如果修改用于初始化数组的字面值,可以进行测试

CREATE OR REPLACE FUNCTION pepe(tamano integer) RETURNS integer AS $_$
declare
 matriz integer[] = '[1:1][-1:0]={{1,2}}';
 i integer;
begin
 for i in 1 .. tamano loop
  matriz := matriz || '[-1:0]={1,2}'::integer[];
  -- matriz[i] := 1;
  RAISE NOTICE 'matriz: %', matriz[i:i][array_lower(matriz, 2):array_upper(matriz, 2)];
 end loop;
 raise notice 'matriz: %', matriz;
 return matriz[1];
end;
$_$ LANGUAGE plpgsql ;
alvherre=# select pepe(10);
NOTICE:  matriz: {{1,2}}
NOTICE:  matriz: {{1,2}}
NOTICE:  matriz: {{1,2}}
NOTICE:  matriz: {{1,2}}
NOTICE:  matriz: {{1,2}}
NOTICE:  matriz: {{1,2}}
NOTICE:  matriz: {{1,2}}
NOTICE:  matriz: {{1,2}}
NOTICE:  matriz: {{1,2}}
NOTICE:  matriz: {{1,2}}
NOTICE:  matriz: [1:11][-1:0]={{1,2},{1,2},{1,2},{1,2},{1,2},{1,2},{1,2},{1,2},{1,2},{1,2},{1,2}}

pepe
--
(1 fila)

感谢所有参与此主题讨论的人员。希望它有所帮助!