¿Cómo almacenar matrices en MySQL?

118

Tengo dos tablas en MySQL. Table Person tiene las siguientes columnas:

id | name | fruits

La fruitscolumna puede contener un valor nulo o una matriz de cadenas como ('manzana', 'naranja', 'plátano') o ('fresa'), etc. La segunda tabla es Table Fruit y tiene las siguientes tres columnas:

____________________________
fruit_name | color  | price
____________________________
apple      | red    | 2
____________________________
orange     | orange | 3
____________________________
...,...

Entonces, ¿cómo debo diseñar la fruitscolumna en la primera tabla para que pueda contener una matriz de cadenas que toman valores de la fruit_namecolumna en la segunda tabla? Dado que no hay un tipo de datos de matriz en MySQL, ¿cómo debo hacerlo?

tonga
fuente
1
¿Qué tal si lo agregas como entradas separadas, naranja, 2, 1, rosa, 2, 1, etc. y luego puedes usar consultas para tratarlas como si fueran matrices?
Sai
@JanusTroelsen: No estoy usando PHP para leer / escribir DB. Entonces, ¿hay una forma universal de hacerlo?
Tonga
1
@tonga mira mi violín, ¿es eso lo que quieres?
echo_Me

Respuestas:

163

La forma correcta de hacer esto es utilizar varias tablas y JOINellas en sus consultas.

Por ejemplo:

CREATE TABLE person (
`id` INT NOT NULL PRIMARY KEY,
`name` VARCHAR(50)
);

CREATE TABLE fruits (
`fruit_name` VARCHAR(20) NOT NULL PRIMARY KEY,
`color` VARCHAR(20),
`price` INT
);

CREATE TABLE person_fruit (
`person_id` INT NOT NULL,
`fruit_name` VARCHAR(20) NOT NULL,
PRIMARY KEY(`person_id`, `fruit_name`)
);

La person_fruittabla contiene una fila para cada fruta con la que una persona está asociada y enlaza efectivamente las tablas persony fruitsjuntas, IE

1 | "banana"
1 | "apple"
1 | "orange"
2 | "straberry"
2 | "banana"
2 | "apple"

Cuando desee recuperar a una persona y toda su fruta, puede hacer algo como esto:

SELECT p.*, f.*
FROM person p
INNER JOIN person_fruit pf
ON pf.person_id = p.id
INNER JOIN fruits f
ON f.fruit_name = pf.fruit_name
Lobo malo
fuente
4
La tercera tabla es la tabla de enlace entre Persona y Fruta. Entonces, si una persona tiene 100 frutas. Necesito crear 100 filas en la tercera tabla, ¿verdad? ¿Es esto eficiente?
Tonga
1
@tonga Exactamente, cada una de las 100 filas tendría el mismo person_idpero diferente fruit_name. Esta es efectivamente una implementación de la teoría de la respuesta de Janus.
Bad Wolf
1
¿Es siempre cierto que cualquier relación entre dos tablas debe almacenarse en la tercera tabla? ¿Puedo hacer una consulta para encontrar la relación simplemente almacenando las claves primarias de dos tablas?
Tonga
2
Sí, así es como está configurado el ejemplo ahora. Cualquier información sobre la persona debe estar en la personmesa, cualquier información sobre la fruta en la fruitsmesa y cualquier información específicamente sobre la relación entre una persona en particular y una fruta en particular en la person_fruitmesa. Debido a que en este ejemplo no hay información adicional, la person_fruittabla tiene solo dos columnas, las claves principales de las tablas persony fruits. person_fruitSin embargo, la cantidad de una fruta específica es un ejemplo de otra cosa que podría ir en la mesa.
Bad Wolf
2
¿No sería mejor usar una INTpara una clave fruitsy solo tener esto INTadentro person_fruit? Por lo tanto, el nombre se puede cambiar más tarde y también necesitaría menos espacio si no tiene muchas más filas dentro fruitsque dentro person_fruit.
12431234123412341234123
58

La razón por la que no hay matrices en SQL es porque la mayoría de las personas no las necesitan. Las bases de datos relacionales (SQL es exactamente eso) funcionan usando relaciones, y la mayoría de las veces, es mejor si asigna una fila de una tabla a cada "bit de información". Por ejemplo, cuando piense "Me gustaría una lista de cosas aquí", cree una nueva tabla, vinculando la fila de una tabla con la fila de otra tabla. [1] De esa forma, puede representar relaciones M: N. Otra ventaja es que esos enlaces no abarrotarán la fila que contiene el elemento vinculado. Y la base de datos puede indexar esas filas. Normalmente, las matrices no están indexadas.

