2009-01-23 12 views
6

Tengo una tabla SQL, así:¿Cuál es la mejor manera de representar una relación muchos a muchos entre registros en una sola tabla SQL?

Actualización: Estoy cambiando la tabla de ejemplo como la naturaleza existente jerárquica de los datos originales (estado, ciudades, escuelas) se cubrían el hecho de que se necesita una relación simple entre Los artículos.

entities 
id  name    
1  Apple  
2  Orange    
3  Banana    
4  Carrot     
5  Mushroom   

quiero definir las relaciones bidireccionales entre estas entidades por lo que un usuario que está viendo una entidad puede ver una lista de todas las entidades relacionadas.

Las relaciones las define un usuario final.

¿Cuál es la mejor manera de representar estas relaciones en la base de datos y, posteriormente, consultarlas y actualizarlas?

Una forma como yo lo veo ...

Mi instinto dice que una tabla de relación de este modo:

entity_entity 
entity_id_a  entity_id_b 
1     2 
5     1 
4     1 
5     4 
1     3 

Siendo ese el caso, le da un entity_id suministrado de 4, ¿cómo se obtener todos los registros relacionados, que serían 1 y 5?

Del mismo modo una consulta de entity_id = 1 debe volver 2, 3, 4, y 5.

Gracias por su tiempo y quiero saber si puedo aclarar la cuestión en absoluto.

Respuesta

11

Defina una restricción: entity_id_a < entity_id_b.

crear índices:

CREATE UNIQUE INDEX ix_a_b ON entity_entity(entity_id_a, entity_id_b); 
CREATE INDEX ix_b ON entity_entity(entity_id_b); 

segundo índice no necesita incluir entity_id_a como lo va a usar sólo para seleccionar todos los a 's dentro de un b. RANGE SCAN en ix_b será más rápido que SKIP SCAN en ix_a_b.

rellenar la tabla con sus entidades de la siguiente manera:

INSERT 
INTO entity_entity (entity_id_a, entity_id_b) 
VALUES (LEAST(@id1, @id2), GREATEST(@id1, @id2)) 

A continuación, seleccione:

SELECT entity_id_b 
FROM entity_entity 
WHERE entity_id_a = @id 
UNION ALL 
SELECT entity_id_a 
FROM entity_entity 
WHERE entity_id_b = @id 

UNION ALL aquí le permite utilizar por encima de los índices y evitar la clasificación adicional para la unicidad.

Todo lo anterior es válido para una relación simétrica y antirreflectante.Eso significa que:

  • Si un se relaciona con b, entonces b se relaciona con un

  • un no está relacionado con un

+0

Este enfoque está funcionando muy bien en la práctica. Gracias por su amabilidad. – GloryFish

1

Creo que la estructura que ha sugerido está bien.

para obtener los registros relacionados hacer algo como

SELECT related.* FROM entities AS search 
LEFT JOIN entity_entity map ON map.entity_id_a = search.id 
LEFT JOIN entities AS related ON map.entity_id_b = related.id 
WHERE search.name = 'Search term' 

Espero que ayude.

+0

Qué pasa si mi término de búsqueda coincide con una entidad cuya identificación se produce sólo en entity_id_b en ¿el mapa? –

+0

En otras palabras, su consulta solo funciona si cada relación se almacena dos veces, al revés. P.ej. (1,4) y (4,1). –

0
select * from entities 
where entity_id in 
(
    select entity_id_b 
    from entity_entity 
    where entity_id_a = @lookup_value 
) 
0

Puedo pensar en algunas maneras.

Una sola pasada con un caso:

SELECT DISTINCT 
    CASE 
     WHEN entity_id_a <> @entity_id THEN entity_id_a 
     WHEN entity_id_b <> @entity_id THEN entity_id_b 
    END AS equivalent_entity 
FROM entity_entity 
WHERE entity_id_a = @entity_id OR entity_id_b = @entity_id 

o dos consultas filtradas unioned así:

