En el trabajo, para SQL Server, escribimos scripts de cambio de esquema que primero retrotraen el cambio que se realizará (idempotentemente, por lo que la sección de reversión funciona bien incluso si el cambio de esquema no se aplica), y luego una sección para aplicar el cambio En TSQL es fácil echar un vistazo en el catálogo del sistema u otras tablas para ver si ya existen tablas/columnas/índices/filas y no hacer nada si no es así.
En PostgreSQL, está un poco más limitado con los comandos que simplemente puede enviar al servidor, pero, por otro lado, DDL es transaccional, por lo que un cambio de esquema mitad aplicado no debería ocurrir. He adaptado bastante bien el esquema al que estoy acostumbrado en el trabajo en mis pequeños proyectos (¿demasiado? Pero incluso aquí tengo un db dev/test y un db "real"), por ejemplo:
\echo Rolling back schema change #35
BEGIN;
DELETE FROM schema_version WHERE schema_id = 35;
DROP TABLE IF EXISTS location_coordinates;
DROP FUNCTION IF EXISTS location_coordinates_populate();
END;
\echo Applying schema change #35
BEGIN;
INSERT INTO schema_version(schema_id, description) VALUES(35, 'Add location_coordinates table');
CREATE TABLE location_coordinates(
location_id INT PRIMARY KEY REFERENCES location(location_id),
latitude FLOAT NOT NULL,
longitude FLOAT NOT NULL,
earth_coordinates earth NOT NULL,
box_10miles cube NOT NULL
);
GRANT SELECT, INSERT, UPDATE, DELETE ON location_coordinates TO ui;
CREATE FUNCTION location_coordinates_populate() RETURNS TRIGGER LANGUAGE 'plpgsql' AS $$
BEGIN
new.earth_coordinates := ll_to_earth(new.latitude, new.longitude);
new.box_10miles := earth_box(new.earth_coordinates, 10 * 1609.344);
RETURN new;
END
$$;
CREATE TRIGGER location_coordinates_populate BEFORE INSERT OR UPDATE ON location_coordinates
FOR EACH ROW EXECUTE PROCEDURE location_coordinates_populate();
INSERT INTO location_coordinates(location_id, latitude, longitude)
SELECT location_id, latitude, longitude FROM location WHERE latitude IS NOT NULL AND longitude IS NOT NULL;
CREATE INDEX location_coordinates_10miles ON location_coordinates USING gist (box_10miles);
END;
\echo Done
Este script se puede ejecutar en la base de datos solo con "psql -f esquema-cambios/35.sql". Al cortar el mensaje de "aplicación ...", puedo obtener los comandos para retrotraerlo. Y como puede ver, el cambio mantiene una tabla de metadatos "schema_version" para que pueda ver qué cambios se aplican. El cambio completo se realiza como una transacción, migración de datos y todo. Aquí he usado la capacidad "IF EXISTS" de los comandos DROP para hacer feliz la sección de reversión incluso cuando el cambio no se aplica. Una cosa que hicimos en el trabajo para Oracle fue escribir cambios de esquema como PL/SQL: ¿podría tener algunas funciones en plpgsql para ayudar con los cambios?
Tenga en cuenta que en el cambio anterior, donde estoy migrando las columnas "latitud" y "longitud" (que eran nulables) de "ubicación" a una relación "ubicación_coordina" separada (y agregando material de distancia) , No solté las viejas columnas. Una cosa que tenemos que tener cuidado es hacer que los cambios de esquema sean compatibles con versiones anteriores si es posible. Entonces puedo aplicar este cambio de esquema antes de actualizando la aplicación para usar las nuevas tablas. Tendría un segundo cambio para soltar las columnas antiguas para aplicar después de actualizando la aplicación. En el trabajo, esto se haría en dos ciclos de lanzamiento diferentes, por lo que durante la versión X todavía tenemos la opción de deshacer la aplicación para liberar X-1 sin tener que deshacer primero todos los cambios de esquema; así como poder implementar cambios de esquema en una ventana separada antes de las aplicaciones. (Técnicamente debería haber escrito un disparador para que las actualizaciones de la tabla anterior se sincronicen con la nueva tabla, pero no lo hice porque eso es demasiado trabajo :))
También tenemos cosas como una aplicación que araña todos nuestros bases de datos para ver lo que está en la tabla schema_version
, y realiza un seguimiento de los cambios para que las personas puedan ver qué cambios se realizaron sin tener que conectarse y obtener una idea del historial de cada cambio (rastreamos "retrotraído en dev", "aplicado en dev "etc.)En el trabajo, nuestra tabla schema_version también incluye información sobre autoría, etc. Una forma mágica de aplicar la información de la versión desde el control de versiones sería genial; un problema que tenemos es que si un SC se aplica en QA, por ejemplo, luego cambia en Perforce, quizás no -uno avisos. Entonces, una forma de rastrear ese cambio de esquema 35, se aplicó la revisión # 4 sería bueno.
Una cosa a tener en cuenta - los cambios de esquema para nosotros están numerados independientemente de las versiones de la aplicación. Obviamente, están relacionados --- eso es otra cosa que la aplicación Spidering permite a las personas ingresar --- pero tratamos de tener muchos pequeños cambios en lugar de un parche gigante "aquí está todo para la versión X". Los cambios de esquema también se usan para agregar nuevos índices, por lo que es posible que no se apliquen en absoluto. En general, los cambios en el esquema son "propiedad" de los desarrolladores, no de los DBA, aunque en el ejemplo de "crear índice" anterior, el DBA básicamente actúa en una función de desarrollador y es dueño del cambio de esquema. Sí, insistimos en un alto nivel de capacidad de SQL por parte de los desarrolladores, aunque otros grupos en la empresa trabajan de forma un poco diferente y dan más trabajo al equipo de DB.
Gracias aragnid. También en nuestra empresa, los roles de los desarrolladores incluyen la creación de los parches de esquema apropiados también. Se utiliza un solo proceso para procesar realmente los parches. Me gusta la idea de una tabla schema_version, la habíamos discutido en mi lugar de trabajo, y verla usar una también lo valida. – chadl