AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 205684
Accepted
guettli
guettli
Asked: 2018-05-04 02:23:48 +0800 CST2018-05-04 02:23:48 +0800 CST 2018-05-04 02:23:48 +0800 CST

Selecione linhas órfãs (de forma genérica)

  • 772

Eu tenho uma tabela my_tablesque é referenciada como chave estrangeira em várias tabelas.

Gostaria de selecionar todas as linhas my_tableque não são referenciadas em outra tabela.

AFAIK, deve ser possível fazer isso de maneira genérica (com alguma mágica de introspecção).

postgresql foreign-key
  • 2 2 respostas
  • 1821 Views

2 respostas

  • Voted
  1. Best Answer
    Erwin Brandstetter
    2018-05-20T20:04:01+08:002018-05-20T20:04:01+08:00

    Citando o manual:

    Uma chave estrangeira deve fazer referência a colunas que são uma chave primária ou formam uma restrição exclusiva.

    Então isso não é necessariamente limitado ao PK. Mas se começarmos com pg_constraint, obteremos todas as restrições FK apontando para a tabela de destino automaticamente. Não há necessidade de fornecer colunas de chave - exceto se você quiser limitar a determinados FKs.

    Usando o tipo de identificadores de objeto regclasse aliases de tabela, podemos manter a função curta e o resultado seguro e inequívoco:

    Consulta básica

    SELECT format(E'SELECT * FROM %s t\nWHERE  NOT EXISTS (SELECT FROM %s'
                , c.confrelid::regclass
                , string_agg(format('%s WHERE %s = %s)', c.conrelid::regclass, src.cols, tgt.cols)
                           , E'\nAND    NOT EXISTS (SELECT FROM '))
    FROM   pg_constraint c
         , cardinality(c.conkey) AS col_ct
         , LATERAL (
       SELECT concat(CASE WHEN col_ct > 1 THEN '(' END
                   , string_agg(quote_ident(attname), ', ' ORDER BY fld.ord) -- original order
                   , CASE WHEN col_ct > 1 THEN ')' END) AS cols
       FROM   unnest(c.conkey) WITH ORDINALITY fld(attnum, ord)             -- possibly n cols
       JOIN   pg_catalog.pg_attribute a ON (a.attrelid, a.attnum) = (c.conrelid, fld.attnum)
       ) src
         , LATERAL (
       SELECT concat(CASE WHEN col_ct > 1 THEN '(' END     -- parentheses for multiple columns
                   , string_agg('t.' || quote_ident(attname), ', t.' ORDER BY fld.ord)
                   , CASE WHEN col_ct > 1 THEN ')' END) AS cols
       FROM   unnest(c.confkey) WITH ORDINALITY fld(attnum, ord)
       JOIN   pg_catalog.pg_attribute a ON (a.attrelid, a.attnum) = (c.confrelid, fld.attnum)
       ) tgt
    WHERE  c.confrelid = 'my_table'::regclass -- target table name, optionally schema-qualified
    AND    c.contype = 'f'  -- FK constraints
    GROUP  BY c.confrelid;
    

    Produz uma consulta do formulário:

    SELECT * FROM my_table t
    WHERE  NOT EXISTS (SELECT FROM schema1.tbl1 WHERE col1 = t.id)
    AND    NOT EXISTS (SELECT FROM "tB-l2" WHERE ("COL2", col3) = (t.col4, t.col5));
    

    Que retorna todas as linhas que não são referenciadas atualmente por nenhuma restrição FK.

    Se cardinality(c.conkey) > 1então é seguro assumir também cardinality(c.confkey) > 1. Portanto, conte apenas uma vez para decidir se deseja adicionar parênteses.

    Automação completa

    Para fazer isso funcionar para qualquer tabela de entrada dinamicamente, crie uma função polimórfica pegando um valor de linha da tabela:

    CREATE OR REPLACE FUNCTION f_orphans(_tbl anyelement)
      RETURNS SETOF anyelement AS
    $func$
    BEGIN
    
    RETURN QUERY EXECUTE (  -- exactly the query from above
    SELECT format(E'SELECT * FROM %s t\nWHERE  NOT EXISTS (SELECT FROM %s'
                , c.confrelid::regclass
                , string_agg(format('%s WHERE %s = %s)', c.conrelid::regclass, src.cols, tgt.cols)
                           , E'\nAND    NOT EXISTS (SELECT FROM '))
    FROM   pg_constraint c
         , cardinality(c.conkey) AS col_ct
         , LATERAL (
       SELECT concat(CASE WHEN col_ct > 1 THEN '(' END
                   , string_agg(quote_ident(attname), ', ' ORDER BY fld.ord)
                   , CASE WHEN col_ct > 1 THEN ')' END) AS cols
       FROM   unnest(c.conkey) WITH ORDINALITY fld(attnum, ord)
       JOIN   pg_catalog.pg_attribute a ON (a.attrelid, a.attnum) = (c.conrelid, fld.attnum)
       ) src
         , LATERAL (
       SELECT concat(CASE WHEN col_ct > 1 THEN '(' END
                   , string_agg('t.' || quote_ident(attname), ', t.' ORDER BY fld.ord)
                   , CASE WHEN col_ct > 1 THEN ')' END) AS cols
       FROM   unnest(c.confkey) WITH ORDINALITY fld(attnum, ord)
       JOIN   pg_catalog.pg_attribute a ON (a.attrelid, a.attnum) = (c.confrelid, fld.attnum)
       ) tgt
    WHERE  c.confrelid = pg_typeof(_tbl)::text::regclass  -- input goes here!
    AND    c.contype = 'f'
    GROUP  BY c.confrelid
    );
    
    END
    $func$  LANGUAGE plpgsql;
    

    Ligue (importante)!

    SELECT * FROM f_orphans(NULL::my_table);
    

    Ou:

    SELECT * FROM f_orphans(NULL::myschema.my_table);
    

    Relacionado:

    • PostgreSQL: Passar tabela como argumento na função
    • Recuperando todos os PK e FK
    • A ordem SQL por consulta resulta de qualquer maneira arbitrária (mas reproduzível)
    • Como preservar a ordem original dos elementos em uma matriz não aninhada?
    • 7
  2. Sahap Asci
    2018-05-15T07:41:56+08:002018-05-15T07:41:56+08:00

    Aqui está uma consulta de exemplo que permite criar a instrução sql:

    WITH 
      fkey_fields AS (
        SELECT DISTINCT
            nt.nspname AS table_schema,
            t.relname AS table_name,
            unnest(c.conkey) AS field_index,
            nft.nspname AS foreign_table_schema,
            ft.relname AS foreign_table_name,
            unnest(c.confkey) AS foreign_field_index
          FROM pg_constraint c
          JOIN pg_class t ON t.oid = c.conrelid
          JOIN pg_namespace nt ON nt.oid = t.relnamespace
          JOIN pg_class ft ON ft.oid = c.confrelid
          JOIN pg_namespace nft ON nft.oid = ft.relnamespace
          WHERE
            c.contype = 'f'
      ),
      table_fields AS (
        SELECT
            rn.nspname AS table_schema,
            c.relname AS table_name,
            a.attname AS field_name,
            a.attnum AS field_index
          FROM pg_attribute a
          JOIN pg_class c ON a.attrelid = c.oid
          JOIN pg_namespace rn ON c.relnamespace = rn.oid
          WHERE 
            a.attnum > 0 AND
            a.attisdropped <> 't' 
      )
    SELECT
      concat(
        format(
          'SELECT * FROM %I.%I',
          fkf.foreign_table_schema,
          fkf.foreign_table_name
        ),
    
        E'\nWHERE\n' ||
        string_agg(
          format('NOT EXISTS (SELECT 1 FROM %I.%I where %I.%I.%I = %I.%I.%I)',
            fkf.table_schema,
            fkf.table_name,
            fkf.table_schema,
            fkf.table_name,
            tf.field_name,    
            fkf.foreign_table_schema,
            fkf.foreign_table_name,
            foreign_tf.field_name  
          ),
          E' AND\n'
        )
      )
      FROM fkey_fields fkf
      JOIN table_fields tf ON
        tf.table_schema = fkf.table_schema AND
        tf.table_name = fkf.table_name AND 
        tf.field_index = fkf.field_index
      JOIN table_fields foreign_tf ON
        foreign_tf.table_schema = fkf.foreign_table_schema AND
        foreign_tf.table_name = fkf.foreign_table_name AND 
        foreign_tf.field_index = fkf.foreign_field_index
      WHERE
        fkf.foreign_table_schema = 'public' AND 
        fkf.foreign_table_name = 'my_table'
      GROUP BY
        fkf.foreign_table_schema,
        fkf.foreign_table_name;
    

    O exemplo de saída está abaixo;

    SELECT * FROM public.my_table
    WHERE
    NOT EXISTS (SELECT 1 FROM public.table2 where public.table2.my_table_id = public.my_table.id) AND 
    NOT EXISTS (SELECT 1 FROM public.table3 where public.table3.my_table_id = public.my_table.id)
    

    o link do violino está aqui

    • 4

relate perguntas

  • Posso ativar o PITR depois que o banco de dados foi usado

  • Práticas recomendadas para executar a replicação atrasada do deslocamento de tempo

  • Os procedimentos armazenados impedem a injeção de SQL?

  • Sequências Biológicas do UniProt no PostgreSQL

  • Qual é a diferença entre a replicação do PostgreSQL 9.0 e o Slony-I?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve