Estoy bastante seguro de que muchas aplicaciones, aplicaciones críticas, bancos, etc., hacen esto a diario.
La idea detrás de todo eso es:
- todas las filas deben tener un historial
- todos los enlaces deben permanecer coherentes
- debería ser fácil hacer solicitudes para obtener columnas "actuales"
- Los clientes que hayan comprado cosas obsoletas aún deben ver lo que han comprado aunque este producto ya no sea parte del catálogo.
y así.
Esto es lo que quiero hacer, y explicaré los problemas que estoy enfrentando.
Todas mis tablas tendrán esas columnas:
id
id_origin
date of creation
start date of validity
start end of validity
Y aquí están las ideas para las operaciones CRUD:
- crear = insertar nueva fila con
id_origin
=id
,date of creation
= ahora,start date of validity
= ahora,end date of validity
= nulo (= significa que es el registro activo actual) - actualización =
- leer = leer todos los registros con
end date of validity
== nulo - actualizar el registro "actual"
end date of validity
= nulo conend date of validity
= ahora - cree uno nuevo con los nuevos valores y
end date of validity
= nulo (= significa que es el registro activo actual)
- leer = leer todos los registros con
- eliminar = actualizar el registro "actual"
end date of validity
= nulo conend date of validity
= ahora
Así que aquí está mi problema: con asociaciones de muchos a muchos. Tomemos un ejemplo con valores:
- Tabla A (id = 1, id_origin = 1, start = now, end = null)
- Tabla A_B (inicio = ahora, fin = nulo, id_A = 1, id_B = 48)
- Tabla B (id = 48, id_origin = 48, start = now, end = null)
Ahora quiero actualizar la tabla A, id de registro = 1
- Marco id de registro = 1 con end = ahora
Inserto un nuevo valor en la tabla A y ... maldita sea, he perdido mi relación A_B a menos que también duplique la relación ... esto terminaría en una tabla:
Tabla A (id = 1, id_origin = 1, start = now, end = now + 8mn)
- Tabla A (id = 2, id_origin = 1, start = now + 8mn, end = null)
- Tabla A_B (inicio = ahora, fin = nulo, id_A = 1, id_B = 48)
- Tabla A_B (inicio = ahora, fin = nulo, id_A = 2, id_B = 48)
- Tabla B (id = 48, id_origin = 48, start = now, end = null)
Y ... bueno, tengo otro problema: la relación A_B: ¿debo marcar (id_A = 1, id_B = 48) como obsoleto o no (A - id = 1 es obsoleto, pero no B - 48)?
Como lidiar con esto?
Tengo que diseñar esto a gran escala: productos, socios, etc.
¿Cuál es tu experiencia en esto? ¿Cómo lo harías (cómo lo has hecho)?
- Editar
Encontré este artículo muy interesante , pero no trata adecuadamente con la "obsolescencia en cascada" (= lo que estoy preguntando en realidad)
fuente
Respuestas:
No tengo claro si estos requisitos son para fines de auditoría o simplemente una referencia histórica simple, como con CRM y carritos de compras.
De cualquier manera, considere tener una tabla main y main_archive para cada área principal donde se requiera. "Main" solo tendrá entradas actuales / activas, mientras que "main_archive" tendrá una copia de todo lo que entra en main. Insertar / actualizar en main_archive puede ser un desencadenante de insertar / actualizar en main. Las eliminaciones contra main_archive pueden ejecutarse a lo largo de un período de tiempo más largo, si alguna vez.
Para los problemas de referencia como Cust X compró el Producto Y, la forma más fácil de resolver su preocupación referencial de cust_archive -> product_archive es nunca eliminar entradas de product_archive. En general, la rotación debería ser mucho más baja en esa tabla, por lo que el tamaño no debería ser una gran preocupación.
HTH.
fuente
LP_
, y cada tabla importante tiene un equivalenteLH_
, con desencadenantes que insertan filas históricas en insertar, actualizar y eliminar. No funciona para todos los casos, pero ha sido un modelo sólido para las cosas que hago.UNION
vista, lo que le permite hacer cosas interesantes como aplicar una restricción única en los registros actuales e históricos.id
" y "id_ref
".id_ref
es una referencia a la idea real de la tabla. Ejemplo:person
yperson_h
. enperson_h
lo que tengo "id
" y "id_ref
", dondeid_ref
se relaciona con 'person.id
' para que pueda tener muchas filas con el mismoperson.id
(= cuando una fila deperson
se modifica) y todo loid
que está de todos mis cuadros son AutoInc.Esto tiene cierta superposición con la programación funcional; específicamente el concepto de inmutabilidad.
Tiene una tabla llamada
PRODUCT
y otra llamadaPRODUCTVERSION
o similar. Cuando cambia un producto, no realiza una actualización, simplemente inserta una nuevaPRODUCTVERSION
fila. Para obtener la última versión, puede indexar la tabla por número de versión (desc), marca de tiempo (desc) o puede tener un indicador (LatestVersion
).Ahora, si tiene algo que hace referencia a un producto, puede decidir a qué tabla apunta. ¿Apunta a la
PRODUCT
entidad (siempre se refiere a este producto) oa laPRODUCTVERSION
entidad (solo se refiere a esta versión del producto)?Se pone complicado. ¿Qué pasa si tienes fotos del producto? Deben apuntar a la tabla de versiones, porque podrían cambiarse, pero en muchos casos, no lo harán y no querrá duplicar datos innecesariamente. Eso significa que necesita una
PICTURE
mesa y una relación dePRODUCTVERSIONPICTURE
muchos a muchos.fuente
He implementado todas las cosas desde aquí con 4 campos que están en todas mis tablas:
Cada vez que un registro tiene que modificarse, lo duplico, marco el registro duplicado como "antiguo" =
date_validity_end=NOW()
y el actual como el buenodate_validity_start=NOW()
ydate_validity_end=NULL
.El truco está en las relaciones de muchos a muchos y de uno a muchos: ¡funciona sin tocarlos! Se trata de las consultas que son más complejas: para consultar un registro en una fecha precisa (= no ahora), tengo para cada combinación, y para la tabla principal, agregar esas restricciones:
Entonces, con productos y atributos (relación de muchos a muchos):
fuente
¿Qué tal esto? Parece simple y bastante efectivo para lo que he hecho en el pasado. En su tabla de "historial", use una PK diferente. Por lo tanto, su campo "CustomerID" es el PK en su tabla Customer, pero en la tabla "history", su PK es "NewCustomerID". "CustomerID" se convierte en otro campo de solo lectura. Eso deja "CustomerID" sin cambios en el historial y todas sus relaciones permanecen intactas.
fuente