Como armazenar arrays no MySQL?

118

Eu tenho duas tabelas no MySQL. Tabela Pessoa tem as seguintes colunas:

id | name | fruits

A fruitscoluna pode conter nulos ou uma matriz de strings como ('maçã', 'laranja', 'banana') ou ('morango'), etc. A segunda tabela é Fruta de mesa e tem as seguintes três colunas:

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

Então, como devo projetar a fruitscoluna na primeira tabela para que ela possa conter uma matriz de strings que obtêm valores da fruit_namecoluna na segunda tabela? Já que não há tipo de dados de matriz no MySQL, como devo fazer isso?

tonga
fonte
1
que tal adicioná-lo como entradas separadas, laranja, 2, 1, rosa, 2, 1, etc. e então você pode usar consultas para tratá-los como se fossem matrizes.
Sai
@JanusTroelsen: Não estou usando PHP para ler / escrever DB. Então, existe uma maneira universal de fazer isso?
tonga
1
@tonga, verifique meu violino, é isso que você quer?
echo_Me

Respostas:

163

A maneira correta de fazer isso é usar várias tabelas e JOIN elas em suas consultas.

Por exemplo:

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`)
);

A person_fruittabela contém uma linha para cada fruto uma pessoa está associado e efetivamente liga o persone fruitsmesas juntos, IE

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

Quando você deseja recuperar uma pessoa e todas as suas frutas, você pode fazer algo assim:

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 mau
fonte
4
A terceira tabela é a tabela de links entre Person e Fruit. Então, se uma pessoa tem 100 frutas. Preciso criar 100 linhas na terceira tabela, certo? Isso é eficiente?
tonga
1
@tonga Exatamente, cada uma das 100 linhas teria o mesmo, person_idmas diferente fruit_name. Esta é efetivamente uma implementação da teoria da resposta de Janus.
Bad Wolf
1
É sempre verdade que qualquer relação entre duas tabelas precisa ser armazenada na terceira tabela? Posso apenas fazer uma consulta para encontrar a relação apenas armazenando as chaves primárias de duas tabelas?
tonga
2
Sim, é assim que o exemplo está configurado agora. Qualquer informação sobre a pessoa deve estar na personmesa, qualquer informação sobre a fruta na fruitsmesa e qualquer informação especificamente sobre a relação entre uma pessoa em particular e uma fruta em particular na person_fruitmesa. Como neste exemplo não há nenhuma informação adicional, a person_fruittabela tem apenas duas colunas, as chaves primárias das tabelas persone fruits. A quantidade de uma fruta específica é um exemplo de outra coisa que poderia entrar na person_fruittabela.
Bad Wolf
2
Não seria melhor usar um INTde uma chave em fruitse só tem esse INTem person_fruit? Portanto, o nome pode ser alterado posteriormente e também precisaria de menos espaço se você não tivesse muito mais linhas dentro do fruitsque em person_fruit.
12431234123412341234123
58

O motivo de não haver matrizes no SQL é porque a maioria das pessoas realmente não precisa deles. Os bancos de dados relacionais (SQL é exatamente isso) funcionam usando relações e, na maioria das vezes, é melhor atribuir uma linha de uma tabela para cada "bit de informação". Por exemplo, onde você pode pensar "Eu gostaria de uma lista de coisas aqui", em vez disso, faça uma nova tabela, ligando a linha de uma tabela com a linha de outra. [1] Dessa forma, você pode representar relacionamentos M: N. Outra vantagem é que esses links não atrapalharão a linha que contém o item vinculado. E o banco de dados pode indexar essas linhas. Normalmente, as matrizes não são indexadas.

Se você não precisa de bancos de dados relacionais, pode usar, por exemplo, um armazenamento de valor-chave.

Leia sobre normalização de banco de dados , por favor. A regra de ouro é "[Todo] não-chave [atributo] deve fornecer um fato sobre a chave, a chave inteira e nada além da chave.". Uma matriz faz muito. Possui múltiplos fatos e armazena o pedido (que não está relacionado à própria relação). E o desempenho é ruim (veja acima).

Imagine que você tem uma mesa pessoal e uma mesa com ligações de pessoas. Agora você pode fazer com que cada linha de pessoa tenha uma lista de suas ligações. Mas cada pessoa tem muitos outros relacionamentos com muitas outras coisas. Isso significa que minha tabela pessoal deve conter um array para cada coisa a que ele está conectado? Não, isso não é um atributo da própria pessoa.

[1]: Tudo bem se a tabela de ligação tiver apenas duas colunas (as chaves primárias de cada tabela)! No entanto, se o próprio relacionamento tiver atributos adicionais, eles devem ser representados nesta tabela como colunas.

Janus Troelsen
fonte
2
Obrigado Janus. Isso faz sentido. Agora eu entendo porque o MySQL não suporta o tipo de matriz em uma coluna.
tonga
2
@Sai - Para o que estou fazendo, eu realmente preciso da solução NoSQL?
tonga
1
OK, então se eu tiver uma tabela na qual um campo contém uma matriz numérica de milhares de elementos, por exemplo, alguns dados 2D coletados de um sensor, é muito melhor usar o banco de dados NoSQL?
tonga
5
@tonga: A quantidade de dados não determina o tipo de banco de dados a ser usado, mas sim a natureza dos dados. Se não houver relações, você não precisa no banco de dados relacional. Mas como esse é o padrão da indústria, você pode mantê-lo e simplesmente não usar os recursos relacionais. A maioria dos dados é relacional de alguma forma! Um motivo comum para desnormalizar bancos de dados relacionais ou usar armazenamentos de valores-chave é por motivos de desempenho. Mas esses problemas só surgem quando você tem MILHÕES de linhas! Não otimize prematuramente! Eu recomendo ir apenas com um banco de dados SQL (eu recomendo PostgreSQL). Se você tiver problemas, pergunte.
Janus Troelsen
2
O PostgreSQL também possui armazenamentos de valores-chave integrados, o que significa que seria ainda mais fácil se afastar do modelo relacional se ele não se adequar a você.
Janus Troelsen
50

O MySQL 5.7 agora fornece um tipo de dados JSON . Este novo tipo de dados fornece uma nova maneira conveniente de armazenar dados complexos: listas, dicionários, etc.

Dito isso, os rrays não mapeiam bem os bancos de dados, por isso os mapas relacionais de objetos podem ser bastante complexos. Historicamente, as pessoas armazenam listas / arrays no MySQL criando uma tabela que os descreve e adicionando cada valor como seu próprio registro. A tabela pode ter apenas 2 ou 3 colunas, ou pode conter muito mais. O modo como você armazena esse tipo de dados realmente depende das características dos dados.

Por exemplo, a lista contém um número estático ou dinâmico de entradas? A lista permanecerá pequena ou deverá crescer para milhões de registros? Haverá muitas leituras nesta mesa? Muitas gravações? Muitas atualizações? Todos esses são fatores que precisam ser considerados ao decidir como armazenar coleções de dados.

Além disso, os armazenamentos de dados / documentos Key: Value, como Cassandra, MongoDB, Redis etc., também fornecem uma boa solução. Esteja ciente de onde os dados estão realmente sendo armazenados (se estão armazenados no disco ou na memória). Nem todos os seus dados precisam estar no mesmo banco de dados. Alguns dados não mapeiam bem para um banco de dados relacional e você pode ter motivos para armazená-los em outro lugar, ou você pode querer usar uma chave na memória: banco de dados de valor como um cache quente para dados armazenados em disco em algum lugar ou como um armazenamento efêmero para coisas como sessões.

Charles Addis
fonte
42

Uma nota a ser considerada, você pode armazenar matrizes no Postgres.

Eric Grotke
fonte
6
Observação adicional: eles podem ser indexados, portanto, as consultas que verificam a existência de valores específicos em um array podem ser muito rápidas. O mesmo vale para tipos complexos de JSON.
dia
5
Isso não responde à pergunta de forma alguma. OP perguntou sobre o MySQL.
jhpratt
1
Se você usar ArrayField no Postgres e tiver uma lista exaustiva de valores nessa coluna (como uma lista fixa de tags), você pode criar um índice GIN - ele irá acelerar drasticamente as consultas nessa coluna.
lumos42 de
25

No MySQL, use o tipo JSON.

Ao contrário das respostas acima, o padrão SQL incluiu tipos de array por quase vinte anos; eles são úteis, mesmo que o MySQL não os tenha implementado.

No seu exemplo, entretanto, você provavelmente desejará criar três tabelas: pessoa e fruta, depois pessoa_fruta para juntá-las.

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);

Se você deseja associar a pessoa a uma variedade de frutas, pode fazer isso com uma visualização:

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;

A visualização mostra os seguintes dados:

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

No 5.7.22, você desejará usar JSON_ARRAYAGG , em vez de hackear o array a partir de uma string.

desenhou
fonte
2

Use o tipo de campo de banco de dados BLOB para armazenar matrizes.

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

Valores Retornados

Retorna uma string contendo uma representação de fluxo de bytes de valor que pode ser armazenada em qualquer lugar.

Observe que esta é uma string binária que pode incluir bytes nulos e precisa ser armazenada e tratada como tal. Por exemplo, a saída serialize () geralmente deve ser armazenada em um campo BLOB em um banco de dados, ao invés de um campo CHAR ou TEXT.

webdevfreak
fonte
-4

você pode armazenar seu array usando group_Concat assim

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

AQUI um exemplo em violino

echo_Me
fonte
4
Não está bem explicado. Nomes de tabelas ruins.
Martin F