SELECT entity_id_b AS equivalent_entity 
FROM entity_entity 
WHERE entity_id_a = @entity_id 
UNION 
SELECT entity_id_a AS equivalent_entity 
FROM entity_entity 
WHERE entity_id_b = @entity_id 
1

El enfoque tabla de vínculo parece estar bien, excepto que es posible que desee un "tipo de relación de por lo que sabes POR QUÉ están relacionados.

Por ejemplo, la relación entre Raleigh y Carolina del Norte no es lo mismo que una relación entre Raleigh y Durham. Además, es posible que desee saber quién es el "padre" en la relación, en caso de que estuviera manejando menús desplegables condicionales. (es decir, si selecciona un estado, puede ver las ciudades que se encuentran en el estado).

Dependiendo de la complejidad de sus requisitos, la configuración simple que tiene en este momento puede no ser suficiente. Si solo necesita mostrar que dos registros están relacionados de alguna manera, la tabla de enlaces debería ser suficiente.

+0

Veo a qué te refieres. En este caso, no estamos representando específicamente una jerarquía. Solo habrá un estado en este sistema y las relaciones no se usarán para una navegación de estilo detallado. – GloryFish

1

Ya he publicado una forma de hacerlo en su diseño, pero también quería ofrecer esta visión de diseño por separado si tiene cierta flexibilidad en su diseño y esto se ajusta más a sus necesidades.

Si los elementos están en clases de equivalencia (no superpuestas), es posible que desee hacer que las clases de equivalencia sean la base para el diseño de la tabla, donde todo en clase se considera equivalente. Las clases mismas pueden ser anónimas:

CREATE TABLE equivalence_class (
    class_id int -- surrogate, IDENTITY, autonumber, etc. 
    ,entity_id int 
) 

entity_id debe ser único para una partición que no se solapan de su espacio.

Esto evita el problema de garantizar la correcta diestra con la mano izquierda o derecha o forzar una matriz de relación superior derecha.

Luego, su consulta es un poco diferente:

SELECT c2.entity_id 
FROM equivalence_class c1 
INNER JOIN equivalence_class c2 
    ON c1.entity_id = @entity_id 
    AND c1.class_id = c2.class_id 
    AND c2.entity_id <> @entity_id 

o, equivalentemente:

SELECT c2.entity_id 
FROM equivalence_class c1 
INNER JOIN equivalence_class c2 
    ON c1.entity_id = @entity_id 
    AND c1.class_id = c2.class_id 
    AND c2.entity_id <> c1.entity_id 
+0

¡Agradable! También puede probar c2.entity_id <> c1.entity_id, en lugar de c2.entity_id <> @entity_id. De esta forma, no tiene que pasar el parámetro @entity_id dos veces. –

+0

Supuse que sería un procedimiento almacenado, pero sí, eso sería equivalente para los devotos de consultas ad hoc con parámetros. –

-1

Mi consejo es que su diseño de la tabla intial es malo. No almacene diferentes tipos de cosas en la misma tabla. (Primera regla de diseño de la base de datos, justo al lado con no almacenar varias piezas de información en el mismo campo). Esto es mucho más difícil de consultar y causará problemas de rendimiento significativos en el futuro. Además, sería un problema ingresar los datos en la tabla de relaciones de negocios. ¿Cómo sabe qué entidades necesitarían crearse cuando realice una nueva entrada? Sería mucho mejor diseñar tablas relacionales adecuadas. Las tablas de entidades son casi siempre una mala idea.No veo ninguna razón en absoluto del ejemplo para tener este tipo de información en una sola tabla. Francamente, tendría una mesa universitaria y una tabla de direcciones relacionadas. Sería fácil de consultar y funcionar mucho mejor.

0

Sobre la base de su esquema actualizado esta consulta debería funcionar:

select if(entity_id_a=:entity_id,entity_id_b,entity_id_a) as related_entity_id where :entity_id in (entity_id_a, entity_id_b) 

donde: entity_id está ligado a la entidad que está consultando

Cuestiones relacionadas