Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Tipo de dados ENUM (enumeração) no MySQL:12 principais fatos e dicas úteis


Os dados MySQL ENUM são um tipo de dados string com um valor escolhido na lista de valores permitidos. Você define esses valores permitidos durante a criação da tabela, conforme mostrado abaixo:
CREATE TABLE Product
(
    id int NOT NULL PRIMARY KEY,
    productName varchar(30) NOT NULL,
    color enum('blue','red','yellow','black','white') NOT NULL DEFAULT 'blue'
);

Fácil, não é?

Para começar, a validação de dados é instantânea sem outra tabela e uma chave estrangeira. No modo de servidor estrito, isso significa que você não pode forçar uma entrada errada. Isso é ótimo!

Ou é?

Como qualquer outra coisa no mundo, nem sempre é um felizes para sempre.

Depois de ler os seguintes 12 fatos importantes sobre o MySQL ENUM, você pode decidir se é bom para seu próximo banco de dados ou tabela no MySQL.

Para este artigo, a versão do MySQL é 8.0.23 e o mecanismo de armazenamento é o InnoDB.

1. MySQL ENUM é um tipo de par de valor de chave/string


MySQL ENUM é um par chave/valor. Os valores são strings e as chaves são números de índice.

Mas onde está o índice?

O MySQL atribui automaticamente os números conforme eles aparecem na sua lista. Portanto, seja sobre uma lista de cores, tipos de clientes, saudações ou métodos de pagamento, os números serão atribuídos. Essa é uma lista fixa que nunca se expandirá. Pense em 20 ou menos itens e valores que nunca terão mais atributos. Caso contrário, você precisa de uma mesa.

Mas como esses índices são numerados?

2. O índice MySQL ENUM começa com 1, mas pode ser NULL ou zero


Vou começar com um exemplo.
CREATE TABLE people
(
  id int NOT NULL PRIMARY KEY AUTO_INCREMENT,
  lastname varchar(30) NOT NULL,
  firstname varchar(30) NOT NULL,
  middlename varchar(30) NOT NULL,
  gender enum('Male','Female') NOT NULL DEFAULT 'Female',
  country enum('United States', 'Canada', 'Brazil', 
               'United Kingdom','Poland','Ukraine', 'Lithuania',  
               'Japan','Philippines','Thailand', 'Australia','New Zealand')  
              DEFAULT 'United States',
  modifieddate datetime NOT NULL DEFAULT NOW() 
);

Existem 2 valores MySQL ENUM aqui: genderpaís . Deixe-me começar com o gênero coluna que contém 2 valores:Masculino e Feminino . O índice para Masculino é 1 e Feminino é 2. Isso significa que os índices de chave começam com 1.

A partir deste exemplo simples, você pode identificar o índice do país coluna. Tem 12 valores. Começa com os Estados Unidos com um índice de 1 e termina com a Nova Zelândia com um índice de 12.

Observação :este índice não se refere aos índices de tabela que usamos para pesquisas rápidas.

Além desses números de 1 a 65.535, as colunas ENUM também podem ser NULL ou zero. Em nosso exemplo, o país coluna aceita NULL. Portanto, além dos índices de 1 a 12, NULL é outro índice possível com valor NULL.

Você também pode ter um índice 0. Isso acontece nas seguintes situações:
  • O modo de servidor para seu MySQL não é restrito.
  • Você insere um valor que não está na lista de valores permitidos.
  • Então, a inserção será bem-sucedida, mas o valor é uma string vazia com índice zero.

Para evitar erros, sempre use um modo de servidor estrito.

3. MySQL ENUM limita os valores possíveis em uma coluna


No modo estrito, o país coluna em nosso exemplo anterior aceitará apenas 12 valores possíveis. Portanto, se você tentar fazer isso, o erro "Dados truncados para a coluna 'país'" será lançado:
INSERT INTO people (lastname, firstname, middlename, gender, country)
  VALUES ('Choi', 'Seungcheol', '','Male','South Korea');

A mensagem de erro abaixo ocorreu porque a Coreia do Sul não está na lista enumerada.

A mensagem de erro é a mesma do MySQL Workbench.

Se o servidor MySQL usado aqui for sensível a maiúsculas e minúsculas, isso também não será aceito:
INSERT INTO people (lastname, firstname, middlename, gender, country)
  VALUES ('Hemsworth', 'Chris', '', 'MALE', 'united states');

Por quê? Definimos o gênero como Masculino , não MASCULINO . E o país é Estados Unidos , não estados unidos.

No final, os valores enumerados no tipo de dados MySQL ENUM agem como restrições de chave estrangeira, mas sem outra tabela.

