Tablas de autorreferencia, buenas o malas? [cerrado]

37

Representando ubicaciones geográficas dentro de una aplicación, el diseño del modelo de datos subyacente sugiere dos opciones claras (¿o quizás más?).

Una tabla con una columna parent_id autorreferenciada uk - london (london parent id = UK id)

o dos tablas, con una relación de uno a muchos utilizando una clave foránea.

Prefiero una tabla autorreferenciada, ya que permite ampliarla fácilmente a tantas subregiones como sea necesario.

EN general, ¿las personas se desvían de las tablas de autorreferencia, o son A-OK?

NimChimpsky
fuente

Respuestas:

40

No hay nada malo con las tablas de autorreferencia.

Es el patrón de diseño de base de datos común para jerarquías profundamente anidadas (¿infinito?).

Oded
fuente
@NimChimpsky: al igual que el concepto de recursión, esta idea es difícil para algunos.
Oded
2
(Al menos) Oracle incluso tiene una construcción SQL especial, la cláusula "COMENZAR CON - CONECTAR POR", para tratar con tablas de autorreferencia.
user281377
1
@ user281377 - Y SQL Server introdujo el hierarchyidtipo.
Finalizado el
use hibernate para que tenga su propia salsa especial
NimChimpsky
44
@NimChimpsky: considere mirar el "Modelo de conjunto anidado" como una alternativa a esa columna "parent_id" también: proporciona la misma funcionalidad, pero un mejor rendimiento y consultas más fáciles para extraer las jerarquías. en.wikipedia.org/wiki/Nested_set_model La serie de libros de Joe Celko "SQL For Smarties" tiene una excelente muestra de SQL sobre conjuntos anidados.
Keith Palmer Jr.
7

Nigromancia
La respuesta correcta es: depende de qué motor de base de datos y de qué herramienta de administración.

Pongamos un ejemplo:
tenemos una tabla de informes,
y un informe puede tener un padre (punto de menú, como categoría),
y ese padre puede tener un padre (por ejemplo, centro de beneficio),
y así hasta el infinito.

El ejemplo más simple de una relación recursiva estándar, como con cualquier entidad / jerarquía autorreferenciada.

La tabla resultante de SQL Server es:

IF  EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'dbo.FK_T_FMS_Reports_T_FMS_Reports') AND parent_object_id = OBJECT_ID(N'dbo.T_FMS_Reports'))
ALTER TABLE dbo.T_FMS_Reports DROP CONSTRAINT FK_T_FMS_Reports_T_FMS_Reports
GO

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'dbo.T_FMS_Reports') AND type in (N'U'))
DROP TABLE dbo.T_FMS_Reports 
GO



CREATE TABLE dbo.T_FMS_Reports 
( 
     RE_UID uniqueidentifier NOT NULL 
    ,RE_RE_UID uniqueidentifier NULL 
    ,RE_Text nvarchar(255) NULL 
    ,RE_Link nvarchar(400) NULL 
    ,RE_Sort int NOT NULL 
    ,RE_Status int NOT NULL 
    ,PRIMARY KEY CLUSTERED ( RE_UID ) 
); 

GO

ALTER TABLE dbo.T_FMS_Reports  WITH CHECK ADD  CONSTRAINT FK_T_FMS_Reports_T_FMS_Reports FOREIGN KEY(RE_RE_UID) 
REFERENCES dbo.T_FMS_Reports (RE_UID) 
-- ON DELETE CASCADE -- here, MS-SQL has a problem 
GO

ALTER TABLE dbo.T_FMS_Reports CHECK CONSTRAINT FK_T_FMS_Reports_T_FMS_Reports 
GO

Pero tiene un problema:
cuando necesita eliminar un punto de menú con todos sus submenús, NO PUEDE establecer delete-cascade, porque Microsoft SQL-Server no admite eliminaciones en cascada recursivas (por otro lado, PostGreSQL sí [pero solo si el gráfico no es cíclico], mientras que a MySQL no le gusta este tipo de estructura de tabla, ya que no admite CTE recursivos).

Por lo tanto, puede explotar la integridad / funcionalidad de eliminación con él, por lo que es obligatorio implementar dicha funcionalidad en su propio código o en un procedimiento almacenado (si su RDBMS admite procedimientos almacenados).

Sin duda, esto hará explotar cualquier tipo de importación / exportación de datos dinámicos completamente automática, porque no puede simplemente ejecutar una declaración de eliminación para todas las tablas de acuerdo con relaciones de clave externa (sin autorreferencia), ni puede hacer una selección simple * y crea una inserción para cada fila en un orden arbitrario.

Por ejemplo, cuando crea una secuencia de comandos INSERT usando SSMS, entonces SSMS no obtendrá la clave foránea y, por lo tanto, creará declaraciones de inserción que insertarán entradas con dependencias, antes de insertar el elemento primario de la dependencia, que fallará con un error , porque la clave externa está en su lugar.

Sin embargo, en sistemas de gestión de bases de datos adecuados (como PostgreSQL), con herramientas adecuadas, esto no debería ser un problema. Simplemente entienda que solo porque paga mucho por su RDBMS (lo estoy mirando, Microsoft; Oracle =?), Y / o su cinturón de herramientas, no significa que esté programado correctamente. Y tampoco OpenSource (por ejemplo, MySQL) lo hace inmune a tan maravillosas minucias.

El diablo está en los detalles, como dice el viejo dicho.

Ahora, no es que no pueda solucionar tales problemas, pero realmente no lo recomendaría, si su sistema va a ser complejo (por ejemplo, más de 200 tablas).
Además, en un entorno comercial habitual (como lo retrata Dilbert), simplemente no se le dará ese tiempo.

Un enfoque mucho mejor, aunque más difícil, sería una mesa de cierre.
Eso tendría la ventaja adicional de que también funciona en MySQL.
Una vez que implemente la funcionalidad de cierre una vez, la tendrá trabajando en lugares adicionales en muy poco tiempo.

Dilema
fuente
3
+1 en llamar la atención sobre las tablas de cierre (al menos la terminología, ya conocía el concepto). Aquí hay un buen artículo para otros que puedan estar interesados. coderwall.com/p/lixing/closure-tables-for-browsing-trees-in-sql
Outfast Source