Si no necesita bases de datos relacionales, puede utilizar, por ejemplo, un almacén de valores clave.

Lea sobre la normalización de la base de datos , por favor. La regla de oro es "[Cada] [atributo] que no sea clave debe proporcionar un hecho sobre la clave, la clave completa y nada más que la clave". Una matriz hace demasiado. Tiene múltiples hechos y almacena el orden (que no está relacionado con la relación en sí). Y el rendimiento es pobre (ver arriba).

Imagina que tienes una mesa de personas y tienes una mesa con llamadas telefónicas de personas. Ahora puede hacer que cada fila de persona tenga una lista de sus llamadas telefónicas. Pero cada persona tiene muchas otras relaciones con muchas otras cosas. ¿Eso significa que mi tabla de persona debe contener una matriz para cada cosa a la que está conectado? No, eso no es un atributo de la persona en sí.

[1]: ¡Está bien si la tabla de enlace solo tiene dos columnas (las claves primarias de cada tabla)! Sin embargo, si la relación en sí tiene atributos adicionales, deben representarse en esta tabla como columnas.

Janus Troelsen
fuente
2
Gracias Janus. Eso tiene sentido. Ahora entiendo por qué MySQL no admite el tipo de matriz en una columna.
Tonga
2
@Sai - Para las cosas que estoy haciendo, ¿realmente necesito la solución NoSQL?
Tonga
1
Bien, entonces, si tengo una tabla en la que un campo contiene una matriz numérica de miles de elementos, por ejemplo, algunos datos 2D recopilados de un sensor, ¿es mucho mejor usar NoSQL DB?
Tonga
5
@tonga: la cantidad de datos no determina el tipo de base de datos que se utilizará, sino la naturaleza de los datos. Si no hay relaciones, no necesita una base de datos relacional. Pero dado que este es el estándar de la industria, puede mantenerlo y no usar las funciones relacionales. ¡La mayoría de los datos son relacionales de alguna manera! Una razón común para desnormalizar bases de datos relacionales o usar almacenes de valores clave es por razones de rendimiento. ¡Pero esos problemas solo surgen una vez que tienes MILLONES de filas! ¡No optimices prematuramente! Recomiendo ir solo con una base de datos SQL (recomiendo PostgreSQL). Si tiene problemas, pregunte.
Janus Troelsen
2
PostgreSQL también tiene almacenes de valores clave incorporados, lo que significa que sería aún más fácil alejarse del modelo relacional si no le queda bien.
Janus Troelsen
50

MySQL 5.7 ahora proporciona un tipo de datos JSON . Este nuevo tipo de datos proporciona una nueva forma conveniente de almacenar datos complejos: listas, diccionarios, etc.

Dicho esto, los rrays no mapean bien las bases de datos, por lo que los mapas relacionales de objetos pueden ser bastante complejos. Históricamente, las personas han almacenado listas / matrices en MySQL creando una tabla que las describe y agregando cada valor como su propio registro. La tabla puede tener solo 2 o 3 columnas, o puede contener muchas más. La forma de almacenar este tipo de datos realmente depende de las características de los datos.

Por ejemplo, ¿la lista contiene un número de entradas estático o dinámico? ¿La lista seguirá siendo pequeña o se espera que crezca a millones de registros? ¿Habrá muchas lecturas en esta mesa? ¿Muchas escrituras? ¿Muchas actualizaciones? Todos estos son factores que deben tenerse en cuenta al decidir cómo almacenar colecciones de datos.

Además, los almacenes de datos clave: valor / almacenes de documentos como Cassandra, MongoDB, Redis, etc. también proporcionan una buena solución. Solo tenga en cuenta dónde se almacenan realmente los datos (si se almacenan en el disco o en la memoria). No es necesario que todos sus datos estén en la misma base de datos. Algunos datos no se asignan bien a una base de datos relacional y es posible que tenga motivos para almacenarlos en otro lugar, o puede que desee utilizar una clave en la memoria: base de datos de valores como caché en caliente para los datos almacenados en el disco en algún lugar o como un almacenamiento efímero para cosas como sesiones.

Charles Addis
fuente
42

Una nota al margen a considerar, puede almacenar matrices en Postgres.

