2012-09-19 26 views
5

Tengo un tipo de pregunta de "resurgir" ... pero, quiero arrojarla porque es un poco diferente a cualquiera que haya leído en desbordamiento de pila.Cómo saber si el registro ha cambiado en Postgres

Problema básico.

Estoy trabajando en mover de mysql a PostgreSQL 9.1.5 (alojado en Heroku). Como parte de eso, necesito importar múltiples archivos CSV todos los días. Algunos de los datos son información de ventas y casi se garantiza que serán nuevos y deben insertarse. Pero, es casi seguro que otras partes de los datos serán iguales. Por ejemplo, los archivos csv (nota plural) tendrán información POS (punto de venta) en ellos. Esto rara vez cambia (y lo más probable es solo a través de adiciones). Luego está la información del producto. Hay aproximadamente 10,000 productos (la gran mayoría no se modificará, pero es posible tener tanto adiciones como actualizaciones).

El último elemento (pero es importante), es que tengo un requisito para poder proporcionar un seguimiento/información de auditoría para cualquier elemento dado. Por ejemplo, si agrego un nuevo registro POS, necesito poder rastrearlo al archivo en el que se encontró. Si cambio un código UPC o una descripción de un producto, entonces necesito poder rastrearlo. a la importación (y archivo) de donde vino el cambio.

Solución que estoy contemplando.

Como los datos se me proporcionan a través de CSV, entonces estoy trabajando en torno a la idea de que COPY será la mejor/la más rápida. La estructura de los datos en los archivos no es exactamente lo que tengo en la base de datos (es decir, destino final). Por lo tanto, los copio en tablas en el esquema de etapas que coinciden con el CSV (nota: un esquema por fuente de datos). Las tablas en los esquemas de etapas tendrán una activación de fila antes de insertar. Estos factores desencadenantes pueden decidir qué hacer con los datos (insertar, actualizar o ignorar).

Para las tablas que tienen más probabilidades de contener datos nuevos, primero intentará insertarlas. Si el registro ya está allí, devolverá NULL (y detendrá el inserto en la tabla de etapas). Para las tablas que rara vez cambian, consultará la tabla y verá si se encuentra el registro. Si es así, entonces necesito una forma de ver si se cambian algunos de los campos. (porque recuerde, necesito mostrar que el registro fue modificado importando x del archivo y) Obviamente, puedo caldear el código y probar cada columna. Pero, estaba buscando algo un poco más "elocuente" y más sostenible que eso.

En cierto modo, lo que estoy haciendo es combinar un sistema de importación con un sistema de seguimiento de auditoría. Entonces, al investigar pistas de auditoría, revisé el siguiente artículo wiki.postgresql.org. Parece que hstore podría ser una buena forma de obtener cambios (y ser capaz de ignorar fácilmente algunas columnas de la tabla que no son importantes, por ejemplo, "last_modified")

Estoy seguro de que todo trabajo ... Creé algunas tablas de prueba, etc. y jugué con ellas.

Mi pregunta?

Es una forma mejor y más sostenible de llevar a cabo esta tarea de encontrar los quizás 3 registros de 10K que requieren un cambio en la base de datos. Definitivamente podría escribir un script de Python (u otra cosa) que lea el archivo e intente averiguar qué hacer con cada registro, pero eso se siente terriblemente ineficiente y dará lugar a muchos viajes redondos.

Algunas cosas finales:

  1. que no tienen control sobre los archivos de entrada. Me encantaría que solo me enviaran los deltas, pero no lo hacen y está completamente fuera de mi control o influencia.
  2. El sistema crece y es probable que se agreguen nuevas fuentes de datos que aumentarán en gran medida la cantidad de datos que se procesan (así que estoy tratando de mantener la eficiencia)
  3. Sé que esto no es agradable, simple ASÍ pregunta (como "cómo ordenar una lista en python") pero creo que una de las mejores cosas de SO es que puedes hacer preguntas difíciles y las personas compartirán sus opiniones sobre cómo piensan que es la mejor manera de resolverlo.
+0

Dos (final) preguntas: 1) ¿tiene borrados, o la entrada es "incremental"? 2) ¿pueden los proveedores de los datos garantizar * claves estables * (sin actualizaciones clave)? – wildplasser

+0

