Scripts SQL Reentrantes

0
11615

Scripts SQL Reentrantes

A medida que un software evoluciona suele ser necesario realizar modificaciones en el modelo de datos.

Para realizar estas modificaciones, a mi particularmente me gusta realizar un script DDL y otro DML que pueda ser ejecutado N veces (reentrante) sin que se produzcan errores.

A continuación y a modo de consulta os voy a mostrar unos script de ejemplo autocomentados en donde creamos un esquema de BD, unas tablas, índices, restricciones y campos.

Ejemplo en MySql.

DELIMITER $$

-- Creamos el Schema si no existe
CREATE SCHEMA IF NOT EXISTS db_test;

$$

DROP PROCEDURE IF EXISTS db_test.procedureTemp;

$$

CREATE PROCEDURE db_test.procedureTemp()
BEGIN

  -- --------------------------------------------------------------------
  -- Declaración de variables
  -- --------------------------------------------------------------------

  DECLARE isOk    BOOL DEFAULT TRUE;
  DECLARE cuenta  INT DEFAULT 0;
  DECLARE cursor1 CURSOR FOR SELECT COUNT(*) FROM db_test.companies WHERE companykey='Autentia';

  -- Al crear un índice que ya existe se genera el siguiente error
  DECLARE CONTINUE HANDLER FOR 1061 SET isOk = False;

  -- Al crear un Foreign Key que ya existe se genera el siguiente error
  DECLARE CONTINUE HANDLER FOR 1005 SET isOk = False;

  -- Al eliminar un Foreign Key que no existe se genera el siguiente error
  DECLARE CONTINUE HANDLER FOR 1025 SET isOk = False;

  -- Para más información sobre los errores:
  --   http://dev.mysql.com/doc/refman/5.0/en/declare-handlers.html
  --   http://dev.mysql.com/doc/refman/5.0/en/error-messages-server.html


  -- --------------------------------------------------------------------------------------------------------------------------------------
  -- Creamos una tabla si no existiese, para ello consultamos las tablas de MySQL que mantienen metainformación sobre los esquemas.
  -- Representa los datos de empresas
  -- --------------------------------------------------------------------------------------------------------------------------------------

  SELECT COUNT(*) INTO cuenta FROM `information_schema`.`tables` WHERE TABLE_SCHEMA='db_test' AND TABLE_NAME='companies' LIMIT 1;
  IF (cuenta = 0)  THEN
      CREATE TABLE db_test.companies (
          id         INT(3)      UNSIGNED NOT NULL AUTO_INCREMENT,
          companykey VARCHAR(25) NOT NULL,
          name       VARCHAR(100) NOT NULL,
          url        VARCHAR(255),
          PRIMARY KEY  (id),
          UNIQUE companyKeyIDX (companykey)        -- No puede haber dos empresas con el mismo identificador
      ) ENGINE=InnoDB CHARSET=utf8 collate=utf8_general_ci;
  END IF;


  -- --------------------------------------------------------------------
  -- Creamos una tabla si no existiese.
  -- Representa los datos de empleados (contactos) dentro de cada empresa
  -- --------------------------------------------------------------------

  SELECT COUNT(*) INTO cuenta FROM `information_schema`.`tables` WHERE TABLE_SCHEMA='db_test' AND TABLE_NAME='employees' LIMIT 1;
  IF (cuenta = 0)  THEN
      CREATE TABLE db_test.employees (
          id          INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
          employeekey VARCHAR(25) NOT NULL,
          companyid   INT(3)  UNSIGNED NOT NULL,
          name        VARCHAR(20),
          lastNames   VARCHAR(30),
          PRIMARY KEY  (id),
          UNIQUE employeeIdx (companyid, employeekey),    -- No puede haber dos empleados en la misma empresa con el mismo identificador
          KEY    companyIDX  (companyid),                 -- Indice necesario para crear la restricción Foreign Key con la tabla companies
          CONSTRAINT empToCompFK FOREIGN KEY (companyid) REFERENCES companies(id) ON DELETE CASCADE ON UPDATE CASCADE
      ) ENGINE=InnoDB CHARSET=utf8 collate=utf8_general_ci;
  END IF;


  -- ------------------------------------------------------------------------------------------------------
  -- Insertamos una empresa de prueba si no existiese (Lo vamos a hacer a través de un cursor)
  -- ------------------------------------------------------------------------------------------------------

  OPEN  cursor1;
  FETCH cursor1 INTO cuenta;
  IF (cuenta = 0)  THEN
      INSERT INTO db_test.companies (id, companykey, name, url) VALUES (1, 'Autentia', 'Autentia Real Business Solutions', 'http://www.autentia.com');
  END IF;
  CLOSE cursor1;

  -- ------------------------------------------------------------------------------------------------------
  -- Insertamos un empleado de prueba si no existiese. (Lo vamos a hacer a traves de SELECT INTO)
  -- ------------------------------------------------------------------------------------------------------

  SELECT COUNT(*) INTO cuenta FROM db_test.employees WHERE ((companyid=1) AND (employeekey='carlosgarcia'));
  IF (cuenta = 0)  THEN
      INSERT INTO db_test.employees (companyid, employeekey, name, lastNames) VALUES (1, 'carlosgarcia','Carlos', 'García Pérez');
  END IF;


  -- ------------------------------------------------------------------------------------------------------
  -- Creamos el campo email del empleado si no existe
  -- ------------------------------------------------------------------------------------------------------

  SELECT COUNT(*) INTO cuenta FROM `information_schema`.`COLUMNS` WHERE ((TABLE_SCHEMA='db_test') AND (TABLE_NAME='employees') AND (COLUMN_NAME='email')) LIMIT 1;
  IF (cuenta = 0)  THEN
      ALTER TABLE db_test.employees ADD COLUMN email VARCHAR(100) NULL;
  END IF;


  -- ------------------------------------------------------------------------------------------------------
  -- Se accede mucho por el email del empleado, vamos a crear un indice por el campo email del empleado
  -- ------------------------------------------------------------------------------------------------------

  -- Notas: No existe en el esquema una tabla que muestre los indices (Aunque si las restricciones en la tabla KEY_COLUMN_USAGE)
  -- Una forma de recuperarlos sería a través de un cursor y "SHOW INDEX FROM db_test.employees", Pero es más fácil hacerlo
  -- controlando el error 1061 que se genera segun la documentación.
  ALTER TABLE db_test.employees ADD INDEX `idxEmployeeEmail` (email);


  -- ------------------------------------------------------------------------------------------------------
  -- Creamos una Foreign Key entre la tabla employess y companies
  -- ------------------------------------------------------------------------------------------------------

  SELECT COUNT(*) INTO cuenta  FROM `information_schema`.`KEY_COLUMN_USAGE` WHERE (CONSTRAINT_SCHEMA='db_test')   AND 
                                                                                  (CONSTRAINT_NAME='FK_Temporal') AND 
                                                                                  (TABLE_NAME='employees');
  IF (cuenta = 0)  THEN
      ALTER TABLE db_test.employees ADD CONSTRAINT `FK_Temporal` FOREIGN KEY `FK_Temporal` (companyid) REFERENCES companies(id) ON DELETE CASCADE ON UPDATE CASCADE;
  END IF;


  -- Borramos una ForeignKey.Si el Foreign Key no existiese se produciría el error 1025 (Como tenemos controlado el error el Script continuará con normalidad)
  ALTER TABLE db_test.employees DROP FOREIGN KEY `FK_Temporal`;