Eric Grotke
fuente
6
Nota adicional: se pueden indexar, por lo que las consultas que comprueban la existencia de valores específicos en una matriz pueden ser muy rápidas. Lo mismo ocurre con los tipos JSON complejos.
timetofly
5
Esto no responde a la pregunta de ninguna manera. OP preguntó sobre MySQL.
jhpratt
1
Si usa ArrayField en Postgres y tiene una lista exhaustiva de valores en esa columna (como una lista fija de etiquetas), puede crear un índice GIN; esto acelerará dramáticamente las consultas en esa columna.
lumos42
25

En MySQL, use el tipo JSON.

Contrariamente a las respuestas anteriores, el estándar SQL ha incluido tipos de matrices durante casi veinte años; son útiles, incluso si MySQL no los ha implementado.

En su ejemplo, sin embargo, es probable que desee crear tres tablas: person y fruit, luego person_fruit para unirlas.

DROP TABLE IF EXISTS person_fruit;
DROP TABLE IF EXISTS person;
DROP TABLE IF EXISTS fruit;

CREATE TABLE person (
  person_id   INT           NOT NULL AUTO_INCREMENT,
  person_name VARCHAR(1000) NOT NULL,
  PRIMARY KEY (person_id)
);

CREATE TABLE fruit (
  fruit_id    INT           NOT NULL AUTO_INCREMENT,
  fruit_name  VARCHAR(1000) NOT NULL,
  fruit_color VARCHAR(1000) NOT NULL,
  fruit_price INT           NOT NULL,
  PRIMARY KEY (fruit_id)
);

CREATE TABLE person_fruit (
  pf_id     INT NOT NULL AUTO_INCREMENT,
  pf_person INT NOT NULL,
  pf_fruit  INT NOT NULL,
  PRIMARY KEY (pf_id),
  FOREIGN KEY (pf_person) REFERENCES person (person_id),
  FOREIGN KEY (pf_fruit) REFERENCES fruit (fruit_id)
);

INSERT INTO person (person_name)
VALUES
  ('John'),
  ('Mary'),
  ('John'); -- again

INSERT INTO fruit (fruit_name, fruit_color, fruit_price)
VALUES
  ('apple', 'red', 1),
  ('orange', 'orange', 2),
  ('pineapple', 'yellow', 3);

INSERT INTO person_fruit (pf_person, pf_fruit)
VALUES
  (1, 1),
  (1, 2),
  (2, 2),
  (2, 3),
  (3, 1),
  (3, 2),
  (3, 3);

Si desea asociar a la persona con una variedad de frutas, puede hacerlo con una vista:

DROP VIEW IF EXISTS person_fruit_summary;
CREATE VIEW person_fruit_summary AS
  SELECT
    person_id                                                                                              AS pfs_person_id,
    max(person_name)                                                                                       AS pfs_person_name,
    cast(concat('[', group_concat(json_quote(fruit_name) ORDER BY fruit_name SEPARATOR ','), ']') as json) AS pfs_fruit_name_array
  FROM
    person
    INNER JOIN person_fruit
      ON person.person_id = person_fruit.pf_person
    INNER JOIN fruit
      ON person_fruit.pf_fruit = fruit.fruit_id
  GROUP BY
    person_id;

La vista muestra los siguientes datos:

+---------------+-----------------+----------------------------------+
| pfs_person_id | pfs_person_name | pfs_fruit_name_array             |
+---------------+-----------------+----------------------------------+
|             1 | John            | ["apple", "orange"]              |
|             2 | Mary            | ["orange", "pineapple"]          |
|             3 | John            | ["apple", "orange", "pineapple"] |
+---------------+-----------------+----------------------------------+

En 5.7.22, querrá usar JSON_ARRAYAGG , en lugar de piratear la matriz a partir de una cadena.

dibujó
fuente
2

Utilice el tipo de campo de base de datos BLOB para almacenar matrices.

Ref: http://us.php.net/manual/en/function.serialize.php

Valores devueltos

Devuelve una cadena que contiene una representación de flujo de bytes del valor que se puede almacenar en cualquier lugar.

Tenga en cuenta que esta es una cadena binaria que puede incluir bytes nulos y debe almacenarse y manejarse como tal. Por ejemplo, la salida serialize () generalmente debe almacenarse en un campo BLOB en una base de datos, en lugar de un campo CHAR o TEXT.

webdevfreak
fuente
-4

puedes almacenar tu matriz usando group_Concat así

 INSERT into Table1 (fruits)  (SELECT GROUP_CONCAT(fruit_name) from table2)
 WHERE ..... //your clause here

AQUÍ un ejemplo en violín

echo_Me
fuente
4
No está bien explicado. Nombres de tablas incorrectos.
Martin F