How to find inherited tables programatically in PostgreSQL?

前端 未结 4 814
借酒劲吻你
借酒劲吻你 2021-01-05 13:41

I have a PostgreSQL 8.3 database where table inheritance is being used. I would like to get a list of all tables along with its schema name which is inherited from a base ta

相关标签:
4条回答
  • 2021-01-05 14:07

    The following statement retrieves all child tables of the table public.base_table_name:

    select bt.relname as table_name, bns.nspname as table_schema 
    from pg_class ct 
        join pg_namespace cns on ct.relnamespace = cns.oid and cns.nspname = 'public' 
        join pg_inherits i on i.inhparent = ct.oid and ct.relname = 'base_table_name' 
        join pg_class bt on i.inhrelid = bt.oid 
        join pg_namespace bns on bt.relnamespace = bns.oid
    

    It should work with 8.3 although I'm not 100% sure.

    0 讨论(0)
  • 2021-01-05 14:08

    Since you're on such an old version of PostgreSQL you'll probably have to use a PL/PgSQL function to handle inheritance depths of > 1. On modern PostgreSQL (or even 8.4) you'd use a recursive common table expression (WITH RECURSIVE).

    The pg_catalog.pg_inherits table is the key. Given:

    create table pp( );     -- The parent we'll search for
    CREATE TABLE notpp(); -- Another root for multiple inheritance
    create table cc( ) inherits (pp); -- a 1st level child of pp
    create table dd( ) inherits (cc,notpp); -- a 2nd level child of pp that also inherits aa
    create table notshown( ) inherits (notpp); -- Table that inherits only notpp
    create table ccdd () inherits (cc,dd) -- Inheritance is a graph not a tree; join node
    

    A correct result will find cc, dd, and ccdd, but not find notpp or notshown.

    A single-depth query is:

    SELECT pg_namespace.nspname, pg_class.relname 
    FROM pg_catalog.pg_inherits 
      INNER JOIN pg_catalog.pg_class ON (pg_inherits.inhrelid = pg_class.oid) 
      INNER JOIN pg_catalog.pg_namespace ON (pg_class.relnamespace = pg_namespace.oid) 
    WHERE inhparent = 'pp'::regclass;
    

    ... but this will only find cc.

    For multi-depth inheritance (ie tableC inherits tableB inherits tableA) you have to extend that via a recursive CTE or a loop in PL/PgSQL, using the children of the last loop as parents in the next.

    Update: Here's an 8.3 compatible version that should recursively find all tables that inherit directly or indirectly from a given parent. If multiple inheritance is used, it should find any table that has the target table as one of its parents at any point along the tree.

    CREATE OR REPLACE FUNCTION find_children(oid) RETURNS SETOF oid as $$
    SELECT i.inhrelid FROM pg_catalog.pg_inherits i WHERE i.inhparent = $1
    UNION
    SELECT find_children(i.inhrelid) FROM pg_catalog.pg_inherits i WHERE i.inhparent = $1;
    $$ LANGUAGE 'sql' STABLE;
    
    CREATE OR REPLACE FUNCTION find_children_of(parentoid IN regclass, schemaname OUT name, tablename OUT name) RETURNS SETOF record AS $$
    SELECT pg_namespace.nspname, pg_class.relname 
            FROM find_children($1) inh(inhrelid) 
              INNER JOIN pg_catalog.pg_class ON (inh.inhrelid = pg_class.oid) 
              INNER JOIN pg_catalog.pg_namespace ON (pg_class.relnamespace = pg_namespace.oid);
    $$ LANGUAGE 'sql' STABLE;
    

    Usage:

    regress=# SELECT * FROM find_children_of('pp'::regclass);
     schemaname | tablename 
    ------------+-----------
     public     | cc
     public     | dd
     public     | ccdd
    (3 rows)
    

    Here's the recursive CTE version, which will work if you update Pg, but won't work on your current version. It's much cleaner IMO.

    WITH RECURSIVE inh AS (
            SELECT i.inhrelid FROM pg_catalog.pg_inherits i WHERE inhparent = 'pp'::regclass
            UNION
            SELECT i.inhrelid FROM inh INNER JOIN pg_catalog.pg_inherits i ON (inh.inhrelid = i.inhparent)
    )
    SELECT pg_namespace.nspname, pg_class.relname 
        FROM inh 
          INNER JOIN pg_catalog.pg_class ON (inh.inhrelid = pg_class.oid) 
          INNER JOIN pg_catalog.pg_namespace ON (pg_class.relnamespace = pg_namespace.oid);
    
    0 讨论(0)
  • 2021-01-05 14:10

    For those who are running a version of PostgreSQL with RECURSIVE support here's a function that finds derived tables for the specified base table.

    CREATE OR REPLACE FUNCTION tables_derived_from(base_namespace name, base_table name)
    RETURNS TABLE (table_schema name, table_name name, oid oid)
    AS $BODY$
        WITH RECURSIVE inherited_id AS
        (
            SELECT i.inhrelid AS oid
            FROM pg_inherits i
            JOIN pg_class base_t ON i.inhparent = base_t.oid
            JOIN pg_namespace base_ns ON base_t.relnamespace = base_ns.oid
            WHERE base_ns.nspname = base_namespace AND base_t.relname = base_table
    
            UNION
    
            SELECT i.inhrelid AS oid
            FROM pg_inherits i
            JOIN inherited_id b ON i.inhparent = b.oid
        )
        SELECT child_ns.nspname as table_schema, child_t.relname as table_name, child_t.oid
        FROM inherited_id i
        JOIN pg_class child_t ON i.oid = child_t.oid 
        JOIN pg_namespace child_ns ON child_t.relnamespace = child_ns.oid
        ORDER BY 1, 2, 3;
    $BODY$ LANGUAGE sql STABLE;
    
    0 讨论(0)
  • 2021-01-05 14:17

    It's important to note that one table can inherit multiple tables, and none of the solutions listed really expose that; they just walk down the tree of a single parent. Consider:

    CREATE TABLE a();
    CREATE TABLE b();
    CREATE TABLE ab_() INHERITS (a,b);
    CREATE TABLE ba_() INHERITS (b,a);
    CREATE TABLE ab__() INHERITS (ab_);
    CREATE TABLE ba__() INHERITS (ba_);
    CREATE TABLE ab_ba_() INHERITS (ab_, ba_);
    CREATE TABLE ba_ab_() INHERITS (ba_, ab_);
    
    WITH RECURSIVE inh AS (
            SELECT i.inhparent::regclass, i.inhrelid::regclass, i.inhseqno FROM pg_catalog.pg_inherits i WHERE inhparent = 'a'::regclass
            UNION
            SELECT i.inhparent::regclass, i.inhrelid::regclass, i.inhseqno FROM inh INNER JOIN pg_catalog.pg_inherits i ON (inh.inhrelid = i.inhparent)
    ) SELECT * FROM inh;
     inhparent | inhrelid | inhseqno 
    -----------+----------+----------
     a         | ab_      |        1
     a         | ba_      |        2
     ab_       | ab__     |        1
     ba_       | ba__     |        1
     ab_       | ab_ba_   |        1
     ba_       | ab_ba_   |        2
     ba_       | ba_ab_   |        1
     ab_       | ba_ab_   |        2
    (8 rows)
    

    Notice that b doesn't show up at all which is incorrect, as both ab_ and ba_ inherit b.

    I suspect the "best" way to handle this would be a column that's text[] and contains (array[inhparent::regclass])::text for each table. That would give you something like

    inhrelid   path
    ab_        {"{a,b}"}
    ba_        {"{b,a}"}
    ab_ba_     {"{a,b}","{b,a}"}
    

    While obviously not ideal, that would at least expose the complete inheritance path and allow you to access it with enough gymnastics. Unfortunately, constructing that is not at all easy.

    A somewhat simpler alternative is not to include the full inheritance path at each level, only each tables direct parents. That would give you this:

    inhrelid    parents
    ab_         {a,b}
    ba_         {b,a}
    ab_ba_      {ab_,ba_}
    
    0 讨论(0)
提交回复
热议问题