ChatGPT解决这个技术问题 Extra ChatGPT

PostgreSQL 如果不存在则创建表

在 MySQL 脚本中,您可以编写:

CREATE TABLE IF NOT EXISTS foo ...;

... 其他的东西 ...

然后您可以多次运行脚本而无需重新创建表。

你如何在 PostgreSQL 中做到这一点?


E
Erwin Brandstetter

此功能已implemented in Postgres 9.1

CREATE TABLE IF NOT EXISTS myschema.mytable (i integer);

对于旧版本,这是一个解决它的函数:

CREATE OR REPLACE FUNCTION create_mytable()
  RETURNS void
  LANGUAGE plpgsql AS
$func$
BEGIN
   IF EXISTS (SELECT FROM pg_catalog.pg_tables 
              WHERE  schemaname = 'myschema'
              AND    tablename  = 'mytable') THEN
      RAISE NOTICE 'Table myschema.mytable already exists.';
   ELSE
      CREATE TABLE myschema.mytable (i integer);
   END IF;
END
$func$;

称呼:

SELECT create_mytable();        -- call as many times as you want. 

笔记

pg_tables 中的列 schemanametablename 区分大小写。如果您在 CREATE TABLE 语句中使用双引号标识符,则需要使用完全相同的拼写。如果不这样做,则需要使用小写字符串。看:

PostgreSQL 列名是否区分大小写?

pg_tables 仅包含实际的表格。该标识符可能仍被相关对象占用。看:

如何检查给定模式中是否存在表

如果角色执行此函数没有创建表的必要权限,您可能希望将SECURITY DEFINER用于该函数并使其拥有由另一个角色拥有必要的特权。这个版本足够安全。


我被迫使用现有的 postgres 8.4 数据库。这个hack可以解决问题,谢谢!
@Boundless:我看到您的编辑被拒绝为“太次要”。我应用它,因为它不会伤害。但是,您应该只执行一次 CREATE FUNCTION。您可能想多次调用 SELECT create_mytable();
Brandstetter:我同意你的看法。我遇到的问题是我不知道该函数是否已创建(就像该表可能存在也可能不存在一样)。所以我想确保在调用它之前创建该函数。
顺便说一句,还支持相反的 DROP TABLE IF EXISTS <tablename>
p
pmoubed

尝试这个:

CREATE TABLE IF NOT EXISTS app_user (
  username varchar(45) NOT NULL,
  password varchar(450) NOT NULL,
  enabled integer NOT NULL DEFAULT '1',
  PRIMARY KEY (username)
)

这实际上是更清洁的解决方案。应该赞成。
事实上,我害怕有多少涉及“功能”的解决方案。
@SDReyes 这些其他解决方案是在 Postgres 9.1 之前发布的,其中包括 IF NOT EXISTS 选项。
不确定这个答案是如何做出贡献的,因为@erwin-brandstetter 答案本身是完整的。
@comiventor 正确,这个确实表明参数的使用是。主要的答案我没有发现,直到我看到这个。这确实有点帮助。
W
Wolkenarchitekt

我从现有答案中创建了一个通用解决方案,可用于任何表格:

CREATE OR REPLACE FUNCTION create_if_not_exists (table_name text, create_stmt text)
RETURNS text AS
$_$
BEGIN

IF EXISTS (
    SELECT *
    FROM   pg_catalog.pg_tables 
    WHERE    tablename  = table_name
    ) THEN
   RETURN 'TABLE ' || '''' || table_name || '''' || ' ALREADY EXISTS';
ELSE
   EXECUTE create_stmt;
   RETURN 'CREATED';
END IF;

END;
$_$ LANGUAGE plpgsql;

用法:

select create_if_not_exists('my_table', 'CREATE TABLE my_table (id integer NOT NULL);');

如果从查询参数中提取表名,则可以进一步简化为仅采用一个参数。我也省略了模式。


z
zpon

此解决方案与 Erwin Brandstetter 的答案有些相似,但仅使用 sql 语言。

并非所有 PostgreSQL 安装都默认使用 plpqsql 语言,这意味着您可能必须在创建函数之前调用 CREATE LANGUAGE plpgsql,然后必须再次删除该语言,以使数据库保持与以前相同的状态(但仅如果数据库没有以 plpgsql 语言开头)。看看复杂性如何增长?

如果您在本地运行脚本,添加 plpgsql 可能不会有问题,但是,如果脚本用于在客户处设置模式,则可能不希望在客户数据库中留下这样的更改。

此解决方案的灵感来自 a post by Andreas Scherbaum

-- Function which creates table
CREATE OR REPLACE FUNCTION create_table () RETURNS TEXT AS $$
    CREATE TABLE table_name (
       i int
    );
    SELECT 'extended_recycle_bin created'::TEXT;
    $$
LANGUAGE 'sql';

-- Test if table exists, and if not create it
SELECT CASE WHEN (SELECT true::BOOLEAN
    FROM   pg_catalog.pg_tables 
    WHERE  schemaname = 'public'
    AND    tablename  = 'table_name'
  ) THEN (SELECT 'success'::TEXT)
  ELSE (SELECT create_table())
END;

-- Drop function
DROP FUNCTION create_table();

即使 plpgsql 可用,您的解决方案也很棒。它很容易扩展,可以在时间点不存在的对象上创建视图和函数。例如,来自外部服务器的表视图。你救了我的一天!谢谢!
Z
Zach Bloomquist

如果不存在则没有 CREATE TABLE ... 但您可以为此编写一个简单的过程,例如:

CREATE OR REPLACE FUNCTION execute(TEXT) RETURNS VOID AS $$
BEGIN
  EXECUTE $1;
END; $$ LANGUAGE plpgsql;


SELECT 
  execute($$
      CREATE TABLE sch.foo 
      (
        i integer
      )
  $$) 
WHERE 
  NOT exists 
  (
    SELECT * 
    FROM information_schema.tables 
    WHERE table_name = 'foo'
      AND table_schema = 'sch'
  );

触发器内部并不总是有效:gist.github.com/igilfanov/4df5e90d8a88d653132746a223639f45 错误:关系“foo”已经存在
i
igilfanov

如果不存在则没有 CREATE TABLE ... 但您可以为此编写一个简单的过程,例如:

CREATE OR REPLACE FUNCTION prc_create_sch_foo_table() RETURNS VOID AS $$
BEGIN

EXECUTE 'CREATE TABLE /* IF NOT EXISTS add for PostgreSQL 9.1+ */ sch.foo (
                    id serial NOT NULL, 
                    demo_column varchar NOT NULL, 
                    demo_column2 varchar NOT NULL,
                    CONSTRAINT pk_sch_foo PRIMARY KEY (id));
                   CREATE INDEX /* IF NOT EXISTS add for PostgreSQL 9.5+ */ idx_sch_foo_demo_column ON sch.foo(demo_column);
                   CREATE INDEX /* IF NOT EXISTS add for PostgreSQL 9.5+ */ idx_sch_foo_demo_column2 ON sch.foo(demo_column2);'
               WHERE NOT EXISTS(SELECT * FROM information_schema.tables 
                        WHERE table_schema = 'sch' 
                            AND table_name = 'foo');

         EXCEPTION WHEN null_value_not_allowed THEN
           WHEN duplicate_table THEN
           WHEN others THEN RAISE EXCEPTION '% %', SQLSTATE, SQLERRM;

END; $$ LANGUAGE plpgsql;