2008-12-01 21 views
9

Digamos que tengo una tabla que representa una super clase, estudiantes. Y luego tengo N tablas que representan subclases de ese objeto (atletas, músicos, etc.). ¿Cómo puedo expresar una restricción tal que un estudiante debe ser modelado en una subclase (no más, no menos)?Mantenimiento de la integridad de la subclase en una base de datos relacional

Precisiones sobre los comentarios:

  • Esto se mantiene de forma manual, no a través de un paquete ORM.
  • El proyecto se relaciona con el servidor SQL (pero sería bueno ver una solución genérica)
  • Este puede no haber sido el mejor ejemplo. Hay un par de escenarios que podemos considerar con respecto a la creación de subclases, y acabo de inventar este ejemplo de estudiante/atleta.

A) En la verdadera orientación a los objetos, es posible que la superclase pueda existir por sí misma y no necesite ser modelada en ninguna subclase.

B) En la vida real, cualquier objeto o estudiante puede tener múltiples roles.

C) El escenario particular que estaba tratando de ilustrar requería que cada objeto se implementara en exactamente una subclase. Piense en la superclase como una implementación abstracta, o solo en elementos comunes tomados de clases/instancias de objetos por lo demás dispares.

Gracias a todos por su colaboración, especialmente a Bill.

+0

Solo para una aclaración: ¿está gestionando esto manualmente o utilizando una solución ORM como hibernar? – Uri

+0

¿Qué base de datos está usando? Si está utilizando PostgreSQL, tiene herencia de tablas disponible. – Elijah

+0

Tengo curiosidad, ¿Por qué no puede ser astudent tanto un atleta como un nusician? –

Respuesta

2

Aquí hay un par de posibilidades. Uno es CHECK en cada tabla que student_id no aparece en ninguna de las otras tablas de subtipos hermanos. Esto probablemente sea costoso y cada vez que necesite un subtipo nuevo, deberá modificar la restricción en todas las tablas existentes.

CREATE TABLE athletes (
    student_id INT NOT NULL PRIMARY KEY, 
    FOREIGN KEY (student_id) REFERENCES students(student_id), 
    CHECK (student_id NOT IN (SELECT student_id FROM musicians 
         UNION SELECT student_id FROM slackers 
         UNION ...)) 
); 

edición: @JackPDouglas señala correctamente que la forma anterior de la restricción CHECK no es compatible con Microsoft SQL Server. De hecho, tampoco es válido según el estándar SQL-99 hacer referencia a otra tabla (consulte http://kb.askmonty.org/v/constraint_type-check-constraint).

SQL-99 define un objeto de metadatos para las restricciones de varias tablas. Esto se llama ASSERTION, sin embargo, no conozco ningún RDBMS que implemente aserciones.

Probablemente una mejor manera es hacer que la clave principal en la tabla students sea una clave primaria compuesta, la segunda columna denota un subtipo. Luego restrinja esa columna en cada tabla secundaria a un valor único correspondiente al subtipo representado por la tabla. editar: no es necesario hacer que PK sea una clave compuesta en tablas secundarias.

CREATE TABLE athletes (
    student_id INT NOT NULL PRIMARY KEY, 
    student_type CHAR(4) NOT NULL CHECK (student_type = 'ATHL'), 
    FOREIGN KEY (student_id, student_type) REFERENCES students(student_id, student_type) 
); 

Por supuesto student_type podría fácilmente ser un entero, sólo estoy mostrando como un char con fines ilustrativos.

Si no tiene soporte para las restricciones CHECK (por ejemplo, MySQL), entonces puede hacer algo similar en un desencadenador.

He leído su seguimiento para asegurarse de que existe una fila en alguna tabla de subclase para cada fila en la tabla de superclase. No creo que haya una forma práctica de hacer esto con los metadatos y las restricciones de SQL. La única opción que puedo sugerir para cumplir este requisito es usar Single-Table Inheritance. De lo contrario, debe confiar en el código de la aplicación para aplicarlo.

editar: JackPDouglas también sugiere utilizar un diseño basado en Class Table Inheritance. Vea his example o mis ejemplos de la técnica similar here o here o here.

+1

-1 para obtener información engañosa sobre las restricciones de verificación. De los [documentos de SQL Server 2008R2] (http://msdn.microsoft.com/en-us/library/ms174979 (v = SQL.105).aspx): "La condición de búsqueda debe evaluar a una expresión booleana y no puede hacer referencia a otra tabla". –

+0

No recibo ningún crédito por recomendar algo diferente posteriormente? Bueno, de todos modos, gracias por explicar tu voto negativo, la mayoría de la gente no hace eso. –

+0

Si su solución inicial * había * funcionado, hubiera sido una opción atractiva. Creo que Jack se sintió atraído inicialmente por su primera solución, solo para sentirse decepcionado de haber perdido su tiempo, ya que parece no estar respaldado. +1 para editar su respuesta :-) –

0

problema interesante. Por supuesto, las restricciones FK están ahí para las subtablas, por lo que tiene que haber un alumno para ellas.

El principal problema es tratar de verificar a medida que se inserta. El estudiante debe insertarse primero para que no viole una restricción FK en una subtabla, por lo que un desencadenante que haga una verificación no funcionaría.

Puede escribir una aplicación que compruebe de vez en cuando si realmente le preocupa esto. Creo que el mayor temor sería la eliminación. Alguien podría eliminar una entrada de subtabla pero no el alumno. Podría tener factores desencadenantes para verificar cuándo se eliminan los elementos de las subtablas ya que ese es probablemente el mayor problema.

Tengo una base de datos con una tabla por jerarquía de subclases así también. Utilizo Hibernate y su mapa correctamente para que elimine todo automáticamente. Si hago esto por 'mano', entonces me aseguraré de eliminar siempre al padre con las cascadas correctas jeje :)