Além disso, há outro benefício que os dados MySQL ENUM fornecem.

4. Saída amigável sem o uso de JOIN


Não há necessidade de JOINs, mas a saída é amigável. Vamos pegar o exemplo ENUM no MySQL abaixo para explicá-lo:
SELECT * FROM people 
WHERE country = 4;

Esta consulta irá recuperar pessoas do Reino Unido. Por padrão, você vê as strings definidas na coluna ENUM. Mas internamente, os índices numerados são armazenados. Aqui está o resultado:

Observação :Os dados que você vê foram gerados usando dbForge Studio para ferramenta de geração de dados do MySQL. Gerei 50.000 nomes usando a ferramenta.

Enquanto isso, a mesma saída pode ser obtida ao usar uma tabela separada e uma junção.
SELECT
 p.id
,p.lastname
,p.firstname
,p.middlename
,CASE WHEN p.gender = 'M' THEN 'Male' ELSE 'Female' END AS gender
,c.countryname AS country
,p.modifieddate
FROM people_no_enums p
LEFT JOIN country c ON p.country = c.id
WHERE p.country = 4;

Então, você deve usar o MySQL ENUM para evitar JOINs? Definitivamente não! Isso é bom para uma lista pequena, mas fixa. Mais dados com um número indefinido de linhas e mais atributos requerem uma tabela. E para fazer uma saída mais amigável como na Figura 2, você também precisará de um JOIN. Ter uma tabela separada é mais flexível e não requer nada do desenvolvedor quando os dados estão ativos. Este não é o caso com Enumdatatype.

5. Filtrar a enumeração do MySQL por índice ou valor de string


No ponto #4, você viu um exemplo com uma cláusula WHERE para filtrar com uma coluna ENUM. Ele usou o índice para especificar o país. Então, isso também funcionará:
SELECT * from people
WHERE country IN (1,3,5)
AND gender = 1;

Você também pode usar o valor da string, como o abaixo:
SELECT * FROM people 
WHERE country='Philippines'
AND gender = 'Female';

6. A classificação é por índice


A classificação pode ser um pouco complicada. Os valores ENUM são armazenados de acordo com seus números de índice, não o valor. Confira o código abaixo e a saída que segue na Figura 3.
SELECT DISTINCT 
 country AS CountryName
,country + 0 AS CountryId
FROM people
ORDER BY country;

Se você quiser que a classificação seja baseada no valor, converta a coluna em um CHAR, como o abaixo.
SELECT DISTINCT 
 country AS CountryName
,country + 0 AS CountryId
FROM people
ORDER BY CAST(country AS char);

Que tal agora?
SELECT DISTINCT 
 country AS CountryName
,country + 0 AS CountryId
FROM people
ORDER BY CountryName;

Pelo que parece, o valor será usado para classificação. Mas esse não é o caso. A saída será a mesma da Figura 3. ORDER BY com CAST é a melhor maneira de classificar por valor.

7. Armazenamento MySQL ENUM é apenas até 2 bytes


De acordo com a documentação oficial, o armazenamento padrão do MySQL ENUM envolve o índice. A tabela resultante é mais compacta em comparação com o armazenamento dos valores. Um (1) byte para enumerações com 1 a 255 valores possíveis. Dois (2) bytes para 256 a 65.535 valores possíveis.

Mas há um segredo que quero lhe contar.

Claro que, quando se trata de armazenamento, os valores vão ocupar mais do que o índice. Como o design adequado da tabela produz um espaço de armazenamento menor, vamos criar outra tabela com uma tabela de país separada.
CREATE TABLE country
(
   id int NOT NULL PRIMARY KEY AUTO_INCREMENT,
   countryname varchar(30) NOT NULL,
   modifieddate datetime DEFAULT NOW()
);

CREATE TABLE people_no_enums
(
  id int NOT NULL PRIMARY KEY AUTO_INCREMENT,
  lastname varchar(30) NOT NULL,
  firstname varchar(30) NOT NULL,
  middlename varchar(30) NOT NULL,
  gender char(1) not NULL,
  country tinyint DEFAULT 1,
  modifieddate datetime NOT NULL DEFAULT NOW() 
);

Agora, vamos inserir os mesmos dados.
INSERT INTO country (id, countryname, modifieddate)
  VALUES (1, 'United States', NOW()), (2, 'Canada', NOW()), (3, 'Brazil', NOW()), 
         (4, 'United Kingdom', NOW()), (5, 'Poland', NOW()), (6, 'Ukraine', NOW()), 
         (7, 'Lithuania', NOW()), (8, 'Japan', NOW()), (9, 'Philippines', NOW()), 
         (10, 'Thailand', NOW()), (11, 'Australia', NOW()), 
         (12, 'New Zealand', NOW());