END


$$


DELIMITER ;

-- Invocamos el procedimiento almacenado
CALL db_test.procedureTemp();

-- Borramos el procedimiento almacenado
DROP PROCEDURE IF EXISTS db_test.procedureTemp;    

Ejemplo en SQLServer.

-- --------------------------------------------------------------------
-- Creamos una base de datos si no existiese.
-- --------------------------------------------------------------------

IF NOT EXISTS (SELECT * from sys.databases where name = 'db_test')
BEGIN
	CREATE DATABASE db_test;
END

-- Establecemos la  base de datos prodeterminada
USE db_test;


-- --------------------------------------------------------------------
-- Creamos una tabla si no existiese.
-- Representa los datos de empresas
-- --------------------------------------------------------------------

IF NOT EXISTS (SELECT * FROM sys.sysobjects WHERE name='companies' AND xtype='U')
BEGIN
	CREATE TABLE companies (
	  id         INTEGER      NOT NULL IDENTITY,
	  companykey VARCHAR(25)  NOT NULL,
	  name       VARCHAR(100) NOT NULL,
	  url        VARCHAR(255),
	  PRIMARY KEY (id)  
	);
END

-- --------------------------------------------------------------------
-- Creamos un índice si no existe.
-- No puede haber dos empresas con el mismo identificador
-- --------------------------------------------------------------------

