在一个MySQL脚本中,你可以写:

CREATE TABLE IF NOT EXISTS foo ...;

... 其他的…

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

在PostgreSQL中如何做到这一点?


当前回答

没有创建表如果不存在…但是你可以为此写一个简单的过程,比如:

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

其他回答

这个解决方案有点类似于Erwin Brandstetter的答案,但只使用sql语言。

并非所有PostgreSQL安装都默认使用plpqsql语言,这意味着在创建函数之前必须调用CREATE language plpgsql,然后必须再次删除该语言,以使数据库处于与之前相同的状态(但仅当数据库一开始就没有plpgsql语言时)。看到复杂性是如何增长的了吗?

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

这个解决方案的灵感来自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();

没有创建表如果不存在…但是你可以为此写一个简单的过程,比如:

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

这个特性已经在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中的schemaname和tablename列区分大小写。如果在CREATE TABLE语句中使用双引号标识符,则需要使用完全相同的拼写。如果没有,则需要使用小写字符串。看到的:

PostgreSQL列名区分大小写吗?

Pg_tables只包含实际的表。标识符仍然可能被相关对象占用。看到的:

如何检查一个表是否存在于给定的模式中

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

试试这个:

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

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

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

如果要从查询参数中提取表名,可以进一步简化为只取一个参数。我还省略了模式。