INSERT INTO people_no_enums
SELECT
 p.id
,p.lastname
,p.firstname
,p.middlename
,CASE WHEN p.gender = 1 THEN 'M' ELSE 'F' END AS gender
,c.id
,p.modifieddate
FROM people p
LEFT JOIN country c ON p.country = c.countryname;

Para isso, usamos a tabela INFORMATION_SCHEMA.TABLES. Veja o código abaixo:
SELECT
table_name,
ROUND(((data_length + index_length)), 2) AS "Size in Bytes"
FROM information_schema.TABLES
WHERE table_schema = "testenumsdb"
AND TABLE_NAME LIKE 'people%'
ORDER BY (data_length + index_length) DESC;

Uma tabela normalizada sem colunas ENUM comparada a uma tabela com ela requer o mesmo tamanho em bytes. Ambos têm 50.000 registros com os mesmos nomes usando o mecanismo de armazenamento InnoDB. Mas é claro que o novo país mesa também ocupará espaço. Você precisa pesar as outras vantagens e desvantagens de usar o ENUM.

8. MySQL ENUM é apenas para literais de string


MySQL ENUM aceita apenas strings literais. Portanto, o código abaixo não funcionará:
CREATE TABLE Product
(
   id int NOT NULL PRIMARY KEY,
   productName varchar(30),
   color enum('red','orange',CONCAT('red','orange'))
);

A função CONCAT dentro do Enumdatatype não é permitida bem como outras expressões SQL válidas.

9. MySQL ENUM não pode ser reutilizado


A partir deste ponto, você verá o lado escuro do MySQL ENUM.

Primeiro, você não pode reutilizá-lo. Você terá que duplicar as mesmas enumerações de cor, tamanho e prioridade se precisar delas em outra tabela. Um projeto como o da Figura 6 abaixo é impossível com ENUMs.

Para usar ENUM nas 2 tabelas acima, você precisa duplicar a lista de prioridades nas 2 tabelas.

10. Adicionar mais valores requer alterar a tabela


No gênero lista anterior, tentamos usar apenas 2 itens:Masculino e Feminino . E se sua empresa decidir abraçar o LGBTQ? Você precisa executar um ALTER TABLE e adicionar ao final da enumeração Lésbica , Gay , Bissexual , Transgênero , e Queer . Aqui está o código:
ALTER TABLE people
   MODIFY COLUMN gender     
          enum('Male','Female','Lesbian','Gay','Bisexual','Transgender','Queer')     
          NOT NULL DEFAULT 'Male';

Executar isso no meu laptop com 50.000 registros levou apenas menos de um segundo. Tabelas maiores e mais complexas consumirão um pouco mais de tempo. Se a lista de gêneros for uma tabela, basta inserir os 5 novos valores.

Renomear um valor também precisará de ALTER TABLE. Uma tabela separada requer apenas uma instrução UPDATE fácil.

11. Você não pode listar facilmente os valores possíveis


Você preenche listas suspensas ou botões de opção agrupados de uma tabela? É fácil quando você tem uma tabela de países. Faça um SELECT id , nome do país DE país , e você está pronto para preencher a lista suspensa.

Mas como você fará isso com o MySQL ENUM?

Primeiro, obtenha as informações da coluna da tabela INFORMATION_SCHEMA.COLUMNS, assim:
/* Get the possible values for country ENUM. */
SELECT  
 TABLE_NAME
,COLUMN_NAME
,COLUMN_TYPE
FROM information_schema.columns
WHERE TABLE_SCHEMA='testenumsdb'
  AND TABLE_NAME = 'people'
  AND COLUMN_NAME = 'country';

Em seguida, você deve analisar essa string e formatá-la antes de preencher uma lista suspensa. Bem arcaico, não é?

Mas há uma última coisa.

12. MySQL ENUM não é padrão


ENUM é uma extensão do MySQL para o padrão ANSI SQL. Outros produtos RDBMS não suportam isso. Portanto, consulte o tutorial apropriado do MySQL antes de iniciar seus projetos. Se você precisar portar seu banco de dados MySQL cheio de ENUMs para o SQL Server, por exemplo, você precisa fazer uma solução alternativa. As soluções alternativas variam dependendo de como você cria a tabela de destino no SQL Server.

Resultado


Você deve pesar os prós e contras de usar o MySQL ENUM. Ter uma tabela separada com a normalização adequada aplicada é o mais flexível no futuro incerto.

Congratulamo-nos com pontos adicionais. Então, vá para a seção de comentários abaixo e conte-nos sobre isso. Você também pode compartilhar isso em suas plataformas de mídia social favoritas.