36

I'm using PostgreSQL and am an SQL beginner. I'm trying to create a table from a query, and if I run:

CREATE TABLE table_name AS
   (....query...)

it works just fine. But then if I add 'if not exists' and run:

CREATE TABLE IF NOT EXISTS table_name AS
   (....query...)

using exactly the same query, I get:

ERROR: syntax error at or near "as"

Is there any way to do this?

Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
user3591836
  • 953
  • 2
  • 16
  • 29

6 Answers6

36

CREATE TABLE AS is considered a separate statement from a normal CREATE TABLE, and until Postgres version 9.5 (see changelog entry) didn't support an IF NOT EXISTS clause. (Be sure to look at the correct version of the manual for the version you are using.)

Although not quite as flexible, the CREATE TABLE ... LIKE syntax might be an alternative in some situations; rather than taking its structure (and content) from a SELECT statement, it copies the structure of another table or view.

Consequently, you could write something like this (untested); the final insert is a rather messy way of doing nothing if the table is already populated:

CREATE OR REPLACE VIEW source_data AS SELECT * FROM foo NATURAL JOIN bar;

CREATE TABLE IF NOT EXISTS snapshot LIKE source_data;

INSERT INTO snapshot
SELECT * FROM source_data
WHERE NOT EXISTS ( SELECT * FROM snapshot );

Alternatively, if you want to discard previous data (e.g. an abandoned temporary table), you could conditionally drop the old table, and unconditionally create the new one:

DROP TABLE IF EXISTS temp_stuff;

CREATE TEMPORARY TABLE temp_stuff AS SELECT * FROM foo NATURAL JOIN bar;
IMSoP
  • 89,526
  • 13
  • 117
  • 169
  • I'll play around with your suggestion. Thanks - I appreciate it. – user3591836 Sep 17 '14 at 18:25
  • 1
    If you are going to create a `VIEW` in order to copy the table structure, you might go all the way and use a **[`MATERIALIZED VIEW`](http://www.postgresql.org/docs/current/static/sql-creatematerializedview.html)** instead. – Erwin Brandstetter Sep 17 '14 at 20:37
  • @ErwinBrandstetter Hm, that's true. Except that that doesn't accept an `IF NOT EXISTS` clause either; you can `DROP MATERIALISED VIEW IF EXISTS`, though. Without knowing the exact use case, it's hard to know if any of these options are actually relevant. – IMSoP Sep 17 '14 at 22:39
  • @ErwinBrandstetter be specific in the `PostgreSQL version` that supports _feature_ because `MATERIALIZED VIEW` supports `PostgreSQL version 9.3` and above – Vivek S. Sep 18 '14 at 05:06
  • @WingedPanther: Correct. I tend to assume the current version of Postgres for questions that can't be bothered to declare the actual version in use. – Erwin Brandstetter Sep 18 '14 at 13:17
  • Is it that `IF NOT EXISTS` isn't allowed on `CREATE TABLE AS`, or does something in the query conflict with using `IF NOT EXISTS`? The reason I ask is - you (very helpfully) distinguish and link to documentation for `CREATE TABLE` vs `CREATE TABLE AS`, but the `CREATE TABLE AS` page includes `IF NOT EXISTS` in its synopsis, so it's not clear what exactly causes the issue. – dwanderson Oct 13 '16 at 17:01
  • @dwanderson At the time of writing, `CREATE TABLE AS` *didn't* support the option; apparently it [was added in version 9.5](https://www.postgresql.org/docs/9.5/static/release-9-5.html#AEN127121). I guess I should update the answer for those lucky enough to be running a nice up to date version. :) – IMSoP Oct 13 '16 at 18:08
  • Ahh, that explains it all - we're on 9.4, so we just missed it (which is why I ended up on this page in the first place - `CREATE TABLE IF NOT EXISTS ... AS` didn't work for me). Thanks for looking into it, and for the prompt reply to a question over 2 years old! – dwanderson Oct 13 '16 at 21:47
  • In version 9.3 at least, the `LIKE source_data` piece needs to be enclosed in parentheses, otherwise the line won't execute – Dylan May 22 '17 at 16:00
7

CREATE TABLE IF NOT EXISTS ... was added in Postgres 9.1. See:

Postgres 9.0 or older

If you are going to write a function for this, base it on system catalog table pg_class, not on views in the information schema or the statistics collector (which only exist if activated).

