Mysql
 sql >> Baza danych >  >> RDS >> Mysql

Typ danych ENUM (wyliczenie) w MySQL:12 najważniejszych faktów i przydatnych wskazówek

Dane MySQL ENUM to łańcuchowy typ danych z wartością wybraną z listy dozwolonych wartości. Ustawiasz te dozwolone wartości podczas tworzenia tabeli, jak pokazano poniżej:

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

Proste, prawda?

Na początek sprawdzanie poprawności danych jest natychmiastowe bez kolejnej tabeli i klucza obcego. W trybie ścisłym oznacza to, że nie możesz wymusić błędnego wpisu. To świetnie!

Czy to prawda?

Jak każda inna rzecz na świecie, nie zawsze jest długo i szczęśliwie.

Po przeczytaniu poniższych 12 kluczowych faktów na temat MySQL ENUM możesz zdecydować, czy jest to dobre dla Twojej następnej bazy danych lub tabeli w MySQL.

W tym artykule wersja MySQL to 8.0.23, a silnik pamięci to InnoDB.

1. MySQL ENUM to typ pary klucz/ciąg znaków

MySQL ENUM to para klucz/wartość. Wartości są ciągami, a klucze numerami indeksu.

Ale gdzie jest indeks?

MySQL automatycznie przypisuje numery, które pojawiają się na Twojej liście. Tak więc, niezależnie od tego, czy chodzi o krótką listę kolorów, typów klientów, zwrotów grzecznościowych czy metod płatności, zostaną przypisane numery. To jest stała lista, która nigdy się nie rozszerza. Pomyśl o 20 lub mniej przedmiotach i wartościach, które nigdy nie będą miały dalszych atrybutów. W przeciwnym razie potrzebujesz stołu.

Ale jak są ponumerowane te indeksy?

2. Indeks MySQL ENUM zaczyna się od 1, ale może być NULL lub zero

Zacznę od przykładu.

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

Znajdują się tutaj 2 wartości MySQL ENUM: płećkraj . Zacznę od płci kolumna zawierająca 2 wartości:Mężczyzna i Kobieta . Indeks dla Mężczyzny to 1, a kobieta wynosi 2. Oznacza to, że indeksy kluczy zaczynają się od 1.

Na podstawie tego prostego przykładu możesz zidentyfikować indeks dla kraju kolumna. Ma 12 wartości. Zaczyna się od Stanów Zjednoczonych z indeksem 1, a kończy na Nowej Zelandii z indeksem 12.

Uwaga :ten indeks nie odnosi się do indeksów tabel, których używamy do szybkiego wyszukiwania.

Oprócz tych liczb od 1 do 65 535, kolumny ENUM mogą również mieć wartość NULL lub zero. W naszym przykładzie kraj kolumna akceptuje NULL. Tak więc, oprócz indeksów od 1 do 12, NULL jest kolejnym możliwym indeksem o wartości NULL.

Możesz również mieć indeks 0. Dzieje się tak w następujących sytuacjach:

  • Tryb serwera dla twojego MySQL nie jest ścisły.
  • Wstawiasz wartość, której nie ma na liście dozwolonych wartości.
  • Wtedy wstawienie się powiedzie, ale wartość jest pustym ciągiem z indeksem równym zero.

Aby uniknąć błędów, zawsze używaj ścisłego trybu serwera.

3. MySQL ENUM ogranicza możliwe wartości w kolumnie

W trybie ścisłym kraj kolumna w naszym przykładzie wcześniej zaakceptuje tylko 12 możliwych wartości. Tak więc, jeśli spróbujesz to zrobić, zostanie zgłoszony błąd „Dane obcięte dla kolumny „kraj””:

INSERT INTO people (lastname, firstname, middlename, gender, country)
  VALUES ('Choi', 'Seungcheol', '','Male','South Korea');

Poniższy komunikat o błędzie wystąpił, ponieważ Korea Południowa nie znajduje się na liście wyliczonej.

Komunikat o błędzie jest taki sam jak w MySQL Workbench.

Jeśli używany tutaj serwer MySQL rozróżnia wielkość liter, nie zostanie to również zaakceptowane:

INSERT INTO people (lastname, firstname, middlename, gender, country)
  VALUES ('Hemsworth', 'Chris', '', 'MALE', 'united states');

Czemu? Określiliśmy płeć jako mężczyzna , a nie MĘŻCZYZNA . A kraj to Stany Zjednoczone , a nie stany zjednoczone.

Ostatecznie wyliczone wartości w typie danych MySQL ENUM działają jak ograniczenia klucza obcego, ale bez innej tabeli.

Poza tym dane MySQL ENUM zapewniają jeszcze jedną korzyść.

4. Przyjazny wynik bez użycia JOIN

Nie ma potrzeby JOIN, ale dane wyjściowe są przyjazne. Aby to wyjaśnić, weźmy poniższy przykład ENUM w MySQL:

SELECT * FROM people 
WHERE country = 4;

To zapytanie spowoduje wyszukanie osób z Wielkiej Brytanii. Domyślnie widzisz ciągi, które zdefiniowałeś w kolumnie ENUM. Ale wewnętrznie indeksy numerowane są przechowywane. Oto wynik:

Uwaga :Dane, które widzisz, zostały wygenerowane za pomocą dbForge Studio dla narzędzia do generowania danych MySQL. Za pomocą tego narzędzia wygenerowałem 50 000 nazwisk.

W międzyczasie ten sam wynik można osiągnąć, używając oddzielnej tabeli i połączenia.

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;

Więc, czy powinieneś używać MySQL ENUM, aby całkowicie uniknąć JOIN? Absolutnie nie! To jest dobre dla małej, ale stałej listy. Więcej danych z nieskończoną liczbą wierszy i większą liczbą atrybutów wymaga tabeli. Aby uzyskać bardziej przyjazny wynik, jak na rysunku 2, będziesz również potrzebować JOIN. Posiadanie oddzielnej tabeli jest bardziej elastyczne i nie wymaga niczego od programisty, gdy dane są aktywne. Nie dotyczy to Enumdatatype.

5. Filtruj wyliczenie MySQL według indeksu lub wartości ciągu

W punkcie #4 widziałeś przykład z klauzulą ​​WHERE do filtrowania za pomocą kolumny ENUM. Używał indeksu do określenia kraju. To też zadziała:

SELECT * from people
WHERE country IN (1,3,5)
AND gender = 1;

Możesz także użyć wartości ciągu, takiej jak ta poniżej:

SELECT * FROM people 
WHERE country='Philippines'
AND gender = 'Female';

6. Sortowanie odbywa się według indeksu

Sortowanie może być trochę trudne. Wartości ENUM są przechowywane zgodnie z ich numerami indeksu, a nie wartością. Sprawdź poniższy kod i dane wyjściowe przedstawione na rysunku 3.

SELECT DISTINCT 
 country AS CountryName
,country + 0 AS CountryId
FROM people
ORDER BY country;

Jeśli chcesz, aby sortowanie było oparte na wartości, rzuć kolumnę na CHAR, jak ten poniżej.

SELECT DISTINCT 
 country AS CountryName
,country + 0 AS CountryId
FROM people
ORDER BY CAST(country AS char);

Co powiesz na to?

SELECT DISTINCT 
 country AS CountryName
,country + 0 AS CountryId
FROM people
ORDER BY CountryName;

Wygląda na to, że wartość będzie używana do sortowania. Ale tak nie jest. Wynik będzie taki sam, jak na rysunku 3. ORDER BY z CAST to najlepszy sposób sortowania według wartości.

7. Magazyn MySQL ENUM tylko do 2 bajtów

Zgodnie z oficjalną dokumentacją domyślna pamięć MySQL ENUM obejmuje indeks. Wynikowa tabela jest bardziej zwarta w porównaniu do przechowywania wartości. Jeden (1) bajt dla wyliczeń z możliwymi wartościami od 1 do 255. Dwa (2) bajty dla 256 do 65 535 możliwych wartości.

Ale jest pewien sekret, który chcę ci powiedzieć.

Oczywiście, jeśli chodzi o przechowywanie, wartości będą zajmować więcej niż indeks. Ponieważ odpowiedni projekt stołu zapewnia mniejszą powierzchnię do przechowywania, stwórzmy inny stół z oddzielną tabelą krajów.

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

Teraz wstawmy te same dane.

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;

W tym celu korzystamy z tabeli INFORMATION_SCHEMA.TABLES. Zobacz kod poniżej:

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;

Znormalizowana tabela bez kolumn ENUM w porównaniu z tabelą z nią wymaga tego samego rozmiaru w bajtach. Oba mają 50 000 rekordów o tych samych nazwach, korzystających z silnika pamięci masowej InnoDB. Ale oczywiście nowy kraj stół też zajmie miejsce. Musisz rozważyć inne zalety i wady korzystania z ENUM.

8. MySQL ENUM jest przeznaczony tylko dla literałów ciągów

MySQL ENUM akceptuje tylko literały łańcuchowe. Tak więc poniższy kod nie zadziała:

CREATE TABLE Product
(
   id int NOT NULL PRIMARY KEY,
   productName varchar(30),
   color enum('red','orange',CONCAT('red','orange'))
);

Funkcja CONCAT wewnątrz Enumdatatype nie jest dozwolona jak również inne poprawne wyrażenia SQL.

9. MySQL ENUM nie może być ponownie użyty

Od tego momentu zobaczysz ciemną stronę MySQL ENUM.

Po pierwsze, nie możesz go ponownie użyć. Będziesz musiał zduplikować wyliczenia tego samego koloru, rozmiaru i priorytetu, jeśli potrzebujesz ich w innej tabeli. Projekt taki jak ten na rysunku 6 poniżej jest niemożliwy w przypadku ENUM.

Aby użyć ENUM w 2 powyższych tabelach, musisz powielić listę priorytetów w 2 tabelach.

10. Dodanie większej liczby wartości wymaga zmiany tabeli

W płci lista wcześniej, próbowaliśmy użyć tylko 2 pozycji:mężczyzna i Kobieta . Co się stanie, jeśli Twoja firma zdecyduje się na LGBTQ? Musisz uruchomić ALTER TABLE i dodać na końcu wyliczenie lesbijka , wesoły , Biseksualne , transpłciowe i Kiwer . Oto kod:

ALTER TABLE people
   MODIFY COLUMN gender     
          enum('Male','Female','Lesbian','Gay','Bisexual','Transgender','Queer')     
          NOT NULL DEFAULT 'Male';

Uruchomienie tego na moim laptopie z 50 000 rekordami zajęło mi mniej niż sekundę. Większe i bardziej złożone tabele zajmą nieco więcej czasu. Jeśli lista płci jest tabelą, wystarczy wstawić 5 nowych wartości.

Zmiana nazwy wartości będzie również wymagać ALTER TABLE. Oddzielna tabela wymaga jedynie łatwej instrukcji UPDATE.

11. Nie możesz łatwo wymienić możliwych wartości

Czy wypełniasz listy rozwijane lub zgrupowane przyciski opcji z tabeli? To proste, gdy masz stół country. Wykonaj WYBIERZ id , nazwa kraju Z kraju i jesteś gotowy do wypełnienia listy rozwijanej.

Ale jak to zrobisz z MySQL ENUM?

Najpierw pobierz informacje o kolumnie z tabeli INFORMATION_SCHEMA.COLUMNS w następujący sposób:

/* 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';

Następnie musisz przeanalizować ten ciąg i sformatować go przed wypełnieniem listy rozwijanej. Dość archaiczne, prawda?

Ale jest jeszcze jedna rzecz.

12. MySQL ENUM jest niestandardowy

ENUM to rozszerzenie MySQL do standardu ANSI SQL. Inne produkty RDBMS nie obsługują tego. Dlatego przed rozpoczęciem projektów zapoznaj się z odpowiednim samouczkiem MySQL. Jeśli chcesz na przykład przenieść bazę danych MySQL pełną ENUM do programu SQL Server, musisz zastosować obejście tego problemu. Obejścia będą się różnić w zależności od sposobu projektowania tabeli docelowej w SQL Server.

Dolna linia

Musisz rozważyć zalety i wady korzystania z MySQL ENUM. Posiadanie oddzielnej tabeli z zastosowaną odpowiednią normalizacją jest najbardziej elastyczne w niepewnej przyszłości.

Mile widziane dodatkowe punkty. Przejdź więc do sekcji komentarzy poniżej i opowiedz nam o tym. Możesz również udostępnić to na swoich ulubionych platformach społecznościowych.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. JSON_STORAGE_SIZE() – Znajdź rozmiar przechowywania dokumentu JSON w MySQL

  2. Neo4j - Usuń ograniczenie za pomocą Cypher

  3. Czy korzystam z puli połączeń JDBC?

  4. Jak zoptymalizować wydajność COUNT(*) w InnoDB za pomocą index

  5. Podłączanie Visual COBOL do MySQL