如果表不存在,如何选择不出错

Posted

技术标签:

【中文标题】如果表不存在,如何选择不出错【英文标题】:How to SELECT without error if table does not exist 【发布时间】:2020-01-19 01:17:23 【问题描述】:

我想从给定的表中运行SELECT,如果该表不存在,它应该不会失败。假设为简单起见,该表仅包含一列和一行,而我在那个标量之后。如果表不存在我想找回0

我知道我可以这样做:

query("select count(1) from information_schema.tables where table_name = 'versions'")
if result > 0
  return query("select version from versions limit 1")
else
  return 0

或者我可以使用存储过程 / plpgsql 函数来做同样的事情。

但是有没有办法在单个临时查询中做同样的事情?

(我正在做两个查询。我没有充分的理由来更改它。我只是好奇是否/如何做到这一点。)

【问题讨论】:

规划器将在规划时查找查询中的所有关系。您可以使用函数“延迟”计划时间,但在简单的查询中这是一个问题。 【参考方案1】:

您希望使用纯 SQL 解决方案单次往返服务器。 这些年来,有许多相关的要求。 原则上不可能

要么需要动态 SQL - 这需要使用 PL/pgSQL 或其他 PL 的函数。 或者您需要两次往返服务器。先检查存在,再查询。

您也不能将其嵌套在一个普通的 SQL 函数中,该函数会在执行前计划主体中的每个语句,并在尝试解析不存在的表名时失败。它甚至不会在函数创建时通过表面测试。见:

Why can PL/pgSQL functions have side effect, while SQL functions can't? Syntax error in dynamic SQL in pl/pgsql function

为您的简单案例提供两种简单的解决方案(在许多可能的方式中):

CREATE OR REPLACE FUNCTION f_select_version_if_exists1() 
  RETURNS int LANGUAGE plpgsql PARALLEL SAFE AS 
$func$
BEGIN 
   IF EXISTS (
      SELECT FROM pg_catalog.pg_tables
      WHERE  tablename  = 'versions'
      AND    schemaname = 'public'  -- see below!
      ) THEN
      RETURN (SELECT version FROM versions LIMIT 1);
   ELSE
      RETURN 0;
   END IF;
END 
$func$;

或者:

CREATE OR REPLACE FUNCTION f_select_version_if_exists2(INOUT _version int = 0) AS 
$func$
BEGIN 
   IF EXISTS (
      SELECT FROM pg_catalog.pg_tables
      WHERE  tablename  = 'versions'
      AND    schemaname = 'public'  -- see below!
      ) THEN
      SELECT INTO _version version
      FROM   versions LIMIT 1;
   END IF;
END 
$func$  LANGUAGE plpgsql PARALLEL SAFE;

我强烈建议同时确定架构名称。 Postgres 中的表名不是唯一的。见:

How to check if a table exists in a given schema

另外,我创建了函数PARALLEL SAFE(仅当它可能嵌套在 big 查询中时才重要)。如果依赖 search_path,这将是错误,因为它通常包含临时对象的架构,这使得它成为 PARALLEL UNSAFE

第二个是创造性地使用INOUT 参数。相关:

Can I make a plpgsql function return an integer without using a variable?

【讨论】:

【参考方案2】:

我相信您正在寻找一个简短的 PLpg/SQL 函数。您可以检查表是否存在于information_schema.tables 中,就像您一直在做的那样,并将此结果存储在一个变量中。之后,您可以在 IF 语句中评估结果集并返回您想要的任何内容,例如

CREATE OR REPLACE FUNCTION f(p_table_name TEXT) 
  RETURNS INT 
LANGUAGE plpgsql AS 
$BODY$
DECLARE 
  res INT;
BEGIN 
  SELECT count(*) 
     into res
  FROM information_schema.tables 
  WHERE table_name = p_table_name;

  IF res>0 THEN RETURN res;
  ELSE RETURN 0;
  END IF;
END 
$BODY$;

不存在的表

SELECT * FROM f('versions');
 f 
---
 0
(1 Zeile)

现有表

SELECT * FROM f('t');
 f 
---
 1
(1 Zeile)

【讨论】:

谢谢。虽然这包含在问题中,但我特别关注非 plsql 解决方案。

以上是关于如果表不存在,如何选择不出错的主要内容,如果未能解决你的问题,请参考以下文章

如果表不存在,如何使用 Derby Db 创建表

MySQL 仅在表不存在时插入数据

如果表不存在,如何运行包含针对另一个 sql 实例上的表的查询的 If-Then-Else 语句?

使用 sqitch 中的 mysql 查询验证表不存在

如何把数据库里的数据导出到EXcl表中

PHP - MySQL - 表不存在