Zum Inhalt springen

Enum-Datentyp

Name Beschreibung
ENUM Dictionary mit allen möglichen String-Werten einer Spalte

Der Enum-Typ steht für eine Dictionary-Datenstruktur mit allen möglichen eindeutigen Werten einer Spalte. Eine Spalte, die die Wochentage speichert, kann beispielsweise ein Enum sein, das alle möglichen Tage enthält. Enums sind besonders interessant für String-Spalten mit niedriger Kardinalität (d. h. weniger eindeutige Werte). Die Spalte speichert dann nur eine numerische Referenz auf die Zeichenkette im Enum-Dictionary, was zu enormen Einsparungen beim Festplattenspeicher und zu schnellerer Abfrageleistung führt.

Enums erstellen

Sie können ein Enum mit fest kodierten Werten erstellen:

CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy');
-- This statement will fail since enums cannot hold NULL values:
-- CREATE TYPE mood AS ENUM ('sad', NULL);
-- This statement will fail since enum values must be unique:
-- CREATE TYPE mood AS ENUM ('sad', 'sad');

Sie können Enums in einem bestimmten Schema anlegen:

CREATE SCHEMA my_schema;
CREATE TYPE my_schema.mood AS ENUM ('sad', 'ok', 'happy');

Anonyme Enums können beim Casten on the fly erzeugt werden:

SELECT 'clubs'::ENUM ('spades', 'hearts', 'diamonds', 'clubs');

Sie können ein Enum auch mit einer SELECT-Anweisung erzeugen, die eine einzelne Spalte von VARCHARs liefert. Die Wertemenge aus der Select-Anweisung wird automatisch dedupliziert, und NULL-Werte werden ignoriert:

CREATE TYPE region AS ENUM (SELECT region FROM sales_data);

Wenn Sie Daten aus einer Datei importieren, können Sie vor dem Import ein Enum für eine VARCHAR-Spalte anlegen:

CREATE TYPE region AS ENUM (SELECT region FROM 'sales_data.csv');
CREATE TABLE sales_data (amount INTEGER, region region);
COPY sales_data FROM 'sales_data.csv';

Enums verwenden

Enum-Werte unterscheiden Groß- und Kleinschreibung, daher gelten ‘maltese’ und ‘Maltese’ als unterschiedliche Werte:

CREATE TYPE breed AS ENUM ('maltese', 'Maltese');
-- Will return false
SELECT 'maltese'::breed = 'Maltese'::breed;
-- Will error
SELECT 'MALTESE'::breed;

Nach dem Anlegen kann ein Enum überall dort verwendet werden, wo ein eingebauter Standardtyp verwendet wird. Beispielsweise können wir eine Tabelle mit einer Spalte anlegen, die auf das Enum verweist.

CREATE TABLE person (
name TEXT,
current_mood mood
);
INSERT INTO person VALUES
('Pedro', 'happy'),
('Mark', NULL),
('Pagliacci', 'sad'),
('Mr. Mackey', 'ok');

Die folgende Abfrage schlägt fehl, weil der Typ mood keinen Wert quackity-quack hat.

INSERT INTO person VALUES ('Hannes', 'quackity-quack');

Enums vs. Strings

DuckDB-Enums werden bei Bedarf automatisch in VARCHAR-Typen gecastet. Diese Eigenschaft erlaubt Vergleiche zwischen verschiedenen Enums oder zwischen einem Enum und einer VARCHAR-Spalte.

Sie erlaubt auch, ein Enum in jeder VARCHAR-Funktion zu verwenden. Zum Beispiel:

SELECT current_mood, regexp_matches(current_mood, '.*a.*') AS contains_a FROM person;
current_mood contains_a
happy true
NULL NULL
sad true
ok false

Beim Vergleich zweier verschiedener Enum-Typen castet DuckDB beide nach String und führt einen String-Vergleich durch:

CREATE TYPE new_mood AS ENUM ('happy', 'anxious');
SELECT * FROM person
WHERE current_mood = 'happy'::new_mood;
-- Equivalent to `WHERE current_mood::VARCHAR = 'happy'::VARCHAR`
name current_mood
Pedro happy

