2010-07-16 26 views
13

Tengo dos tablas¿Cuál es la forma más eficiente de almacenar una matriz de enteros en una columna MySQL?

A:

plant_ID | name. 
1  | tree 
2  | shrubbery 
20  | notashrubbery 

B:

area_ID | name | plants 
1  | forrest | *needhelphere* 

ahora quiero la zona para almacenar cualquier número de plantas, en un orden específico y algunas plantas podrían aparece varias veces: por ejemplo, 2,20,1,2,2,20,1

¿Cuál es la forma más eficiente de almacenar esta variedad de plantas?
Teniendo en cuenta que tengo que hacerlo de modo que si realizo una búsqueda para encontrar áreas con la planta 2, no obtengo áreas que son, por ejemplo, 1,20,232,12,20 (pad con 0 principales?) Qué sería la consulta para eso?

si sirve de ayuda, supongamos que tengo una base de datos de no más de 99999999 plantas diferentes. Y sí, esta pregunta no tiene nada que ver con las plantas ....

Pregunta extra ¿Es hora de alejarse de MySQL? ¿Hay una mejor base de datos para gestionar esto?

+2

Si solo MySQL nos permite tener múltiples valores para una sola columna ... – animuson

+6

Cada vez que sienta la necesidad de combinar datos en un solo campo, patee: está violando la forma normal y necesita agregar otra tabla para manejar la situación apropiadamente (Consulte las respuestas de Matchu y mgrove a continuación.) –

+0

si le interesa una forma no relacional de almacenar estos datos (es decir, una colección/matriz real), busque en un OODBMS como db4o. –

Respuesta

25

Si va a buscar tanto por bosque como por planta, parece que se beneficiaría de una relación de muchos a muchos. Elimine su columna plants y cree una tabla areas_plants completamente nueva (o como quiera llamarla) para relacionar las dos tablas.

Si el área 1 tiene las plantas 1 y 2, y la zona 2 tiene las plantas 2 y 3, la mesa areas_plants se vería así:

area_id | plant_id | sort_idx 
----------------------------- 
     1 |  1 |  0 
     1 |  2 |  1 
     2 |  2 |  0 
     2 |  3 |  1 

A continuación, puede buscar relaciones de ambos lados, y el uso sencillo SE UNE para obtener los datos relevantes de cualquiera de las tablas. No es necesario ensuciar en condiciones SIMILARES para descubrir si está en la lista, bla, bla, puaj. He estado allí para una base de datos heredada. No es divertido. Use SQL a su máximo potencial.

+6

Esto (presentar una tabla de combinación) es la solución correcta al problema. Lo único que agregaría es una columna sort_idx para retener el orden de la planta como se solicita en la pregunta. –

+0

@Paul Sasik - bien visto :) No se molestará en editar, ya que usted ya tiene la respuesta allí. – Matchu

+1

seguí y agregué la columna. Ayuda a errar en el lado pedante de los noobs de diseño db, creo. –

3

¿Sabía que esto viola la forma normal?

Normalmente, uno tendría una tabla de plantas de área: area_ID, plant_ID con una restricción única en las dos y claves foráneas en las otras dos tablas. Esta tabla de "enlaces" es lo que le proporciona muchas relaciones o muchas a la misma.

Las consultas sobre esto son generalmente muy eficientes, utilizan índices y no requieren cadenas de análisis.

+0

+1: Pero la clave primaria compuesta> restricción única compuesta –

+0

@OMG Potros, sí, pero a veces las personas quieren una clave sustituta en esa tabla. –

+0

Las mismas personas como ORM y Miley Cyrus ... –

2

Sus atributos de relación deben ser atómicos, no constituidos por valores múltiples como listas. Es muy difícil buscarlos. Necesita una nueva relación que asigne las plantas al area_ID y la combinación area_ID/plant sea la clave principal.

7

¿Qué tal esto:

mesa: plantas

plant_ID | name 
1  | tree 
2  | shrubbery 
20  | notashrubbery 

mesa: áreas

area_ID | name 
1  | forest 

tabla: area_plant_map

area_ID | plant_ID | sequence 
1  | 1  | 0 
1  | 2  | 1 
1  | 20  | 2 

Esa es la forma normalizada estándar para hacerlo (con una tabla de mapeo).

Para encontrar todas las áreas con arbustos (planta 2), hacer esto:

SELECT * 
FROM areas 
INNER JOIN area_plant_map ON areas.area_ID = area_plant_map.area_ID 
WHERE plant_ID = 2 
+3

¡Leí "shubbery" y ahora tengo a los Caballeros de Nee! skit corriendo por mi cabeza ... –

+0

Ver pregunta - Área 1 podría tener esta secuencia de plantas: 2,20,1,2,2,20,1 (por lo que el área 1 tiene 3 arbustos en la posición 0,3 y 4) – Moak

+0

@Moak en ese caso, simplemente coloca otra columna con un número de secuencia. –

2

Uso muchos-a-muchos relación:

CREATE TABLE plant (
    plant_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, 
    name VARCHAR(255) 
) ENGINE=INNODB; 

CREATE TABLE area (
    area_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, 
    name VARCHAR(255) 
) ENGINE=INNODB; 

CREATE TABLE plant_area_xref (
    plant_id INT NOT NULL, 
    area_id INT NOT NULL, 
    sort_idx INT NOT NULL, 
    FOREIGN KEY (plant_id) REFERENCES plant(plant_id) ON DELETE CASCADE, 
    FOREIGN KEY (area_id) REFERENCES area(area_id) ON DELETE CASCADE, 
    PRIMARY KEY (plant_id, area_id, sort_idx)  
) ENGINE=INNODB; 

EDIT:

Sólo para respuesta su pregunta de bonificación:

Bonus Question Is it time to step away from MySQL? Is there a better DB to manage this? 

Esto no tiene nada que ver con MySQL. Este fue solo un problema con el mal diseño de la base de datos. Debe usar tablas de intersección y una relación muchos a muchos para casos como este en cada RDBMS (MySQL, Oracle, MSSQL, PostgreSQL, etc.).

+0

¿No pierde esto el sort_idx? 'plant_id, area_id' no es único,' area_id/sort_idx' sería. Necesito poder mantener esta secuencia intacta 'area: 1' ->' plants: 2,20,1,2,2,20,1' (en ese orden) – Moak

+0

Se agregó sort_idx. plant_id, area_id no necesitan ser únicos. La unicidad se logra con la doble clave primaria. –

+0

sí, pero mi punto es que Área 1 - Planta 2 podría ocurrir doble (la misma planta dos veces en el área, por lo que la singularidad real es area/sort_index. Obtuve lo que necesitaba (doble primaria) si la cambia entonces Puedo aceptar la respuesta. – Moak

Cuestiones relacionadas