0

Gracias, Bill. Me pones a pensar ...

La tabla de superclase tiene una columna de código de subclase. Cada una de las tablas de subclase tiene una restricción de clave externa, así como una que dicta que la identificación existe con un subconjunto de la tabla de superclase (donde código = atleta).

La única parte que falta aquí es que es posible modelar una superclase sin una subclase. Incluso si hace que la columna del código sea obligatoria, podría ser una unión vacía. Esto se puede solucionar agregando una restricción de que los identificadores de la superclase existen en una unión de los identificadores en las tablas de las subclases. La inserción se pone un poco complicada con estas dos limitaciones si se imponen restricciones en el medio de las transacciones. Eso o simplemente no te preocupes por objetos no clasificados.

Editar: Bleh, una idea tan buena ... Pero obstaculizada por el hecho de que las subconsultas que se refieren a otras tablas no son compatibles. Al menos no en SQL Server.

0

que puede solucionarse mediante la adición de una restricción que existen ids de la superclase en una unión de los identificadores de las tablas de subclase.

Según la inteligencia que desee poner en su esquema (y la cantidad de MS SQL Server le permite colocar allí), en realidad no tendría que hacer una unión de las tablas de las subclases, ya que sabe , si el id existe en cualquier tabla de subclase, debe existir en la misma subclase que la identificada por la columna de código de subclase.

+0

(a) no puede consultar otra tabla en una restricción en MS SQL Server, y (b) no puede consultar una tabla cuyo nombre depende de un valor de datos. –

3

Cada registro de Estudiante tendrá una columna de Subclase (suponga que es un CHAR (1)). {A = Atleta, M = músico ...}

Ahora crea tus tablas de Atleta y Músico.También deben tener una columna SubClass, pero debe haber una restricción de verificación que codifique el valor para el tipo de tabla que representan. Por ejemplo, debe poner un valor predeterminado de 'A' y una restricción CHECK de 'A' para la columna SubClass en la tabla de atletas.

Vincule sus tablas de Músico y Atleta a la tabla de Estudiantes utilizando una clave foránea COMPUESTA de StudentID Y Subclase. ¡Y tu estas listo! Ve a disfrutar de una buena taza de café.

CREATE TABLE Student (
    StudentID INT NOT NULL IDENTITY PRIMARY KEY, 
    SubClass CHAR(1) NOT NULL, 
    Name VARCHAR(200) NOT NULL, 
    CONSTRAINT UQ_Student UNIQUE (StudentID, SubClass) 
); 

CREATE TABLE Athlete (
    StudentID INT NOT NULL PRIMARY KEY, 
    SubClass CHAR(1) NOT NULL, 
    Sport VARCHAR(200) NOT NULL, 
    CONSTRAINT CHK_Jock CHECK (SubClass = 'A'), 
    CONSTRAINT FK_Student_Athlete FOREIGN KEY (StudentID, Subclass) REFERENCES Student(StudentID, Subclass) 
); 

CREATE TABLE Musician (
    StudentID INT NOT NULL PRIMARY KEY, 
    SubClass CHAR(1) NOT NULL, 
    Instrument VARCHAR(200) NOT NULL, 
    CONSTRAINT CHK_Band_Nerd CHECK (SubClass = 'M'), 
    CONSTRAINT FK_Student_Musician FOREIGN KEY (StudentID, Subclass) REFERENCES Student(StudentID, Subclass) 
); 
+0

+1. Esto realmente funciona. – BobbyShaftoe

1

Si usted está interesado en el modelado de datos, además de modelado de objetos, le sugiero que mirar hacia arriba "relacional especialización modelado generalización" en la web.

Solía ​​haber algunos buenos recursos que explican este tipo de patrón bastante bien.

Espero que esos recursos todavía estén allí.

Aquí hay una vista simplificada de lo que espero que encuentres.

Antes de comenzar a diseñar una base de datos, es útil crear un modelo conceptual de datos que conecte los valores almacenados en la base de datos al tema. Hacer un modelo conceptual de datos es realmente análisis de datos, no diseño de bases de datos. A veces es difícil mantener el análisis y el diseño separados.

Una forma de modelar los datos en el nivel conceptual es el modelo Entidad-Relación (ER). Existen patrones bien conocidos para modelar la situación de especialización-generalización. La conversión de esos patrones de ER a tablas de SQL (llamado diseño lógico) es bastante sencilla, aunque hay que hacer algunas elecciones de diseño.

El caso que usted dio de un estudiante que posiblemente tenga varios roles como músico probablemente no ilustra el caso que le interesa, si lo leí bien. Parece que le interesa el caso en que las subclases son mutuamente excluyentes. Tal vez el caso donde un vehículo podría ser un auto, un camión o una motocicleta podría ser más fácil de discutir.

Una diferencia que es probable que encuentre es que la tabla general para la superclase realmente no necesita la columna de código de tipo. El tipo de una única instancia de superclase puede derivarse por la presencia o ausencia de claves externas en las diversas tablas de subclase. Si es más inteligente incluir u omitir el código de tipo depende de cómo pretenda utilizar los datos.

0

Agregaría una restricción de verificación posiblemente.
Crea ForeignKeys como Nullable. Agregue una verificación para asegurarse de que no sean ambas nulas y para asegurarse de que no estén ambas configuradas. CONSTRAINT [CK_HasOneForiegnKey] CHECK ((FK_First! = NULL O FK_Second! = NULL) Y NO (FK_First! = NULL AND FK_Second! = NULL)).

No estoy seguro, pero creo que esto le permitirá establecer solo una clave a la vez.

Cuestiones relacionadas