CREATE OR REPLACE FUNCTION create_table_qry(_tbl text
                                          , _qry text
                                          , _schema text = NULL)
  RETURNS bool
  LANGUAGE plpgsql AS
$func$
DECLARE
   _sch text := COALESCE(_schema, current_schema());
BEGIN
   IF EXISTS (
      SELECT FROM pg_catalog.pg_class c
      JOIN   pg_catalog.pg_namespace n ON n.oid = c.relnamespace
      WHERE  n.nspname = _sch
      AND    c.relname = _tbl
      ) THEN
   
      RAISE NOTICE 'Name is not free: %.%',_sch, _tbl;
      RETURN  FALSE;
   ELSE
      EXECUTE format('CREATE TABLE %I.%I AS %s', _sch, _tbl, _qry);

      RAISE NOTICE 'Table created successfully: %.%',_sch, _tbl;
      RETURN  TRUE;
   END IF;
END
$func$;

The function takes a table name and the query string, and optionally also a schema to create the table in (defaults to the current schema).

Note the correct use of = in the function header and := in the function body:

Also note how identifiers are escaped as identifiers. You can't use regclass, since the table does not exist, yet:

Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
0

Try this,

create or replace function create_table(tblname text) returns text as
$$ 
BEGIN
$1 = trim($1);
IF not EXISTS (select relname from pg_stat_user_tables where relname =$1) THEN
execute 'create table '||$1||' as select * from tbl'; -- <put your query here>
return ''||$1||' Created Successfully !!';
else
return  ''||$1||' Already Exists !!';
END IF;
END
$$
language plpgsql 

create or replace function create_table_qry(tblname text,qry text) returns text as
$$ 
BEGIN
$1 = trim($1);
IF not EXISTS (select relname from pg_stat_user_tables where relname =$1) THEN
execute 'create table '||$1||' as '||$2||'';
return ''||$1||' Created Successfully !!';
else
return  ''||$1||' Already Exists !!';
END IF;
END
$$
language plpgsql 
Vivek S.
  • 19,945
  • 7
  • 68
  • 85
0

It’s simple:

 CREATE TABLE IF NOT EXISTS abc ( sql_id BIGINT(20) NOT NULL
   AUTO_INCREMENT PRIMARY KEY, sender VARCHAR(20) NULL)
Eric Aya
  • 69,473
  • 35
  • 181
  • 253
Saquib Azam
  • 73
  • 1
  • 4
  • Usually it's better to explain a solution instead of just posting some rows of anonymous code. You can read [How do I write a good answer](https://stackoverflow.com/help/how-to-answer), and also [Explaining entirely code-based answers](https://meta.stackexchange.com/questions/114762/explaining-entirely-%E2%80%8C%E2%80%8Bcode-based-answers) – Anh Pham Dec 25 '18 at 12:08
  • The poster asked about `create table as` not `create table`. For `create table`, yes, `if not exists` is supported – SimonInNYC Dec 04 '20 at 15:16
0

Use do :

do $$ begin

if not exists (  SELECT 1
   FROM   information_schema.tables 
   WHERE  table_schema = 'schema_name'
   AND    table_name = 'bla ') then

  create table schema_name.bla as select * from blu;
end if;

end $$;

0

CTAS (Create Table AS) for REDSHIFT PLPGSQL flavor. Shout out to Erwin Brandstetter for the root idea using pure PG syntax.

CREATE
OR
REPLACE
PROCEDURE pipeline.sp_create_table_if_not_exists_as (sch VARCHAR, tbl VARCHAR, qry VARCHAR, tbl_attrs VARCHAR)
 AS
    /*
     specifically an exception for CTAS functionality: https://docs.aws.amazon.com/redshift/latest/dg/r_CREATE_TABLE_AS.html
    */
$$
BEGIN

IF EXISTS (
   SELECT 1
   FROM   pg_catalog.pg_class c
   JOIN   pg_catalog.pg_namespace n ON n.oid = c.relnamespace
   WHERE  n.nspname = sch
   AND    c.relname = tbl
   ) THEN

   RAISE INFO 'Table already exists: %.%', sch, tbl;
ELSE
    EXECUTE 'CREATE TABLE ' || sch || '.' || tbl || ' ' || tbl_attrs || ' AS ' || qry;
    RAISE INFO 'Table created successfully: %.%, using query: [%], optional attributes: [%]', sch, tbl, qry, tbl_attrs;
END IF;

END;
$$
language plpgsql;
Goran B.
  • 542
  • 4
  • 14