PostgreSQL: utiliser les noms de schéma et de table d'autres tables?

8

Considérez la situation où j'ai tous mes noms de schéma dans une table et tous mes noms de table dans une autre table.

Est-il possible de faire quelque chose comme dans ce qui suit (pseudo-code)?

SELECT value FROM (SELECT schema_name FROM schemas).(SELECT table_name FROM tables)

Ou dois-je diviser la requête en trois SELECT?

Chau
la source

Réponses:

7

Jack a montré la voie à suivre. Cependant, je pense qu'il y a place à amélioration.

Je place tout dans le schéma xpour des tests pratiques. Configuration de test:

DROP SCHEMA x CASCADE;
CREATE SCHEMA x;

-- meta tables for schema and table name    
CREATE TABLE x.schma(schma_id int, schma text);
INSERT INTO  x.schma VALUES (1, 'x');

CREATE TABLE x.tbl(tbl_id int, tbl text);
INSERT INTO  x.tbl VALUES (1, 't1'), (2, 't2');

-- dummy tables to be used in example query:
CREATE TABLE x.t1(id int);
INSERT INTO  x.t1 VALUES (1),(2);

CREATE TABLE x.t2(foo text);
INSERT INTO  x.t2 VALUES ('some text'), ('some more text');

Ancienne fonction (réponse originale):

CREATE OR REPLACE FUNCTION x.f_dynaquery_old(int, int, _col text, _type anyelement, OUT col anyelement)
  RETURNS SETOF anyelement AS
$func$
BEGIN
   RETURN QUERY EXECUTE '
   SELECT ' || quote_ident(_col) || '
   FROM   ' || (
       (SELECT schma FROM schma WHERE schma_id = $1) || '.' ||
       (SELECT tbl   FROM tbl   WHERE tbl_id   = $2))::regclass;
END
$func$  LANGUAGE plpgsql;

Version plus propre avec format()(mise à jour 2017):

CREATE OR REPLACE FUNCTION x.f_dynaquery(_schma_id int, _tbl_id int
                                       , _col text, _type anyelement)
  RETURNS TABLE(col anyelement) AS
$func$
BEGIN
   RETURN QUERY EXECUTE format(
      'SELECT %I FROM %I.%I'
    , _col
    , (SELECT schma FROM schma WHERE schma_id = _schma_id)
    , (SELECT tbl   FROM tbl   WHERE tbl_id   = _tbl_id)
   );
END
$func$  LANGUAGE plpgsql;

COMMENT ON FUNCTION x.f_dynaquery(int, int, text, anyelement)
IS 'Query any column from a dynamically assembled tablename.
$1 .. id of schema
$2 .. id of table
$3 .. name of column
$4 .. type of column (only data type matters, not the value)';

Appel:

SELECT col FROM x.f_dynaquery(1, 1, 'id', NULL::int);
  col
-----
   1
   2


SELECT col FROM x.f_dynaquery(1, 2, 'foo', NULL::text);
  col
----------------
 some text
 some more text

Points majeurs

Erwin Brandstetter
la source
@JackDouglas: J'ai construit sur votre exemple, donc vous pourriez être intéressé par celui-ci.
Erwin Brandstetter
1
J'ai trouvé cela par hasard - le '@' ne m'atteindra pas si je ne suis pas l'affiche à moins d'avoir commenté le post! Je pensais que vous aimeriez savoir :)
Jack dit d'essayer topanswers.xyz
@JackDouglas: Ah, je ne savais pas qu'un tel commentaire ne vous parvient pas. Merci pour l'info!
Erwin Brandstetter
6

Vous avez besoin de SQL dynamique - peut-être quelque chose comme ceci:

create role stack;
create schema authorization stack;
set role stack;

create or replace function f(p_schema in text, p_table in text)
                           returns setof integer language plpgsql immutable as $$
begin
  return query execute 'select value from '||p_schema||'.'||p_table;
end;$$;

create table t1(value integer);
insert into t1(value) values (1);
insert into t1(value) values (2);

create table t2(value integer);
insert into t2(value) values (1);

create table schemas(schema_name text);
insert into schemas(schema_name) values ('stack');

create table tables(table_name text);
insert into tables(table_name) values ('t1');
insert into tables(table_name) values ('t2');
insert into tables(table_name) values ('t1');
insert into tables(table_name) values ('t2');

select f(schema_name, table_name) from schemas cross join tables;
 f
---
 1
 2
 1
(3 rows)

Je suppose que chaque table est présente dans chaque schéma comme la question l'indique

Jack dit d'essayer topanswers.xyz
la source