PostgreSQL: pasa la tabla como argumento en la función

11

Estoy descubriendo TYPEen PostgreSQL. Tengo un TABLE TYPEque alguna tabla debe respetar (interfaz). Por ejemplo:

CREATE TYPE dataset AS(
    ChannelId INTEGER
   ,GranulityIdIn INTEGER
   ,GranulityId INTEGER
   ,TimeValue TIMESTAMP
   ,FloatValue FLOAT
   ,Status BIGINT
   ,QualityCodeId INTEGER
   ,DataArray FLOAT[]
   ,DataCount BIGINT
   ,Performance FLOAT
   ,StepCount INTEGER
   ,TableRegClass regclass
   ,Tags TEXT[]
   ,WeightedMean FLOAT
   ,MeanData FLOAT
   ,StdData FLOAT
   ,MinData FLOAT
   ,MaxData FLOAT
   ,MedianData FLOAT
   ,Percentiles FLOAT[]
);

Puedo crear una tabla usando esta plantilla con:

CREATE TABLE test OF dataset;

He visto muchas opciones en la API , pero estoy un poco perdido. Me gustaría saber si es posible asignar este tipo a INPUT/OUTPUTparámetros de función .

Digamos que tengo una FUNCTIONllamada processque recibe una muestra de registros de un conjunto de datos TABLE source, los procesa y luego devuelve un TABLE sinkcon el mismo TYPE.

Es decir, me gustaría saber si es posible crear uno TYPEque se comporte así:

CREATE FUNCTION process(
    input dataset
) RETURNS dataset
AS ...

Y eso se puede llamar así:

SELECT
    *
FROM
    source, process(input := source) AS sink;

Me pregunto si es posible con PostgreSQL, y pregunto cómo hacerlo. ¿Alguien de ustedes sabe?


Aquí hay un MWE de lo que estoy tratando de hacer:

DROP TABLE IF EXISTS source;
DROP FUNCTION IF EXISTS process(dataset);
DROP TYPE dataset;

CREATE TYPE dataset AS (
    id INTEGER
   ,t  TIMESTAMP
   ,x  FLOAT
);


CREATE TABLE source OF dataset;
ALTER TABLE source ADD PRIMARY KEY(Id);
INSERT INTO source VALUES
    (1, '2016-01-01 00:00:00', 10.0)
   ,(2, '2016-01-01 00:30:00', 11.0)
   ,(3, '2016-01-01 01:00:00', 12.0)
   ,(4, '2016-01-01 01:30:00',  9.0)
   ;

CREATE OR REPLACE FUNCTION process(
    _source dataset
)
RETURNS SETOF dataset
AS
$BODY$
SELECT * FROM source;
$BODY$
LANGUAGE SQL;

SELECT * FROM process(source);

Pero no tiene éxito, es como la fuente se percibe como una columna en lugar de una SETOF RECORDScon el tipo de conjunto de datos.

jlandercy
fuente

Respuestas:

13

Su parámetro _sourceen el MWE agregado no está referenciado en ninguna parte. El identificador sourceen el cuerpo de la función no tiene guión bajo y se interpreta como nombre de tabla constante de forma independiente.

Más importante aún, no funcionaría así de todos modos. SQL solo permite parametrizar valores en sentencias DML. Detalles en esta respuesta relacionada:

Solución

Todavía puede hacerlo funcionar utilizando SQL dinámico con EXECUTEuna función plpgsql. Detalles:

O intente esta búsqueda de preguntas y respuestas relacionadas

CREATE TYPE dataset AS (id integer, t timestamp, x float);
CREATE TABLE source OF dataset (PRIMARY KEY(Id));  -- add constraints in same command

INSERT INTO source VALUES
    (1, '2016-01-01 00:00:00', 10.0)
   ,(2, '2016-01-01 00:30:00', 11.0);

CREATE OR REPLACE FUNCTION process(_tbl regclass)
  RETURNS SETOF dataset AS
$func$
BEGIN
RETURN QUERY EXECUTE 'SELECT * FROM ' || _tbl;
END
$func$  LANGUAGE plpgsql;

SELECT * FROM process('source');  -- table name as string literal 

Incluso puede hacer que esto funcione para cualquier tabla dada:

CREATE OR REPLACE FUNCTION process2(_tbl anyelement)
  RETURNS SETOF anyelement AS
$func$
BEGIN
RETURN QUERY EXECUTE 'SELECT * FROM ' || pg_typeof(_tbl);
END
$func$  LANGUAGE plpgsql;

SELECT * FROM process2(NULL::source);  -- note the call syntax!!

Explicación detallada:

Erwin Brandstetter
fuente
Gracias por responder. Lo comprobaré en unas pocas horas. Solo para saber antes de probar, es su solución aceptar recibir filas de como SELECT. Me refiero SELECT * FROM process((SELECT * FROM source WHERE cond)).
jlandercy
@j: No, pasas un nombre de tabla . No hay forma de pasar una tabla en sí (sin variable de tabla). Hay varias formas de evitarlo. Relacionado: stackoverflow.com/a/27853965/939860 o stackoverflow.com/a/31167928/939860 . Para trabajar en el resultado de una consulta, usaría un cursor o una tabla temporal ...
Erwin Brandstetter
0

Esto hará lo que quiera sin necesidad de ningún SQL dinámico :

drop table if exists source cascade;
drop function if exists process(dataset) cascade;
drop type if exists dataset cascade;

create type dataset as (
    id integer
   ,t  timestamp
   ,x  float
);

create table source of dataset;
alter table source add primary key(id);
insert into source values
   (1, '2016-01-01 00:00:00', 10.0)
 , (2, '2016-01-01 00:30:00', 11.0)
;

create or replace function process(
    x_source dataset[]
)
returns setof dataset
as
$body$
select * from unnest(x_source);
$body$
language sql;

select *
from
  process(
    array(
      select
        row(id, t, x)::dataset
      from source
    )
  );

Por lo que puedo decir (después de buscar en Google extensivamente, porque tuve el mismo problema) no puedes pasar una tabla directamente a una función.

Sin embargo, como se muestra, puede transformar una tabla en una matriz []de un tipo personalizado que consta de varios tipos básicos (similar a una definición de tabla).

Luego puede pasar esa matriz y deshacerla de nuevo en una tabla una vez que esté en la función.

Sam Fed
fuente