Beim Vergleich eines Enums mit einem VARCHAR castet DuckDB das Enum nach VARCHAR und führt einen String-Vergleich durch:

SELECT * FROM person
WHERE current_mood = name;
-- Equivalent to `WHERE current_mood::VARCHAR = name`
-- No rows returned

Beim Vergleich mit einer konstanten Zeichenkette führt DuckDB eine Optimierung durch und try_cast(⟨constant string⟩, enum_type){:.language-sql .highlight}, sodass physisch ein Integer-Vergleich statt eines String-Vergleichs stattfindet (logisch bleibt es ein String-Vergleich):

SELECT * FROM person
WHERE current_mood = 'sad';
-- Equivalent to `WHERE current_mood::VARCHAR = 'sad'`
name current_mood
Pagliacci sad

Warnung Das bedeutet, dass der Vergleich mit einer beliebigen (nicht äquivalenten) Zeichenkette immer false ergibt (und keinen Fehler):

SELECT * FROM person
WHERE current_mood = 'bogus';
-- Equivalent to `WHERE current_mood::VARCHAR = 'bogus'`
-- No rows returned

Wenn Sie Typsicherheit erzwingen möchten, casten Sie explizit auf das Enum:

SELECT * FROM person
WHERE current_mood = 'bogus'::mood;
-- Conversion Error: Could not convert string 'bogus' to UINT8

Ordnung von Enums

Enum-Werte sind gemäß ihrer Reihenfolge in der Enum-Definition geordnet. Zum Beispiel:

CREATE TYPE priority AS ENUM ('low', 'medium', 'high');
SELECT 'low'::priority < 'high'::priority AS comp;
-- note that 'low'::VARCHAR < 'high'::VARCHAR is false!
comp
true
SELECT unnest(['medium'::priority, 'high'::priority, 'low'::priority]) AS m
ORDER BY m;
m
low
medium
high

Warnung Wenn Sie ein Enum mit einem Nicht-Enum vergleichen (z. B. einem VARCHAR oder einem anderen Enum-Typ), wird das Enum zuerst in eine Zeichenkette gecastet (wie im vorherigen Abschnitt beschrieben), und der Vergleich erfolgt lexikographisch wie bei Zeichenketten:

CREATE TABLE tasks (name TEXT, priority_level priority);
INSERT INTO tasks VALUES ('a', 'low'), ('b', 'medium'), ('c', 'high');
-- WARNING!
-- Equivalent to `WHERE priority_level::VARCHAR >= 'medium'`
SELECT * FROM tasks
WHERE priority_level >= 'medium';
-- Misses the 'high' priority task!
name priority_level
b medium

Wenn Sie also z. B. „alle Prioritäten ab medium“ erhalten möchten, casten Sie explizit auf den Enum-Typ:

SELECT * FROM tasks
WHERE priority_level >= 'medium'::priority;
name priority_level
b medium
c high

Funktionen

Siehe Enum-Funktionen.

Zeigen Sie beispielsweise die verfügbaren Werte im Enum mood mit der Funktion enum_range:

SELECT enum_range(NULL::mood) AS my_enum_range;
my_enum_range
[sad, ok, happy]

Enums entfernen

Enum-Typen werden im Katalog gespeichert, und für jede Tabelle, die sie verwendet, wird eine Katalogabhängigkeit hinzugefügt. Ein Enum kann mit folgendem Befehl aus dem Katalog entfernt werden:

DROP TYPE ⟨enum_name⟩;

Derzeit können Enums, die in Tabellen verwendet werden, entfernt werden, ohne die Tabellen zu beeinträchtigen.

Warnung Dieses Verhalten der Enum-Entfernung kann sich ändern. In zukünftigen Versionen wird erwartet, dass abhängige Spalten vor dem Löschen des Enums entfernt werden müssen oder das Enum mit dem zusätzlichen Parameter CASCADE gelöscht werden muss.