PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

SELECT colonnes dynamiques sans fonctions dans PostgreSQL

Ce que vous essayez de faire n'est guère possible dans son intégralité.

Créer du SQL dynamique

Tout d'abord, voici ce que vous pouvez faire :une fonction plpgsql qui crée le SQL pour une telle requête :

CREATE OR REPLACE FUNCTION f_union_common_col_sql(text, text)
 RETURNS text
AS $function$
DECLARE 
  _cols text;
BEGIN

_cols := string_agg(attname, ', ')
FROM (
    SELECT a.attname
    FROM   pg_attribute a
    WHERE  a.attrelid = $1::regclass::oid
    AND    a.attnum >= 1
    INTERSECT
    SELECT a.attname
    FROM   pg_attribute a
    WHERE  a.attrelid = $2::regclass::oid
    AND    a.attnum >= 1
    ) x;

RETURN 'SELECT ' || _cols || '
FROM   ' || quote_ident($1) || '
UNION
SELECT ' || _cols || '
FROM   ' || quote_ident($1);

END;
$function$  LANGUAGE plpgsql;

COMMENT ON FUNCTION f_union_common_col_sql(text, text) IS 'Create SQL to query all visible columns that two tables have in common.
# Without duplicates. Use UNION ALL if you want to include duplicates.
# Depends on visibility dicatated by search_path
$1 .. table1: optionally schema-qualified, case sensitive!
$2 .. table2: optionally schema-qualified, case sensitive!';

Appel :

SELECT f_union_common_col_sql('myschema1.tbl1', 'myschema2.tbl2');

Vous donne la requête complète. Exécutez-le dans un second appel.

Vous pouvez trouver la plupart de tout ce que j'ai utilisé ici dans le manuel sur les fonctions plpgsql .
La fonction d'agrégat string_agg() a été introduit avec PostgreSQL 9.0. Dans les anciennes versions, vous feriez :array_to_string(array_agg(attname), ', ') .

Exécuter SQL dynamique ?

Ensuite, voici ce que vous à peine pouvez faire :

CREATE OR REPLACE FUNCTION f_union_common_col(text, text)
  RETURNS SETOF record AS
$BODY$
DECLARE 
  _cols text;
BEGIN

_cols := string_agg(attname, ', ')
FROM (
    SELECT a.attname
    FROM   pg_attribute a
    WHERE  a.attrelid = $1::regclass::oid
    AND    a.attnum >= 1
    INTERSECT
    SELECT a.attname
    FROM   pg_attribute a
    WHERE  a.attrelid = $2::regclass::oid
    AND    a.attnum >= 1
    ) x;

RETURN QUERY EXECUTE '
SELECT ' || _cols || '
FROM quote_ident($1)
UNION
SELECT ' || _cols || '
FROM quote_ident($2)';

END;
$BODY$
  LANGUAGE plpgsql VOLATILE;

COMMENT ON FUNCTION f_union_common_col(text, text) IS 'Query all visible columns that two tables have in common.
# Without duplicates. Use UNION ALL if you want to include duplicates.
# Depends on visibility dicatated by search_path
# !BUT! you need to specify a column definition list for every call. So, hardly useful.
$1 .. table1 (optionally schema-qualified)
$2 .. table1 (optionally schema-qualified)';

Un appel de fonction nécessite que vous spécifiiez la liste des colonnes cibles. donc c'est à peine utile :

SELECT * from f_union_common_col('myschema1.tbl1', 'myschema2.tbl2')

ERROR:  a column definition list is required for functions returning "record"

Il n'y a pas de moyen facile de contourner cela. Vous auriez à créer dynamiquement une fonction ou au moins un type complexe. C'est là que je m'arrête.