Es diferente con la fuente de datos y el tipo de datos. Definitivamente es una situación en la que probablemente debería estar a la "defensiva" al manejar los datos y estar preparado para casi cualquier cosa.Dicho esto, creo que podría haber eliminado (pero, raro) y creo que las claves * deberían * ser estables (en otras palabras, la ID para el registro POS debe permanecer igual entre las cargas). –

Respuesta

7

Tengo muchas operaciones similares. Lo que hago es COPY a tablas de fases temporales:

CREATE TEMP TABLE target_tmp AS 
SELECT * FROM target_tbl LIMIT 0; -- only copy structure, no data 

COPY target_tmp FROM '/path/to/target.csv'; 

Para rendimiento, ejecute ANALYZE - temp. ¡las tablas no son analizadas por autovacuum!

ANALYZE target_tmp; 

también para el rendimiento, tal vez incluso crear un índice o dos en la tabla temporal, o añadir una clave primaria si los datos lo permite.

ALTER TABLE ADD CONSTRAINT target_tmp_pkey PRIMARY KEY(target_id); 

No necesita el rendimiento para pequeñas importaciones.

A continuación, utilice el alcance completo de los comandos SQL para digerir los datos nuevos.
Por ejemplo, si la clave primaria de la tabla de destino es target_id ..

Tal DELETE lo que no existe más?

DELETE FROM target_tbl t 
WHERE NOT EXISTS (
    SELECT 1 FROM target_tmp t1 
    WHERE t1.target_id = t.target_id 
); 

Entonces UPDATE lo que ya existe:

UPDATE target_tbl t 
SET col1 = t1.col1 
FROM target_tmp t1 
WHERE t.target_id = t1.target_id 

Para evitar vacías actualizaciones, sólo tiene que añadir:

... 
AND col1 IS DISTINCT FROM t1.col1; -- repeat for relevant columns 

O, si toda la fila es relevante:

... 
AND t IS DISTINCT FROM t1;   -- check the whole row 

Entonces INSERT lo que hay de nuevo:

INSERT INTO target_tbl(target_id, col1) 
SELECT t1.target_id, t1.col1 
FROM target_tmp t1 
LEFT JOIN target_tbl t USING (target_id) 
WHERE t.target_id IS NULL; 

Limpiar si su sesión continúa (tablas temporales se dejan caer al final de la sesión automáticamente):

DROP TABLE target_tmp; 

o uso ON COMMIT DROP o similar con CREATE TEMP TABLE.
Código no probado, pero debería funcionar en cualquier versión moderna de PostgreSQL a excepción de errores tipográficos.

+0

Gracias por la respuesta. Buen ejemplo/detalle; Estoy seguro de que esto ayudará a algunas personas. Y está muy cerca de lo que necesito hacer. El único requisito que me proporciona es el rastreo de rastreo de la importación "id del lote" que realmente cambió el registro. En otras palabras, no siempre quiero actualizar/actualizar el registro si existe, solo actualizar si hay cambios. Supongo que podría. Y luego solo tenga un activador de actualización en la tabla y descubra si el registro realmente cambió ... de ser así, luego agregue un registro de seguimiento de auditoría. ¿Parece razonable? ¿Tienes una mejor manera? –

+0

@DavidS: Esto puede ser más simple de lo que esperaba. Agregué un poco a la sección ACTUALIZAR. Casi siempre es una buena idea excluir las actualizaciones vacías de todos modos. Si necesita un seguimiento de auditoría, le sugiero que copie las versiones obsoletas (más la marca de tiempo) en una tabla de historial antes de 'DELETE' /' UPDATE'. –

+0

buenas sugerencias y gracias por actualizar la respuesta. Para fines de auditoría, ¿prefiere copiar a la tabla de archivos (una copia completa de los datos + marca de tiempo) sobre simplemente usar un hstore (x. *) Y almacenarlo en un campo de texto en una tabla audit_trail/history type? La principal ventaja de la tabla de archivos es que se puede consultar fácilmente. La principal ventaja del enfoque hstore parece ser la flexibilidad si su esquema cambia en el futuro. Sé que está un poco fuera de tema y probablemente sea una cuestión propia, pero curiosa por tus pensamientos. ¡Gracias! –

Cuestiones relacionadas