PostgreSQL crea tabla si no existe

175

En un script MySQL puedes escribir:

CREATE TABLE IF NOT EXISTS foo ...;

... otras cosas ...

y luego puede ejecutar el script muchas veces sin volver a crear la tabla.

¿Cómo se hace esto en PostgreSQL?

peter2108
fuente

Respuestas:

279

Esta característica se ha implementado en Postgres 9.1 :

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



Para versiones anteriores , aquí hay una función para solucionarlo:

CREATE OR REPLACE FUNCTION create_mytable ()
  RETURNS void 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$ LANGUAGE plpgsql;

Llamada:

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

Notas:

  • Las columnas schemanamey tablenameen pg_tablesdistinguen entre mayúsculas y minúsculas. Si cita entre comillas los identificadores en la CREATE TABLEdeclaración, debe usar exactamente la misma ortografía. Si no lo hace, debe usar cadenas en minúsculas. Ver:

  • pg_tablessolo contiene tablas reales . El identificador aún puede estar ocupado por objetos relacionados. Ver:

  • Si el rol que ejecuta esta función no tiene los privilegios necesarios para crear la tabla, es posible que desee utilizarla SECURITY DEFINERpara que la función sea propiedad de otro rol con los privilegios necesarios. Esta versión es lo suficientemente segura.

Erwin Brandstetter
fuente
Me veo obligado a usar una base de datos postgres 8.4 existente. Este truco funciona, ¡gracias!
Ilimitado
1
@Boundless: vi que su edición fue rechazada como "demasiado pequeña". Lo apliqué, porque no dolerá. Sin embargo, debe ejecutar la CREATE FUNCTIONúnica vez. Es SELECT create_mytable();posible que desee llamar muchas veces.
Erwin Brandstetter
1
Brandstetter: Estoy de acuerdo contigo. El problema con el que me encontré fue que no sabía si la función se creó o no (al igual que la tabla puede o no existir). Así que quiero asegurarme de que la función se cree antes de llamarla.
Ilimitado
84

Prueba esto:

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)
)
Achilles Ram Nakirekanti
fuente
Esta es en realidad la solución más limpia. debería ser votado
SDReyes
44
de hecho, me aterra la cantidad de soluciones que involucran 'función'.
SDReyes
8
@SDReyes esas otras soluciones se publicaron antes de Postgres 9.1, que incluía la IF NOT EXISTSopción.
Kris
2
No estoy seguro de cómo contribuyó esta respuesta ya que la respuesta @ erwin-brandstetter fue completa en sí misma.
comentarista el
@comiventor correcto, este muestra cómo muestra el uso del parámetro. La respuesta principal no lo vi hasta que vi este. Esto ayuda un poco.
Enojado 84
8

Creé una solución genérica a partir de las respuestas existentes que se pueden reutilizar para cualquier tabla:

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;

Uso:

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

Se podría simplificar aún más si se toma un solo parámetro si se extrae el nombre de la tabla del parámetro de consulta. También omití los esquemas.

Ingo Fischer
fuente
3

Esta solución es algo similar a la respuesta de Erwin Brandstetter, pero usa solo el lenguaje sql.

No todas las instalaciones de PostgreSQL tienen el lenguaje plpqsql por defecto, esto significa que puede que tenga que llamar CREATE LANGUAGE plpgsqlantes de crear la función, y luego tener que eliminar el idioma nuevamente, para dejar la base de datos en el mismo estado que tenía antes (pero solo si la base de datos no tenía el lenguaje plpgsql para empezar). ¿Ves cómo crece la complejidad?

Agregar el plpgsql puede no ser un problema si está ejecutando su script localmente, sin embargo, si el script se usa para configurar el esquema en un cliente, puede no ser conveniente dejar cambios como este en la base de datos de clientes.

Esta solución está inspirada en una publicación de 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();
zpon
fuente
Su solución es excelente incluso cuando plpgsql está disponible. Es fácil de ampliar para la creación de vistas y funciones en objetos que no existen en el momento. Por ejemplo, vistas en tablas de servidores extranjeros. ¡Salvaste mi día! ¡Gracias!
Alex Yu
3

No existe CREAR TABLA SI NO EXISTE ... pero puede escribir un procedimiento simple para eso, algo como:

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;
igilfanov
fuente
3

No existe CREAR TABLA SI NO EXISTE ... pero puede escribir un procedimiento simple para eso, algo como:

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'
  );
Szymon Lipiński
fuente
dentro de un disparador no siempre funciona: gist.github.com/igilfanov/4df5e90d8a88d653132746a223639f45 ERROR: la relación "foo" ya existe
igilfanov