IF NOT EXISTS (SELECT * FROM sys.sysindexes WHERE name='companyKeyIDX' AND id=(SELECT id FROM sys.sysobjects WHERE name='companies'))
BEGIN
	CREATE UNIQUE INDEX "companyKeyIDX" ON companies (companykey);	
END

-- --------------------------------------------------------------------
-- Creamos una tabla si no existiese.
-- Representa los datos de empleados (contactos) dentro de cada empresa
-- --------------------------------------------------------------------


IF NOT EXISTS (SELECT * FROM sys.sysobjects WHERE name='employees' AND xtype='U')
BEGIN
	CREATE TABLE employees (
	  id          INTEGER		NOT NULL IDENTITY,
	  employeekey VARCHAR(25)	NOT NULL,
	  companyid   INTEGER		NOT NULL,
	  name        VARCHAR(20),
	  lastNames   VARCHAR(30),
	  PRIMARY KEY  (id),
	  CONSTRAINT employeeIdx UNIQUE (companyid, employeekey)    -- No puede haber dos empleados en la misma empresa con el mismo identificador
	)
END

-- ------------------------------------------------------------------------------------------------------
-- Insertamos una empresa de prueba si no existiese
-- ------------------------------------------------------------------------------------------------------


IF NOT EXISTS (SELECT id FROM companies WHERE companykey='Autentia')
BEGIN
	SET IDENTITY_INSERT companies ON;	-- Necesario para poder insertar en un campo IDENTITY (Autonumérico)
	INSERT INTO companies (id, companykey, name, url) VALUES (1, 'Autentia', 'Autentia Real Business Solutions', 'http://www.autentia.com');
	SET IDENTITY_INSERT companies OFF;
END


-- ------------------------------------------------------------------------------------------------------
-- Insertamos un empleado de prueba si no existiese. (Lo vamos a hacer a traves de SELECT INTO)
-- ------------------------------------------------------------------------------------------------------

IF NOT EXISTS (SELECT id FROM employees WHERE ((companyid=1) AND (employeekey='carlosgarcia')))
BEGIN   
	INSERT INTO employees (companyid, employeekey, name, lastNames) VALUES (1, 'carlosgarcia','Carlos', 'García Pérez');
END


-- ------------------------------------------------------------------------------------------------------
-- Creamos el campo email del empleado si no existe
-- ------------------------------------------------------------------------------------------------------

IF NOT EXISTS (SELECT id FROM sys.syscolumns where name='email' and id=(SELECT id FROM sys.sysobjects WHERE name='employees' AND xtype='U'))
BEGIN
	ALTER TABLE employees ADD email VARCHAR(100) NULL;
END

-- ------------------------------------------------------------------------------------------------------
-- Se accede mucho por el email del empleado, vamos a crear un indice por el campo email del empleado
-- ------------------------------------------------------------------------------------------------------

IF NOT EXISTS (SELECT id from sys.sysindexes WHERE ((name='idxEmployeeEmail') AND (id=(SELECT id FROM sys.sysobjects WHERE name='employees' AND xtype='U'))))
BEGIN
	CREATE INDEX "idxEmployeeEmail" ON employees (email);
END

-- ------------------------------------------------------------------------------------------------------
-- Creamos una Foreign Key entre la tabla employess y companies
-- ------------------------------------------------------------------------------------------------------

IF NOT EXISTS (SELECT * from sys.objects WHERE (name='empToCompFK') AND (type='F') AND (parent_object_id=(SELECT id FROM sys.sysobjects WHERE name='employees' AND xtype='U')))
BEGIN
	ALTER TABLE employees ADD CONSTRAINT empToCompFK  FOREIGN KEY (companyid) REFERENCES companies(id) ON DELETE CASCADE ON UPDATE CASCADE;
END

Bueno, espero que os haya servido de utilidad este pequeño tutorial, yo personalmente lo usaré a modo de consulta, pues hay veces que uno se tira mucho tiempo en otros entornos y luego vienen bien estos tutoriales para refrescar la memoria ;-).

Carlos García Pérez. Creador de MobileTest, un complemento educativo para los profesores y sus alumnos.

cgpcosmad@gmail.com

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad