sql >> Databasteknik >  >> RDS >> Mysql

ENUM (Enumeration) Datatyp i MySQL:Topp 12 fakta och användbara tips

MySQL ENUM-data är en strängdatatyp med ett värde valt från listan över tillåtna värden. Du ställer in dessa tillåtna värden under tabellskapandet enligt nedan:

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

Lätt, eller hur?

Till att börja med är datavalideringen omedelbar utan ytterligare en tabell och en främmande nyckel. Under strikt serverläge betyder detta att du inte kan tvinga fram en felaktig inmatning. Det här är bra!

Eller är det?

Som alla andra saker i världen är det inte alltid en lycklig i alla sina dagar.

Efter att ha läst följande 12 nyckelfakta om MySQL ENUM kan du bestämma om det är bra för din nästa databas eller tabell i MySQL.

För den här artikeln är MySQL-versionen 8.0.23 och lagringsmotorn är InnoDB.

1. MySQL ENUM är en nyckel-/strängvärdespartyp

MySQL ENUM är ett nyckel/värdepar. Värden är strängar och nycklar är indexnummer.

Men var är indexet?

MySQL tilldelar automatiskt nummer som de visas på din lista. Så oavsett om det handlar om en kortlista med färger, kundtyper, hälsningar eller betalningsmetoder, kommer nummer att tilldelas. Det är en fast lista som aldrig kommer att utökas. Tänk på 20 eller färre objekt och värden som aldrig kommer att ha fler attribut. Annars behöver du ett bord.

Men hur numreras dessa index?

2. MySQL ENUM Index börjar med 1 men kan vara NULL eller Noll

Jag börjar med ett exempel.

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

Det finns två MySQL ENUM-värden här: kön och land . Låt mig börja med könet kolumn som innehåller 2 värden:Man och Kvinna . Indexet för Man är 1 och Kvinna är 2. Det betyder att nyckelindex börjar med 1.

Från detta enkla exempel kan du identifiera indexet för landet kolumn. Den har 12 värden. Det börjar med USA med ett index på 1 och slutar med Nya Zeeland med ett index på 12.

Obs :detta index refererar inte till tabellindexen som vi använder för snabba sökningar.

Förutom dessa siffror från 1 till 65 535 kan ENUM-kolumner också vara NULL eller noll. I vårt exempel är landet kolumnen accepterar NULL. Så, förutom index 1 till 12, är NULL ett annat möjligt index med ett NULL-värde.

Du kan också ha ett 0-index. Detta händer i följande situationer:

  • Serverläget för din MySQL är inte strikt.
  • Du infogar ett värde som inte finns på listan över tillåtna värden.
  • Då kommer infogningen att lyckas, men värdet är en tom sträng med index noll.

För att undvika fel, använd alltid ett strikt serverläge.

3. MySQL ENUM begränsar de möjliga värdena i en kolumn

I strikt läge, landet kolumnen i vårt exempel tidigare accepterar endast 12 möjliga värden. Så om du försöker göra detta kommer felet "Data trunkerad för kolumn 'land'" att visas:

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

Felmeddelandet nedan uppstod eftersom Sydkorea inte finns på den uppräknade listan.

Felmeddelandet är detsamma som i MySQL Workbench.

Om MySQL-servern som används här är skiftlägeskänslig, accepteras inte detta heller:

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

Varför? Vi definierade könet som Man , inte MAN . Och landet är USA , inte förenta staterna.

I slutändan fungerar uppräknade värden i MySQL ENUM-datatypen som begränsningar för främmande nyckel men utan en annan tabell.

Bortsett från detta finns det en annan fördel som MySQL ENUM-data ger.

4. Vänlig utgång utan användning av JOIN

Inget behov av JOINs, men resultatet är vänligt. Låt oss ta nedanstående ENUM i MySQL-exemplet för att förklara det:

SELECT * FROM people 
WHERE country = 4;

Denna fråga kommer att hämta personer från Storbritannien. Som standard ser du strängarna du definierade i kolumnen ENUM. Men internt lagras de numrerade indexen. Här är resultatet:

Obs :Datan du ser genererades med dbForge Studio för MySQL:s datagenereringsverktyg. Jag genererade 50 000 namn med hjälp av verktyget.

Samtidigt kan samma utdata uppnås när du använder en separat tabell och en join.

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;

Så, ska du använda MySQL ENUM för att undvika JOINs helt och hållet? Definitivt inte! Detta är bra för en liten men fast lista. Mer data med ett obestämt antal rader och fler attribut kräver en tabell. Och för att göra en vänligare utgång som i figur 2 behöver du också en JOIN. Att ha en separat tabell är mer flexibelt och kräver ingenting av utvecklaren när data är live. Detta är inte fallet med Enumdatatype.

5. Filtrera MySQL-uppräkning efter index eller strängvärde

I punkt #4 såg du ett exempel med en WHERE-sats att filtrera med en ENUM-kolumn. Den använde indexet för att ange landet. Så det här kommer att fungera också:

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

Du kan också använda strängvärdet, som det nedan:

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

6. Sortering sker efter Index

Sortering kan vara lite knepigt. ENUM-värden lagras enligt deras indexnummer, inte värdet. Kolla in koden nedan och utgången som följer i figur 3.

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

Om du vill att sorteringen ska baseras på värde, kasta kolumnen till en CHAR, som den nedan.

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

Vad sägs om det här?

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

Ur utseendet kommer värdet att användas för sortering. Men så är inte fallet. Resultatet blir detsamma som i figur 3. ORDER BY med en CAST är det bästa sättet att sortera efter värde.

7. MySQL ENUM-lagring är endast upp till 2 byte

Enligt den officiella dokumentationen involverar MySQL ENUM-standardlagringen indexet. Den resulterande tabellen är mer kompakt jämfört med att lagra värdena. En (1) byte för uppräkningar med 1 till 255 möjliga värden. Två (2) byte för 256 till 65 535 möjliga värden.

Men det finns en hemlighet jag vill berätta för dig.

Naturligtvis, när det gäller lagring, kommer värdena att uppta mer än index. Eftersom korrekt bordsdesign ger ett mindre lagringsutrymme, låt oss skapa ett annat bord med ett separat landbord.

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

Låt oss nu infoga samma data.

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;

För att göra det använder vi tabellen INFORMATION_SCHEMA.TABLES. Se koden nedan:

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;

En normaliserad tabell utan ENUM-kolumner jämfört med en tabell med den kräver samma storlek i byte. Båda har 50 000 poster med samma namn med InnoDB-lagringsmotorn. Men naturligtvis det nya landet bordet kommer också att ta plats. Du måste väga de andra fördelarna och nackdelarna med att använda ENUM.

8. MySQL ENUM är endast för strängbokstavar

MySQL ENUM accepterar endast bokstavliga strängar. Så, koden nedan fungerar inte:

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

CONCAT-funktionen i Enumdatatypen är inte tillåten samt andra giltiga SQL-uttryck.

9. MySQL ENUM kan inte återanvändas

Från denna punkt kommer du att se den mörka sidan av MySQL ENUM.

För det första kan du inte återanvända den. Du måste duplicera samma färg, storlek och prioritetsuppräkningar om du behöver dem i en annan tabell. En design som den i figur 6 nedan är omöjlig med ENUM.

För att använda ENUM i de två tabellerna ovan måste du duplicera prioritetslistan på de två tabellerna.

10. Att lägga till fler värden kräver att tabellen ändras

I kön listan tidigare försökte vi använda endast 2 objekt:Man och Kvinna . Vad händer om ditt företag bestämmer sig för att anamma HBTQ? Du måste köra en ALTER TABLE och lägga till lesbisk i slutet av uppräkningen , Gay , Bisexuell , Transgender och Queer . Här är koden:

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

Att köra detta på min bärbara dator med 50 000 rekord tog bara mindre än en sekund. Större och mer komplexa bord kommer att ta lite mer tid. Om könslistan är en tabell behöver du bara infoga de 5 nya värdena.

Om du byter namn på ett värde behöver du också ALTER TABLE. En separat tabell kräver bara en enkel UPDATE-sats.

11. Du kan inte enkelt lista ned möjliga värden

Fyller du i rullgardinslistor eller grupperade alternativknappar från en tabell? Det är enkelt när du har ett landbord. Gör ett SELECT id , landsnamn FRÅN land , och du är redo att fylla i rullgardinsmenyn.

Men hur gör du detta med MySQL ENUM?

Hämta först kolumninformationen från tabellen INFORMATION_SCHEMA.COLUMNS, så här:

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

Sedan måste du analysera den strängen och formatera den innan du fyller i en rullgardinslista. Ganska ålderdomligt, eller hur?

Men det finns en sista sak.

12. MySQL ENUM är icke-standard

ENUM är ett MySQL-tillägg till ANSI SQL-standarden. Andra RDBMS-produkter stöder inte detta. Så, se lämplig MySQL-handledning innan du startar dina projekt. Om du behöver porta din MySQL-databas full av ENUM till SQL Server, till exempel, måste du göra en lösning. Lösningar kommer att variera beroende på hur du designar måltabellen i SQL Server.

Bottomline

Du måste väga för- och nackdelar med att använda MySQL ENUM. Att ha en separat tabell med korrekt normalisering tillämpad är det mest flexibla i en osäkra framtid.

Vi välkomnar ytterligare poäng. Så gå vidare till avsnittet Kommentarer nedan och berätta om det. Du kan också dela detta på dina favoritplattformar för sociala medier.


  1. Hur man undslipper enstaka citat, specialtecken i MySQL

  2. Oracle DROP TABELL OM FINNS Alternativ

  3. Sök efter helordsmatchning i MySQL

  4. Hur man undviker att dividera med noll i MySQL