AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 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

选择孤立的行(以通用方式)

  • 772

我有一个表my_tables,它在几个表中被引用为外键。

我想选择my_table其他表中未引用的所有行。

AFAIK,应该可以用一种通用的方式来做到这一点(有一些内省的魔法)。

postgresql foreign-key
  • 2 2 个回答
  • 1821 Views

2 个回答

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

    引用手册:

    外键必须引用作为主键或形成唯一约束的列。

    所以这不一定限于PK。但是如果我们从 开始pg_constraint,我们会自动获得所有指向目标表的 FK 约束。无需提供任何键列 - 除非您想限制某些 FK。

    使用对象标识符类型regclass和表别名,我们可以保持函数简短,结果安全且明确:

    基本查询

    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;
    

    产生以下形式的查询:

    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));
    

    它返回当前未被任何FK 约束引用的所有行。

    如果cardinality(c.conkey) > 1那么它也是安全的假设cardinality(c.confkey) > 1。所以只计算一次来决定是否加括号。

    全自动化

    要动态地对任何输入表进行此操作,请创建一个采用表的行值的多态函数:

    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;
    

    打电话(重要)!

    SELECT * FROM f_orphans(NULL::my_table);
    

    或者:

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

    有关的:

    • PostgreSQL:将表作为参数传递给函数
    • 检索所有 PK 和 FK
    • SQL order by query 以任意(但可重现)的方式产生结果
    • 如何保留未嵌套数组中元素的原始顺序?
    • 7
  2. Sahap Asci
    2018-05-15T07:41:56+08:002018-05-15T07:41:56+08:00

    这是一个示例查询,可让您创建 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;
    

    输出示例如下;

    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)
    

    小提琴链接在这里

    • 4

相关问题

  • 我可以在使用数据库后激活 PITR 吗?

  • 运行时间偏移延迟复制的最佳实践

  • 存储过程可以防止 SQL 注入吗?

  • PostgreSQL 中 UniProt 的生物序列

  • PostgreSQL 9.0 Replication 和 Slony-I 有什么区别?

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目

    • 12 个回答
  • Marko Smith

    如何让sqlplus的输出出现在一行中?

    • 3 个回答
  • Marko Smith

    选择具有最大日期或最晚日期的日期

    • 3 个回答
  • Marko Smith

    如何列出 PostgreSQL 中的所有模式?

    • 4 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

    如何在不修改我自己的 tnsnames.ora 的情况下使用 sqlplus 连接到位于另一台主机上的 Oracle 数据库

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Martin Hope
    Jin 连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane 如何列出 PostgreSQL 中的所有模式? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh 为什么事务日志不断增长或空间不足? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland 列出指定表的所有列 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney MySQL 能否合理地对数十亿行执行查询? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx 如何监控大型 .sql 文件的导入进度? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST

热门标签

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

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve