Firebird Documentation Index → Firebird 3.0 Sprachreferenz

Die Quelle des meist kopierten Referenzmaterials: Paul Vinkenoog
Weitere Quelle kopierten Referenzmaterials: Thomas Woinke
Übersetzung ins Deutsche: Martin Köditz
Copyright © 2017-2023 Das Firebird Projekt und alle beteiligten Autoren, unter der Public Documentation License Version 1.0. Bitte vergleichen Sie auch Lizenzhinweise im Anhang.

1. Über die Firebird 3.0-Sprachreferenz

Diese Sprachreferenz beschreibt die von Firebird 3.0 unterstützte SQL-Sprache.

Diese Firebird SQL Sprachreferenz ist das zweite zusammenhängende Dokument, das alle Aspekte der Abfragesprache betrachtet, die von Entwicklern für die Kommunikation ihrer Anwendungen mit dem Firebird Relation Database Management System benötigt werden.

1.1. Thema

Das Kernthema dieses Dokuments ist die vollständige Implementierung der SQL-Abfragesprache. Firebird entspricht weitestgehend den internationalen Standards für SQL in Bezug auf Datentypen, Datenspeicherstrukturen, Mechanismen zur referentiellen Integrität sowie Fähigkeiten zur Datenmanipulation und Zugriffsrechte. Firebird beinhaltet außerdem eine robuste Prozedursprache — procedural SQL (PSQL) — für gespeicherte Prozeduren, Trigger und dynamisch ausführbare Code Blöcke. Dies sind die Themen, die in diesem Dokument behandelt werden.

1.2. Urheberschaft

Für die Firebird 3.0-Version wurde die Firebird 2.5-Sprachreferenz als Basis verwendet, und Firebird 3.0-Informationen wurden basierend auf den Firebird 3.0-Versionshinweisen, der Funktionsdokumentation und der russischen Firebird 3.0-Sprachreferenz hinzugefügt. Dieses Dokument ist jedoch keine direkte Übersetzung der russischen Firebird 3.0-Sprachreferenz.

1.2.1. Mitwirkende

Direkter Inhalt
  • Dmitry Filippov (Autor)

  • Alexander Karpeykin (Autor)

  • Alexey Kovyazin (Autor, Editor)

  • Dmitry Kuzmenko (Autor, Editor)

  • Denis Simonov (Autor, Editor)

  • Paul Vinkenoog (Autor, Designer)

  • Dmitry Yemanov (Autor)

  • Mark Rotteveel (Autor)

Ressourcen Inhalt
  • Adriano dos Santos Fernandes

  • Alexander Peshkov

  • Vladyslav Khorsun

  • Claudio Valderrama

  • Helen Borrie

  • und andere

Übersetzungen
  • Martin Köditz, it & synergy GmbH

1.3. Anmerkungen

Sponsoren und andere Spender
Sponsoren der russischen Sprachreferenz
  • Moscow Exchange (Russland)

    Moscow Exchange ist die größte Börse in Russland sowie Osteuropa, gegründet am 19. Dezember 2011, durch den Zusammenschluss der Börsengruppen MICEX (gegründet 1992) und RTS (gegründet 1995). Moscow Exchange zählt zu den 20 weltweit führenden Börsen im Handel von Anleihen und Aktien, als zu den 10 größten Börsenplattformen für Handelsderivate.

  • IBSurgeon (ibase.ru) (Russland)

    Technischer Support und Entwickler administrativer Tools für das Firebird DBMS.

1.4. Beitragen

Es gibt verschiedene Möglichkeiten, wie Sie zur Dokumentation von Firebird oder Firebird im Allgemeinen beitragen können:

2. SQL Sprachstruktur

Diese Referenz beschreibt die von Firebird unterstützte SQL-Sprache.

2.1. Hintergrund zu Firebirds SQL-Sprache

Zu Beginn, ein paar Punkte über die Eigenschaften die im Hintergrund von Firebirds Sprache eine Rolle spielen.

2.1.1. SQL Bestandteile

Verschiedene Teilmengen von SQL gehören wiederum in verschiedene Aktivitätsbereiche. Die Teilmengen in Firebirds Sprachimplementation sind:

  • Dynamic SQL (DSQL)

  • Procedural SQL (PSQL)

  • Embedded SQL (ESQL)

  • Interactive SQL (ISQL)

Dynamic SQL macht den Hauptteil der Sprache aus, der in Abschnitt (SQL/Foundation) 2 der SQL-Spezifikation beschrieben wird. DSQL repräsentiert Statements, die von Anwendungen über die Firebird API durch die Datenbank verarbeitet werden.

Procedural SQL erweitert Dynamic SQL, um zusammengesetzte Anweisungen zu ermöglichen, die lokale Variablen, Zuweisungen, Bedingungen, Schleifen und andere prozedurale Konstrukte enthalten. PSQL entspricht dem Teil 4 (SQL/PSM) Teil der SQL-Spezifikationen. Ursprünglich waren PSQL-Erweiterungen nur in persistent gespeicherten Modulen (Prozeduren und Trigger) verfügbar, aber in neueren Versionen wurden sie auch in Dynamic SQL aufgetaucht (siehe EXECUTE BLOCK).

Embedded SQL definiert die DSQL-Untermenge, die von Firebird gpre unterstützt wird, der Anwendung, mit der Sie SQL-Konstrukte in Ihre Host-Programmiersprache (C, C++, Pascal, Cobol usw.) einbetten und diese eingebetteten Konstrukte in die richtigen Firebird-API-Aufrufe vorverarbeiten können.

Nur ein Teil der in DSQL implementierten Anweisungen und Ausdrücke wird in ESQL unterstützt.

Interactive ISQL bezieht sich auf die Sprache, die mit Firebird isql ausgeführt werden kann, der Befehlszeilenanwendung für den interaktiven Zugriff auf Datenbanken. Als normale Client-Anwendung ist ihre Muttersprache DSQL. Es bietet auch einige zusätzliche Befehle, die außerhalb seiner spezifischen Umgebung nicht verfügbar sind.

Sowohl DSQL- als auch PSQL-Teilmengen werden in dieser Referenz vollständig vorgestellt. Weder ESQL- noch ISQL-Varianten werden hier beschrieben, sofern nicht explizit erwähnt.

2.1.2. SQL-Dialekte

SQL-Dialekt ist ein Begriff, der die spezifischen Funktionen der SQL-Sprache definiert, die beim Zugriff auf eine Datenbank verfügbar sind. SQL-Dialekte können auf Datenbankebene definiert und auf Verbindungsebene angegeben werden. Drei Dialekte stehen zur Verfügung:

  • Dialekt 1 dient ausschließlich dazu, die Abwärtskompatibilität mit Legacy-Datenbanken aus sehr alten InterBase-Versionen, v.5 und darunter, zu ermöglichen. Dialekt 1-Datenbanken behalten bestimmte Sprachfunktionen bei, die sich von Dialekt 3 unterscheiden, dem Standard für Firebird-Datenbanken.

    • Datums- und Uhrzeitinformationen werden im Datentyp DATE gespeichert. Ein Datentyp TIMESTAMP ist ebenfalls verfügbar, der mit dieser DATE-Implementierung identisch ist.

    • Anführungszeichen können als Alternative zu Apostrophen zum Trennen von Zeichenfolgendaten verwendet werden. Dies steht im Gegensatz zum SQL-Standard – doppelte Anführungszeichen sind sowohl in Standard-SQL als auch in Dialekt 3 für einen bestimmten syntaktischen Zweck reserviert. Strings in doppelten Anführungszeichen sind daher streng zu vermeiden.

    • Die Genauigkeit für die Datentypen NUMERIC und DECIMAL ist kleiner als in Dialekt 3 und wenn die Genauigkeit einer festen Dezimalzahl größer als 9 ist, speichert Firebird sie intern als langen Gleitkommawert.

    • Der Datentyp BIGINT (64-Bit-Ganzzahl) wird nicht unterstützt.

    • Bei Bezeichnern wird die Groß-/Kleinschreibung nicht beachtet und müssen immer den Regeln für normale Bezeichner entsprechen — siehe Abschnitt Bezeichner weiter unten.

    • Obwohl Generatorwerte als 64-Bit-Ganzzahlen gespeichert werden, gibt eine Dialekt-1-Client-Anfrage, beispielsweise SELECT GEN_ID (MyGen, 1) den Generatorwert auf 32 Bit gekürzt zurück.

  • Dialekt 2 ist nur über die Firebird-Client-Verbindung verfügbar und kann nicht in der Datenbank eingestellt werden. Es soll das Debuggen möglicher Probleme mit Altdaten bei der Migration einer Datenbank von Dialekt 1 auf 3 unterstützen.

  • In Dialekt 3-Datenbanken,

    • Zahlen (Datentypen DECIMAL und NUMERIC) werden intern als lange Festkommawerte (skalierte Ganzzahlen) gespeichert, wenn die Genauigkeit größer als 9 ist.

    • Der Datentyp TIME ist nur zum Speichern von Uhrzeitdaten verfügbar.

    • Der Datentyp DATE speichert nur Datumsinformationen.

    • Der 64-Bit-Integer-Datentyp BIGINT ist verfügbar.

    • Doppelte Anführungszeichen sind für die Abgrenzung nicht regulärer Bezeichner reserviert, um Objektnamen zu ermöglichen, bei denen die Groß-/Kleinschreibung beachtet wird oder die auf andere Weise nicht die Anforderungen für reguläre Bezeichner erfüllen.

    • Alle Strings müssen durch einfache Anführungszeichen (Apostrophe) getrennt werden.

    • Generatorwerte werden als 64-Bit-Ganzzahlen gespeichert.

Für neu entwickelte Datenbanken und Anwendungen wird die Verwendung von Dialect 3 dringend empfohlen. Sowohl Datenbank- als auch Verbindungsdialekte sollten übereinstimmen, außer unter Migrationsbedingungen mit Dialekt 2.

Diese Referenz beschreibt die Semantik von SQL Dialect 3, sofern nicht anders angegeben.

2.1.3. Fehlerbedingungen

Die Verarbeitung jeder SQL-Anweisung wird entweder erfolgreich abgeschlossen oder schlägt aufgrund einer bestimmten Fehlerbedingung fehl. Die Fehlerbehandlung kann sowohl auf der Clientseite der Anwendung als auch auf der Serverseite mit PSQL erfolgen.

2.2. Grundelemente: Aussagen, Klauseln, Schlüsselwörter

Das primäre Konstrukt in SQL ist die Anweisung. Eine Anweisung definiert, was das Datenbankverwaltungssystem mit einem bestimmten Daten- oder Metadatenobjekt tun soll. Komplexere Anweisungen enthalten einfachere Konstrukte — Klauseln und Optionen.

Klauseln

Eine Klausel definiert eine bestimmte Art von Direktive in einer Anweisung. Zum Beispiel spezifiziert die Klausel WHERE in einer SELECT-Anweisung und in einigen anderen Datenmanipulationsanweisungen (UPDATE, DELETE) Kriterien zum Durchsuchen einer oder mehrerer Tabellen nach den Zeilen, die ausgewählt, aktualisiert oder gelöscht werden sollen . Die ORDER BY-Klausel gibt an, wie die Ausgabedaten — die Ergebnismenge — sortiert werden sollen.

Optionen

Optionen sind die einfachsten Konstrukte und werden in Verbindung mit bestimmten Schlüsselwörtern angegeben, um eine Qualifizierung für Klauselelemente bereitzustellen. Wenn alternative Optionen verfügbar sind, ist es üblich, dass eine von ihnen die Standardeinstellung ist, die verwendet wird, wenn für diese Option nichts angegeben ist. Zum Beispiel gibt die SELECT-Anweisung alle Zeilen zurück, die den Suchkriterien entsprechen, es sei denn, die DISTINCT-Option beschränkt die Ausgabe auf nicht duplizierte Zeilen.

Schlüsselwörter

Alle Wörter, die im SQL-Lexikon enthalten sind, sind Schlüsselwörter. Einige Schlüsselwörter sind reserviert, was bedeutet, dass ihre Verwendung als Bezeichner für Datenbankobjekte, Parameternamen oder Variablen in einigen oder allen Kontexten verboten ist. Nicht reservierte Schlüsselwörter können als Bezeichner verwendet werden, obwohl dies nicht empfohlen wird. Von Zeit zu Zeit können nicht reservierte Schlüsselwörter reserviert werden, wenn eine neue Sprachfunktion eingeführt wird.

Die folgende Anweisung wird beispielsweise ohne Fehler ausgeführt, da ABS zwar ein Schlüsselwort, aber kein reserviertes Wort ist.

CREATE TABLE T (ABS INT NOT NULL);

Im Gegenteil, die folgende Anweisung gibt einen Fehler zurück, da ADD sowohl ein Schlüsselwort als auch ein reserviertes Wort ist.

CREATE TABLE T (ADD INT NOT NULL);

Siehe die Liste der reservierten Wörter und Schlüsselwörter im Kapitel Reservierte Wörter und Schlüsselwörter.

2.3. Bezeichner

Alle Datenbankobjekte haben Namen, die oft als Identifier oder Bezeichner angegeben werden. Die maximale Identifier-Länge beträgt 31 Byte. Als Bezeichner sind zwei Arten von Namen gültig: reguläre Namen, ähnlich den Variablennamen in regulären Programmiersprachen, und getrennte Namen, die für SQL spezifisch sind. Um gültig zu sein, muss jeder Bezeichnertyp einer Reihe von Regeln entsprechen, wie folgt:

2.3.1. Regeln für reguläre Objektbezeichner

  • Länge darf 31 Zeichen nicht überschreiten

  • Der Name muss mit einem alphabetischen 7-Bit-ASCII-Zeichen ohne Akzent beginnen. Es können weitere 7-Bit-ASCII-Buchstaben, Ziffern, Unterstriche oder Dollarzeichen folgen. Andere Zeichen, einschließlich Leerzeichen, sind nicht gültig. Bei dem Namen wird die Groß-/Kleinschreibung nicht beachtet, dh er kann in Groß- oder Kleinschreibung deklariert und verwendet werden. Somit sind aus Sicht des Systems die folgenden Namen gleich:

    fullname
    FULLNAME
    FuLlNaMe
    FullName
Reguläre Namenssyntax
<name> ::=
  <letter> | <name><letter> | <name><digit> | <name>_ | <name>$

<letter> ::= <upper letter> | <lower letter>

<upper letter> ::= A | B | C | D | E | F | G | H | I | J | K | L | M |
                   N | O | P | Q | R | S | T | U | V | W | X | Y | Z

<lower letter> ::= a | b | c | d | e | f | g | h | i | j | k | l | m |
                   n | o | p | q | r | s | t | u | v | w | x | y | z

<digit> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

2.3.2. Regeln für getrennte Objektbezeichner

  • Länge darf 31 Byte nicht überschreiten. Bezeichner werden im Zeichensatz UNICODE_FSS gespeichert, dh Zeichen außerhalb des ASCII-Bereichs werden mit 2 oder 3 Byte gespeichert.

  • Der gesamte String muss in doppelte Anführungszeichen eingeschlossen werden, z.B. "anIdentifier"

  • Es kann jedes Zeichen aus dem Zeichensatz UNICODE_FSS enthalten, einschließlich Akzentzeichen, Leerzeichen und Sonderzeichen

  • Ein Bezeichner kann ein reserviertes Wort sein

  • Bei Bezeichnern mit Trennzeichen muss die Groß-/Kleinschreibung in allen Kontexten beachtet werden

  • Nachgestellte Leerzeichen in durch Trennzeichen getrennten Namen werden wie bei jeder Stringkonstanten entfernt

  • Begrenzte Bezeichner sind nur in Dialekt 3 verfügbar. Weitere Informationen zu Dialekten finden Sie unter SQL-Dialekte

Getrennte Namenssyntax
<delimited name> ::= "<permitted_character>[<permitted_character> ...]"

Ein durch Trennzeichen getrennter Bezeichner wie "FULLNAME" entspricht den regulären Bezeichnern FULLNAME, fullname, FullName und so weiter. Der Grund dafür ist, dass Firebird reguläre Bezeichner in Großbuchstaben speichert, unabhängig davon, wie sie definiert oder deklariert wurden. Begrenzte Bezeichner werden immer nach der genauen Schreibweise ihrer Definition oder Deklaration gespeichert. Somit unterscheidet sich "FullName" (quoted) von FullName (unquoted, d.h. regulär), das als FULLNAME in den Metadaten gespeichert wird.

2.4. Literals

Literale werden verwendet, um Daten direkt darzustellen. Beispiele für Standardtypen von Literalen sind:

integer        - 0, -34, 45, 0X080000000;
fixed-point    - 0.0, -3.14
floating-point - 3.23e-23;
string         - 'text', 'don''t!';
binary string  - x'48656C6C6F20776F726C64'
date           - DATE '2018-01-19';
time           - TIME '15:12:56';
timestamp      - TIMESTAMP '2018-01-19 13:32:02';
boolean        - true, false, unknown
null state     - null

Details zum Umgang mit den Literalen für jeden Datentyp werden im nächsten Kapitel Datentypen und Untertypen besprochen.

2.5. Operatoren und Sonderzeichen

Eine Reihe von Sonderzeichen ist für die Verwendung als Operatoren oder Trennzeichen reserviert.

<special char> ::=
    <space> | " | % | & | ' | ( | ) | * | + | , | -
  | . | / | : | ; | < | = | > | ? | [ | ] | ^ | { | }

Einige dieser Zeichen können einzeln oder in Kombination als Operatoren (arithmetisch, string, logisch), als Trennzeichen für SQL-Befehle, als Anführungszeichen für Bezeichner und als Begrenzung von String-Literalen oder Kommentaren verwendet werden.

Operatorsyntax
<operator> ::=
    <string concatenation operator>
  | <arithmetic operator>
  | <comparison operator>
  | <logical operator>

<string concatentation operator> ::= "||"

<arithmetic operator> ::= * | / | + | - |

<comparison operator> ::=
    =  | <> | != | ~= | ^= | > | < | >= | <=
  | !> | ~> | ^> | !< | ~< | ^<

<logical operator> ::= NOT | AND | OR

Weitere Informationen zu Operatoren finden Sie unter Ausdrücke.

2.6. Bemerkungen

Kommentare können in SQL-Skripten, SQL-Anweisungen und PSQL-Modulen vorhanden sein. Ein Kommentar kann ein beliebiger Text sein, der vom Code-Autor angegeben wird und normalerweise verwendet wird, um zu dokumentieren, wie bestimmte Teile des Codes funktionieren. Der Parser ignoriert den Text von Kommentaren.

Firebird unterstützt zwei Arten von Kommentaren: block und in-line.

Syntax
<comment> ::= <block comment> | <single-line comment>

<block comment> ::=
  /* <character>[<character> …] */

<single-line comment> ::=
  -- <character>[<character> …]<end line>

Blockkommentare beginnen mit dem Zeichenpaar /* und enden mit dem Zeichenpaar */. Text in Blockkommentaren kann beliebig lang sein und mehrere Zeilen belegen.

Inline-Kommentare beginnen mit einem Bindestrich-Paar -- und werden bis zum Ende der aktuellen Zeile fortgesetzt.

Beispiele
CREATE PROCEDURE P(APARAM INT)
  RETURNS (B INT)
AS
BEGIN
  /* This text will be ignored during the execution of the statement
     since it is a comment
  */
  B = A + 1; -- In-line comment
  SUSPEND;
END

3. Datentypen und Untertypen

Daten verschiedener Art werden verwendet, um:

  • Spalten in einer Datenbanktabelle in der CREATE TABLE-Anweisung definieren oder Spalten mit ALTER TABLE ändern

  • deklarieren oder ändern Sie eine Domäne mit den Anweisungen CREATE DOMAIN oder ALTER DOMAIN

  • lokale Variablen in Stored Procedures, PSQL-Blöcken und Triggern deklarieren und Parameter in Stored Procedures angeben

  • Argumente und Rückgabewerte indirekt angeben, wenn externe Funktionen deklariert werden (UDFs — benutzerdefinierte Funktionen)

  • Argumente für die Funktion CAST() bereitstellen, wenn Daten explizit von einem Typ in einen anderen konvertiert werden

Tabelle 1. Übersicht der Datentypen
Name Größe Präzision & Grenzen Beschreibung

BIGINT

64 Bits

Von -263 bis (263 - 1)

Nur in Dialekt 3 verfügbar

BLOB

unterschiedlich

Die Größe eines BLOB-Segments ist auf 64K begrenzt. Die maximale Größe eines BLOB-Feldes sind 4GB.

Ein Datentyp mit dynamisch unterschiedlicher Größe für die Ablage von großen Datenmengen, wie z.B. Bilder, Texte, Audiodaten. Die strukturelle Basiseinheit is das Segment. Der BLOB-Untertyp definiert dessen Inhalt.

BOOLEAN

8 Bits

false, true, unknown

Boolean-Datentyp

CHAR(n), CHARACTER(n)

n Zeichen. Größe in Bytes abhängig von der Encodierung, der Anzahl Bytes pro Zeichen

von 1 bis 32,767 Bytes

Ein Datentyp mit fester Länge. Bei Anzeige der Daten werden Leerzeichen an das Ende der Zeichenkette bis zur angegebenen Länge angefügt. Die Leerzeichen werden nicht in der Datenbank gespeichert, jedoch wiederhergestellt, um die definierte Länge bei Anzeige am Client zu erreichen. Die Leerzeichen werden nicht über das Netzwerk versendet, was den Datenverkehr reduziert. Wurde kein Zeichenlänge angegeben, wird 1 als Standardwert verwendet.

DATE

32 Bits

von 01.01.0001 AD bis 31.12.9999 AD

ISC_DATE. Nur Datum, kein Zeitelement

DECIMAL (precision, scale)

Varying (16, 32 or 64 bits)

precision = von 1 bis 18, legt die mindestmögliche Anzahl zu speichernder Ziffern fest; _scale) = von 0 bis 18, gibt die Anzahl der Nachkommastellen an.

Eine Kommazahl mit scale Nachkommastellen. scale muss kleiner oder gleich -precision_ sein. Beispiel: NUMERIC(10,3) ist eine Zahl im Format: ppppppp.sss

DOUBLE PRECISION

64 Bits

2.225 * 10-308 bis 1.797 * 10308

Doppelte Präzision nach IEEE, ~15 Stellen, zuverlässige Größe hängt von der Plattform ab.

FLOAT

32 bits

1.175 * 10-38 bis 3.402 * 1038

Einfache Präzision nach IEEE, ~7 Stellen

INTEGER, INT

32 Bits

-2.147.483.648 up to 2.147.483.647

Ganzzahlen mit Vorzeichen

NUMERIC (precision, scale)

Unterschiedlich (16, 32 oder 64 Bits)

precision = von 1 bis 18, legt die genaue Anzahl zu speichernder Stellen fest; scale = von 0 bis 18, legt die Anzahl der Nachkommastellen fest.

Eine Kommazahl mit scale Nachkommastellen. scale muss kleiner oder gleich -precision_ sein. Beispiel: NUMERIC(10,3) ist eine Zahl im Format: ppppppp.sss

SMALLINT

16 Bits

-32.768 bis 32.767

Ganzzahlen mit Vorzeichen (word)

TIME

32 Bits

0:00 to 23:59:59.9999

ISC_TIME. Tageszeit. Kann nicht zum Spiechern von Zeitintervallen verwendet werden.

TIMESTAMP

64 Bits (2 X 32 Bits)

Von Anfang des Tages 01.01.0001 AD bis Ende des Tages 31.12.9999 AD

Datum und Uhrzeit eines Tages

VARCHAR(n), CHAR VARYING, CHARACTER VARYING

n Zeichen. Größe in Bytes, abhängig von der Enkodierung, der Anzahl von Bytes für ein Zeichen

von 1 bis 32,765 Bytes

Zeichenkette mit variabler Länge. Die Gesamtgröße der Zeichen darf (32KB-3) nicht übersteigen. Dies berücksichtigt auch die hinterlegte Enkodierung. Die beiden hinteren Bytes speichern die deklarierte Länge. Es gibt keine Standardgröße. Das Argument n ist erforderlich. Führende und abschließende Leerzeichen werden gespeichert und nicht abgeschnitten, außer den Leerzeichen, die hinter der definierten Länge liegen.

Hinweis zu Daten

Beachten Sie, dass eine Zeitreihe, bestehend aus Daten der letzten Jahrhunderte, verarbeitet wird, ohne auf historische Gegebenheiten Rücksicht zu nehmen. Dennoch ist der Gregorianische Kalender komplett anwendbar.

3.1. Ganzzahlen-Datentypen

Die Datentypen SMALLINT, INTEGER und BIGINT werden für Ganzzahlen verschiedener Präzisionen in Dialekt 3 verwendet. Firebird unterstützt keine vorzeichenlosen (unsigned) Integer.

3.1.1. SMALLINT

Der 16-Bit-Datentyp "SMALLINT" dient der kompakten Datenspeicherung von Integer-Daten, für die nur ein enger Bereich möglicher Werte benötigt wird. Zahlen vom Typ SMALLINT liegen im Bereich von -216 bis 216 - 1, also von -32.768 bis 32.767.

SMALLINT-Beispiele
CREATE DOMAIN DFLAG AS SMALLINT DEFAULT 0 NOT NULL
  CHECK (VALUE=-1 OR VALUE=0 OR VALUE=1);

CREATE DOMAIN RGB_VALUE AS SMALLINT;

3.1.2. INTEGER

Der Datentyp INTEGER ist eine 32-Bit-Ganzzahl. Die Kurzbezeichnung des Datentyps lautet 'INT'. Zahlen vom Typ INTEGER liegen im Bereich von -232 bis 232 - 1, also von -2.147.483.648 bis 2.147.483.647.

INTEGER-Beispiele
CREATE TABLE CUSTOMER (
  CUST_NO INTEGER NOT NULL,
  CUSTOMER VARCHAR(25) NOT NULL,
  CONTACT_FIRST VARCHAR(15),
  CONTACT_LAST VARCHAR(20),
  ...
    PRIMARY KEY (CUST_NO) )

3.1.3. BIGINT

BIGINT ist ein SQL:99-kompatibler 64-Bit-Integer-Datentyp, der nur in Dialect 3 verfügbar ist. Wenn ein Client Dialekt 1 verwendet, wird der vom Server gesendete Generatorwert auf eine 32-Bit-Ganzzahl (INTEGER) reduziert. Wenn Dialekt 3 für die Verbindung verwendet wird, ist der Generatorwert vom Typ BIGINT.

Zahlen des Typs 'BIGINT' liegen im Bereich von -263 bis 263 - 1, oder von -9.223.372.036.854.775.808 bis 9.223.372.036.854.775.807.

3.1.4. Hexadezimales Format für Integer-Zahlen

Ab Firebird 2.5 können Konstanten der drei Integer-Typen im hexadezimalen Format mit 9 bis 16 hexadezimalen Stellen für BIGINT bzw. 1 bis 8 Stellen für INTEGER angegeben werden. Die Hex-Darstellung zum Schreiben in SMALLINT wird nicht explizit unterstützt, aber Firebird wandelt eine Hex-Zahl bei Bedarf transparent in SMALLINT um, sofern sie in den negativen und positiven SMALLINT-Bereich fällt.

Die Verwendung und die numerischen Wertebereiche der hexadezimalen Notation werden in der Diskussion zu Zahlenkonstanten im Kapitel Allgemeine Sprachelemente genauer beschrieben.

Beispiele mit Integer-Typen
CREATE TABLE WHOLELOTTARECORDS (
  ID BIGINT NOT NULL PRIMARY KEY,
  DESCRIPTION VARCHAR(32)
);

INSERT INTO MYBIGINTS VALUES (
  -236453287458723,
  328832607832,
  22,
  -56786237632476,
  0X6F55A09D42,       -- 478177959234
  0X7FFFFFFFFFFFFFFF, -- 9223372036854775807
  0XFFFFFFFFFFFFFFFF, -- -1
  0X80000000,         -- -2147483648, ein INTEGER
  0X080000000,        -- 2147483648, ein BIGINT
  0XFFFFFFFF,         -- -1, ein INTEGER
  0X0FFFFFFFF         -- 4294967295, ein BIGINT
);

Die hexadezimalen INTEGERs im obigen Beispiel werden automatisch in BIGINT umgewandelt, bevor sie in die Tabelle eingefügt werden. Dies geschieht jedoch nach der Zahlenwert ermittelt wurde, also werden 0x80000000 (8 Stellen) und 0x080000000 (9 Stellen) als unterschiedliche BIGINT Werte gespeichert.

3.2. Gleitkomma-Datentypen

Gleitkomma-Datentypen werden in einem binären IEEE 754-Format gespeichert, das Vorzeichen, Exponent und Mantisse umfasst. Die Genauigkeit ist dynamisch und entspricht dem physischen Speicherformat des Werts, das genau 4 Byte für den Typ "FLOAT" und 8 Byte für "DOUBLE PRECISION" beträgt.

Angesichts der Besonderheiten beim Speichern von Gleitkommazahlen in einer Datenbank werden diese Datentypen nicht zum Speichern von Gelddaten empfohlen. Aus den gleichen Gründen werden Spalten mit Gleitkommadaten nicht für die Verwendung als Schlüssel oder für die Anwendung von Eindeutigkeitsbeschränkungen empfohlen.

Zum Testen von Daten in Spalten mit Gleitkomma-Datentypen sollten Ausdrücke anhand eines Bereichs, beispielsweise BETWEEN, prüfen, anstatt nach genauen Übereinstimmungen zu suchen.

Bei der Verwendung dieser Datentypen in Ausdrücken ist bei der Rundung der Auswertungsergebnisse äußerste Vorsicht geboten.

3.2.1. FLOAT

Der Datentyp FLOAT hat eine ungefähre Genauigkeit von 7 Stellen nach dem Komma. Um die Sicherheit der Lagerung zu gewährleisten, verlassen Sie sich auf 6 Ziffern.

3.2.2. DOUBLE PRECISION

Der Datentyp "DOUBLE PRECISION" wird mit einer ungefähren Genauigkeit von 15 Stellen gespeichert.

3.3. Festkomma-Datentypen

Festkomma-Datentypen stellen die Vorhersagbarkeit von Multiplikations- und Divisionsoperationen sicher und machen sie zur ersten Wahl zum Speichern von Geldwerten. Firebird implementiert zwei Festkomma-Datentypen: NUMERIC und DECIMAL. Beide Typen begrenzen laut Norm die gespeicherte Zahl auf die angegebene Skala (die Anzahl der Nachkommastellen).

Unterschiedliche Behandlungen begrenzen die Genauigkeit für jeden Typ: Die Genauigkeit für NUMERIC-Spalten ist genau „wie deklariert“, während DECIMAL-Spalten Zahlen akzeptieren, deren Genauigkeit mindestens der Deklaration entspricht.

HINWEIS: Das Verhalten von NUMERIC und DECIMAL in Firebird entspricht dem SQL-Standard DECIMAL; die Genauigkeit entspricht mindestens der Deklaration.

Beispielsweise definiert NUMERIC(4, 2) eine Zahl, die insgesamt aus vier Ziffern besteht, einschließlich zweier Nachkommastellen; das heißt, er kann bis zu zwei Ziffern vor dem Punkt und nicht mehr als zwei Ziffern nach dem Punkt haben. Wird in eine Spalte mit dieser Datentypdefinition die Zahl 3.1415 geschrieben, wird der Wert 3.14 in der Spalte NUMERIC(4, 2) gespeichert.

Die Deklarationsform für Festkommadaten, zB NUMERIC(p, s), ist beiden Typen gemeinsam. Es ist wichtig zu wissen, dass das Argument s in dieser Vorlage scale ist und nicht “eine Anzahl von Stellen nach dem Komma”. Das Verständnis des Mechanismus zum Speichern und Abrufen von Festkommadaten sollte dabei helfen, zu veranschaulichen, warum: Zum Speichern wird die Zahl mit 10s (10 hoch s) multipliziert und in eine ganze Zahl umgewandelt; beim Lesen wird die ganze Zahl zurückkonvertiert.

Die Methode zum Speichern von Festkommadaten im DBMS hängt von mehreren Faktoren ab: Deklarierte Genauigkeit, Datenbankdialekt, Deklarationstyp.

Tabelle 2. Methode der physischen Speicherung für reelle Zahlen
Skalierung Datentyp Dialekt 1 Dialekt 3

1 - 4

NUMERIC

SMALLINT

SMALLINT

1 - 4

DECIMAL

INTEGER

INTEGER

5 - 9

NUMERIC oder DECIMAL

INTEGER

INTEGER

10 - 18

NUMERIC oder DECIMAL

DOUBLE PRECISION

BIGINT

3.3.1. NUMERIC

Datenformat für die Deklaration
  NUMERIC
| NUMERIC(precision)
| NUMERIC(precision, scale)
Tabelle 3. NUMERIC-Typparameter
Parameter Beschreibung

precision

Präzision zwischen 1 und 18. Standardmäßig auf 9.

scale

Skala, zwischen 0 und scale. Standardmäßig auf 0.

Speicherbeispiele

Zusätzlich zu der obigen Erläuterung speichert das DBMS "NUMERISCHE" Daten gemäß der deklarierten Präzision und Skala. Einige weitere Beispiele sind:

NUMERIC(4) gespeichert als SMALLINT (genaue Daten)
NUMERIC(4,2)               SMALLINT (Daten * 102)
NUMERIC(10,4) (Dialekt 1)  DOUBLE PRECISION
              (Dialekt 3)  BIGINT (Daten * 104)

Denken Sie immer daran, dass das Speicherformat von der Genauigkeit abhängt. Zum Beispiel definieren Sie den Spaltentyp als NUMERIC(2,2) unter der Annahme, dass sein Wertebereich -0,99…​0,99 beträgt. Der tatsächliche Wertebereich für die Spalte beträgt jedoch -327,68..327,67, was darauf zurückzuführen ist, dass der Datentyp NUMERIC(2,2) im SMALLINT-Format gespeichert ist. Im Speicher sind die Datentypen NUMERIC(4,2), NUMERIC(3,2) und NUMERIC(2,2) tatsächlich gleich. Das heißt, wenn Sie wirklich Daten in einer Spalte mit dem Datentyp NUMERIC(2,2) speichern und den Bereich auf -0,99…​0,99 begrenzen möchten, müssen Sie dafür eine Einschränkung erstellen.

3.3.2. DECIMAL

Datendeklarationsformat
  DECIMAL
| DECIMAL(precision)
| DECIMAL(precision, scale)
Tabelle 4. DECIMAL-Typparameter
Parameter Beschreibung

precision

Präzision zwischen 1 und 18. Standardmäßig auf 9.

scale

Skala, zwischen 0 und scale. Standardmäßig auf 0.

Speicherbeispiele

Das Speicherformat in der Datenbank für DECIMAL ist NUMERIC sehr ähnlich, mit einigen Unterschieden, die anhand einiger weiterer Beispiele leichter zu erkennen sind:

DECIMAL(4) gespeichert als INTEGER (exact data)
DECIMAL(4,2)               INTEGER (data * 102)
DECIMAL(10,4) (Dialekt 1)  DOUBLE PRECISION
              (Dialekt 3)  BIGINT (data * 104)

3.4. Datentypen für Datum und Uhrzeit

Die Datentypen DATE, TIME und TIMESTAMP werden verwendet, um mit Daten zu arbeiten, die Datums- und Uhrzeitangaben enthalten. Dialekt 3 unterstützt alle drei Typen, während Dialekt 1 nur DATUM hat. Der DATE-Typ in Dialekt 3 ist “nur Datum”, während der DATE-Typ von Dialekt 1 sowohl Datum als auch Uhrzeit speichert, was TIMESTAMP in Dialekt 3 entspricht. Dialekt 1 hat keinen Typ “nur Datum”.

Dialekt 1 DATE Daten können alternativ als TIMESTAMP definiert werden und dies wird für neue Definitionen in Dialekt 1 Datenbanken empfohlen.

  1. Sekundenbruchteile Wenn Sekundenbruchteile in Datums- und Zeitdatentypen gespeichert sind, speichert Firebird sie auf Zehntausendstelsekunden. Wenn eine niedrigere Granularität bevorzugt wird, kann der Bruchteil in Dialekt-3-Datenbanken von ODS 11 oder höher explizit als Tausendstel, Hundertstel oder Zehntelsekunden angegeben werden.

Einige nützliche Informationen über die Genauigkeit im Sekundenbereich:

Der Zeitteil von TIME oder TIMESTAMP ist ein 4-Byte-WORD, mit Platz für Dezimillisekunden-Genauigkeit und Zeitwerte werden als die Anzahl der seit Mitternacht verstrichenen Dezimillisekunden gespeichert. Die tatsächliche Genauigkeit von Werten, die in Zeit(stempel)-Funktionen und -Variablen gespeichert oder daraus gelesen werden, beträgt:

  • CURRENT_TIME ist standardmäßig auf Sekunden genau und kann mit CURRENT_TIME (0|1|2|3) bis auf Millisekunden genau angegeben werden

  • CURRENT_TIMESTAMP Millisekunden-Genauigkeit. Die Genauigkeit von Sekunden bis Millisekunden kann mit CURRENT_TIMESTAMP (0|1|2|3) specified angegeben werden

  • Literal 'NOW': Millisekunden-Präzision

  • Die Funktionen DATEADD() und DATEDIFF() unterstützen eine Genauigkeit von bis zu Millisekunden. Dezi-Millisekunden können angegeben werden, sie werden jedoch auf die nächste ganze Zahl gerundet, bevor eine Operation ausgeführt wird

  • Die Funktion EXTRACT() gibt mit den Argumenten SECOND und MILLISECOND eine Genauigkeit von Dezi-Millisekunden zurück

  • Für TIME- und TIMESTAMP-Literale akzeptiert Firebird gerne eine Genauigkeit von Dezi-Millisekunden, kürzt (nicht rundet) den Zeitteil jedoch auf die nächste kleinere oder gleiche Millisekunde. Versuchen Sie zum Beispiel SELECT TIME '14:37:54.1249' FROM rdb$database

  • die Operatoren ‘+’ und ‘-’ arbeiten mit Dezimillisekunden-Genauigkeit, aber nur innerhalb des Ausdrucks. Sobald etwas gespeichert oder auch nur aus RDB$DATABASE ausgewählt wird, geht es auf Millisekunden genau zurück

Die Genauigkeit von Dezi-Millisekunden ist selten und wird derzeit nicht in Spalten oder Variablen gespeichert. Die beste Annahme aus all dem ist, dass, obwohl Firebird TIME und die TIMESTAMP-Zeitteilwerte als die Anzahl der seit Mitternacht verstrichenen Dezi-Millisekunden (10-4 Sekunden) speichert, die tatsächliche Genauigkeit variieren kann von Sekunden bis Millisekunden.

3.4.1. DATE

Der Datentyp DATE in Dialect 3 speichert nur das Datum ohne Uhrzeit. Der verfügbare Bereich zum Speichern von Daten reicht vom 01. Januar bis zum 31. Dezember 9999.

Dialekt 1 hat keinen Typ “Nur Datum”.

In Dialekt 1 erhalten Datumsliterale ohne Zeitteil sowie 'TODAY', 'YESTERDAY' und 'TOMORROW' automatisch einen Null-Zeitteil.

Wenn es Ihnen aus irgendeinem Grund wichtig ist, ein Dialekt-1-Zeitstempelliteral mit einem expliziten Zeitteil Null zu speichern, akzeptiert die Engine ein Literal wie '2016-12-25 00:00:00.0000'. Allerdings hätte '2016-12-25' genau den gleichen Effekt, mit weniger Tastenanschlägen!

3.4.2. TIME

Der Datentyp TIME ist nur in Dialekt 3 verfügbar. Es speichert die Tageszeit im Bereich von 00:00:00.0000 bis 23:59:59,9999.

Wenn Sie den Zeitteil von DATE in Dialekt 1 benötigen, können Sie die EXTRACT-Funktion verwenden.

Beispiele für die Verwendung von EXTRACT()
EXTRACT (HOUR FROM DATE_FIELD)
EXTRACT (MINUTE FROM DATE_FIELD)
EXTRACT (SECOND FROM DATE_FIELD)

Siehe auch EXTRACT()-Funktion im Kapitel Eingebaute Funktionen.

3.4.3. TIMESTAMP

Der Datentyp TIMESTAMP ist in Dialekt 3 und Dialekt 1 verfügbar. Es besteht aus zwei 32-Bit-Wörtern – einem Datumsteil und einem Zeitteil – um eine Struktur zu bilden, die sowohl Datum als auch Uhrzeit speichert. Es ist das gleiche wie der Typ DATE in Dialekt 1.

Die Funktion EXTRACT funktioniert mit TIMESTAMP genauso gut wie mit dem Dialekt 1 DATE-Typ.

3.4.4. Operationen mit Datums- und Uhrzeitwerten

Die Methode der Speicherung von Datums- und Uhrzeitwerten ermöglicht es, diese als Operanden in einige arithmetische Operationen einzubeziehen. Im Speicher wird ein Datumswert oder ein Datumsteil eines Zeitstempels als die Anzahl von Tagen dargestellt, die seit “Datum Null” - 17. November 1898 - verstrichen sind, während ein Zeitwert oder der Zeitteil eines Zeitstempels dargestellt wird als Anzahl der Sekunden (mit Berücksichtigung von Sekundenbruchteilen) seit Mitternacht.

Ein Beispiel ist das Subtrahieren eines früheren Datums, einer früheren Zeit oder eines Zeitstempels von einem späteren, was zu einem Zeitintervall in Tagen und Bruchteilen von Tagen führt.

Tabelle 5. Arithmetische Operationen für Datums- und Uhrzeitdatentypen
Operand 1 Operation Operand 2 Ergebnis

DATE

+

TIME

TIMESTAMP

DATE

+

Numerischer Wert n

DATE um n ganze Tage erhöht. Gebrochene Werte werden auf die nächste Ganzzahl gerundet (nicht abgeschnitten).

TIME

+

DATE

TIMESTAMP

TIME

+

Numerischer Wert n

TIME um n Sekunden erhöht. Bruchteile werden berücksichtigt.

TIMESTAMP

+

Numerischer Wert n

TIMESTAMP, wobei das Datum um die Anzahl der Tage und der Teil eines Tages durch die Zahl n repräsentiert wird — somit wird “+ 2.75” das Datum um 2 Tage und 18 Stunden weiterstellen wird

DATE

-

DATE

Anzahl der vergangenen Tage innerhalb des Bereichs DECIMAL(9, 0)

DATE

-

Numerischer Wert n

DATE um n ganze Tage reduziert. Gebrochene Werte werden auf die nächste Ganzzahl gerundet (nicht abgeschnitten).

TIME

-

TIME

Anzahl der vergangenen Sekunden, innerhalb des Bereichs DECIMAL(9, 4)

TIME

-

Numerischer Wert n

TIME um n Sekunden reduziert. Bruchteile werden berücksichtigt.

TIMESTAMP

-

TIMESTAMP

Anzahl der Tage und der Tageszeit, innerhalb des Bereichs DECIMAL(18, 9)

TIMESTAMP

-

Numerischer Wert n

TIMESTAMP wobei das Datum sich auf der Anzahl der Tage und der Tageszeit beruht, die durch die Zahl n repräsentiert wird — somit wird “- 2.25” das Datum um 2 Tage und 6 Stunden reduzieren.

Hinweise

Der Typ DATE wird in Dialekt 1 als TIMESTAMP betrachtet.

Siehe auch

DATEADD, DATEDIFF

3.5. Zeichendatentypen

Für die Arbeit mit Zeichendaten hat Firebird die Datentypen CHAR mit fester Länge und VARCHAR mit variabler Länge. Die maximale Größe der in diesen Datentypen gespeicherten Textdaten beträgt 32.767 Byte für 'CHAR' und 32.765 Byte für 'VARCHAR'. Die maximale Anzahl von Zeichen, die in diese Grenzen passt, hängt davon ab, welches CHARACTER SET für die betrachteten Daten verwendet wird. Die Sortierreihenfolge hat keinen Einfluss auf dieses Maximum, kann sich jedoch auf die maximale Größe eines Index auswirken, der die Spalte umfasst.

Wenn beim Definieren eines Zeichenobjekts kein Zeichensatz explizit angegeben wird, wird der beim Erstellen der Datenbank angegebene Standardzeichensatz verwendet. Wenn in der Datenbank kein Standardzeichensatz definiert ist, erhält das Feld den Zeichensatz NONE.

3.5.1. Unicode

Die meisten aktuellen Entwicklungstools unterstützen Unicode, implementiert in Firebird mit den Zeichensätzen UTF8 und UNICODE_FSS. UTF8 enthält Kollationen für viele Sprachen. UNICODE_FSS ist eingeschränkter und wird hauptsächlich von Firebird intern zum Speichern von Metadaten verwendet. Beachten Sie, dass ein UTF8-Zeichen bis zu 4 Byte belegt, wodurch die Größe von CHAR-Feldern auf 8.191 Zeichen (32.767/4) begrenzt ist.

Der tatsächliche Wert von “Bytes pro Zeichen” hängt vom Bereich ab, zu dem das Zeichen gehört. Lateinische Buchstaben ohne Akzent belegen 1 Byte, kyrillische Buchstaben der Codierung WIN1251 belegen 2 Byte in UTF8, Zeichen anderer Codierungen können bis zu 4 Byte belegen.

Der in Firebird implementierte UTF8-Zeichensatz unterstützt die neueste Version des Unicode-Standards und empfiehlt daher seine Verwendung für internationale Datenbanken.

3.5.2. Client-Zeichensatz

Bei der Arbeit mit Strings ist es wichtig, den Zeichensatz der Client-Verbindung im Auge zu behalten. Wenn die Zeichensätze der gespeicherten Daten nicht mit denen der Client-Verbindung übereinstimmen, werden die Ausgabeergebnisse für String-Spalten automatisch neu codiert, sowohl beim Senden der Daten vom Client an den Server als auch beim Zurücksenden von der Server an den Client. Wenn die Datenbank beispielsweise in der Codierung WIN1251 erstellt wurde, aber KOI8R oder UTF8 in den Verbindungsparametern des Clients angegeben ist, ist die Abweichung transparent.

3.5.3. Sonderzeichensätze

Zeichensatz NONE

Der Zeichensatz NONE ist ein Sonderzeichensatz in Firebird. Es kann so charakterisiert werden, dass jedes Byte Teil einer Zeichenkette ist, die Zeichenkette jedoch im System ohne Hinweise darauf gespeichert wird, was ein Zeichen darstellt: Zeichencodierung, Sortierung, Groß-/Kleinschreibung usw. sind einfach unbekannt. Es liegt in der Verantwortung der Clientanwendung, mit den Daten umzugehen und die Mittel bereitzustellen, um die Bytefolge auf eine für die Anwendung und den menschlichen Benutzer sinnvolle Weise zu interpretieren.

Zeichensatz OCTETS

Daten in der OCTETS-Kodierung werden als Bytes behandelt, die möglicherweise nicht wirklich als Zeichen interpretiert werden. OCTETS bietet eine Möglichkeit, Binärdaten zu speichern, die das Ergebnis einiger Firebird-Funktionen sein können. Die Datenbank-Engine hat keine Vorstellung davon, was sie mit einer Bitfolge in OCTETS tun soll, außer sie nur zu speichern und abzurufen. Auch hier ist die Clientseite dafür verantwortlich, die Daten zu validieren, sie in für die Anwendung und ihre Benutzer sinnvollen Formaten darzustellen und alle Ausnahmen zu behandeln, die sich aus der Decodierung und Codierung ergeben.

3.5.4. Sortierreihenfolge

Jeder Zeichensatz hat eine Standardkollatierungssequenz (COLLATE), die die Sortierreihenfolge angibt. Normalerweise ist dies nichts anderes als eine Sortierung basierend auf dem numerischen Code der Zeichen und eine grundlegende Zuordnung von Groß- und Kleinbuchstaben. Wenn für Strings ein Verhalten erforderlich ist, das nicht von der Standardsortierreihenfolge bereitgestellt wird, und eine geeignete alternative Kallation für diesen Zeichensatz unterstützt wird, kann eine COLLATE collation-Klausel in der Spaltendefinition angegeben werden.

Eine COLLATE collation-Klausel kann neben der Spaltendefinition auch in anderen Kontexten angewendet werden. Für Größer-als/Kleiner-Vergleichsoperationen kann es in der WHERE-Klausel einer SELECT-Anweisung hinzugefügt werden. Wenn die Ausgabe in einer speziellen alphabetischen Reihenfolge oder ohne Beachtung der Groß-/Kleinschreibung sortiert werden muss und die entsprechende Sortierung vorhanden ist, kann eine COLLATE-Klausel in die ORDER BY-Klausel eingefügt werden, wenn Zeilen nach einem Zeichenfeld sortiert werden und mit die GROUP BY-Klausel bei Gruppierungsoperationen.

Suche ohne Berücksichtigung der Groß-/Kleinschreibung

Für eine Suche ohne Beachtung der Groß-/Kleinschreibung könnte die Funktion UPPER verwendet werden, um sowohl das Suchargument als auch die gesuchten Zeichenfolgen in Großbuchstaben umzuwandeln, bevor eine Übereinstimmung versucht wird:

…
where upper(name) = upper(:flt_name)

Bei Zeichenfolgen in einem Zeichensatz, der eine Sortierung ohne Beachtung der Groß-/Kleinschreibung zur Verfügung hat, können Sie einfach die Sortierung anwenden, um das Suchargument und die gesuchten Zeichenfolgen direkt zu vergleichen. Wenn Sie beispielsweise den Zeichensatz WIN1251 verwenden, ist die Sortierung PXW_CYRL zu diesem Zweck unabhängig von der Groß-/Kleinschreibung:

…
WHERE FIRST_NAME COLLATE PXW_CYRL >= :FLT_NAME
…
ORDER BY NAME COLLATE PXW_CYRL
Siehe auch

CONTAINING

UTF8-Sortierreihenfolgen

Die folgende Tabelle zeigt die möglichen Sortierfolgen für den Zeichensatz UTF8.

Tabelle 6. Sortierfolgen für Zeichensatz UTF8
Kollation Eigenschaften

UCS_BASIC

Die Sortierung funktioniert nach der Position des Zeichens in der Tabelle (binär). In Firebird 2.0 hinzugefügt

UNICODE

Die Sortierung funktioniert nach dem UCA-Algorithmus (Unicode Collation Algorithm) (alphabetisch). In Firebird 2.0 hinzugefügt

UTF8

Die standardmäßige, binäre Sortierung, identisch mit UCS_BASIC, die aus Gründen der SQL-Kompatibilität hinzugefügt wurde

UNICODE_CI

Sortierung ohne Berücksichtigung der Groß-/Kleinschreibung, funktioniert ohne Berücksichtigung der Groß-/Kleinschreibung. Hinzugefügt in Firebird 2.1

UNICODE_CI_AI

Groß-/Kleinschreibung, akzentunabhängige Sortierung, arbeitet alphabetisch ohne Berücksichtigung von Groß-/Kleinschreibung oder Akzenten. Hinzugefügt in Firebird 2.5

Beispiel

Ein Beispiel für die Sortierung für den UTF8-Zeichensatz ohne Berücksichtigung der Groß-/Kleinschreibung oder der Akzentuierung von Zeichen (ähnlich wie COLLATE PXW_CYRL).

...
ORDER BY NAME COLLATE UNICODE_CI_AI

3.5.5. Zeichenindizes

In Firebird vor Version 2.0 kann ein Problem beim Erstellen eines Indexes für Zeichenspalten auftreten, die eine nicht standardmäßige Kollatierungssequenz verwenden: Die Länge eines indizierten Felds ist auf 252 Byte begrenzt, wenn COLLATE nicht angegeben ist, oder 84 Byte, wenn `COLLATE ` ist angegeben. Multi-Byte-Zeichensätze und zusammengesetzte Indizes begrenzen die Größe noch weiter.

Ab Firebird 2.0 beträgt die maximale Länge für einen Index ein Viertel der Seitengröße, d.h. von 1.024 — für Seitengröße 4.096 — bis 4.096 Bytes — für Seitengröße 16.384. Die maximale Länge einer indizierten Zeichenfolge beträgt 9 Byte weniger als diese Viertelseitenbegrenzung.

Berechnen der maximalen Länge eines indizierten Zeichenfolgenfelds

Die folgende Formel berechnet die maximale Länge einer indizierten Zeichenfolge (in Zeichen):

max_char_length = FLOOR((page_size / 4 - 9) / N)

wobei N die Anzahl der Bytes pro Zeichen im Zeichensatz ist.

Die folgende Tabelle zeigt die maximale Länge einer indizierten Zeichenfolge (in Zeichen), je nach Seitengröße und Zeichensatz, berechnet mit dieser Formel.

Tabelle 7. Maximale Indexlängen nach Seitengröße und Zeichengröße

Seitengröße

Bytes je Zeichen

1

2

3

4

6

4.096

1.015

507

338

253

169

8.192

2.039

1.019

679

509

339

16.384

4.087

2.043

1.362

1.021

682

Bei Sortierungen, bei denen die Groß-/Kleinschreibung nicht beachtet wird (“_CI”), belegt ein Zeichen im index nicht 4, sondern 6 (sechs) Bytes, sodass die maximale Schlüssellänge für eine Seite von z 169 Zeichen.

3.5.6. Zeichenarten im Detail

CHAR

CHAR ist ein Datentyp mit fester Länge. Wenn die eingegebene Anzahl von Zeichen kleiner als die angegebene Länge ist, werden dem Feld abschließende Leerzeichen hinzugefügt. Im Allgemeinen muss das Auffüllzeichen kein Leerzeichen sein: Es hängt vom Zeichensatz ab. Das Füllzeichen für den Zeichensatz OCTETS ist beispielsweise null.

Der vollständige Name dieses Datentyps ist CHARACTER, aber es ist nicht erforderlich, vollständige Namen zu verwenden, und die Leute tun dies selten.

Zeichendaten mit fester Länge können verwendet werden, um Codes zu speichern, deren Länge Standard ist und eine bestimmte "Breite" in Verzeichnissen hat. Ein Beispiel für einen solchen Code ist ein EAN13-Barcode – 13 Zeichen, alle ausgefüllt.

Deklarationssyntax
{CHAR | CHARACTER} [(length)]
  [CHARACTER SET <set>] [COLLATE <name>]

Wenn keine Länge length angegeben ist, wird sie mit 1 angenommen.

Eine gültige Länge length reicht von 1 bis zur maximalen Anzahl von Zeichen, die innerhalb von 32.767 Bytes untergebracht werden können.

Formal ist die COLLATE-Klausel nicht Teil der Datentyp-Deklaration und ihre Position hängt von der Syntax der Anweisung ab.

VARCHAR

VARCHAR ist der grundlegende Stringtyp zum Speichern von Texten variabler Länge, bis maximal 32.765 Byte. Die gespeicherte Struktur entspricht der tatsächlichen Größe der Daten plus 2 Byte, wobei die Länge der Daten aufgezeichnet wird.

Alle Zeichen, die von der Clientanwendung an die Datenbank gesendet werden, werden als aussagekräftig angesehen, einschließlich der führenden und abschließenden Leerzeichen. Nachgestellte Leerzeichen werden jedoch nicht gespeichert: Sie werden beim Abrufen bis zur aufgezeichneten Länge der Zeichenfolge wiederhergestellt.

Der vollständige Name dieses Typs ist CHARACTER VARYING. Eine andere Variante des Namens wird als CHAR VARYING geschrieben.

Syntax
{VARCHAR | {CHAR | CHARACTER} VARYING} (length)
  [CHARACTER SET <set>] [COLLATE <name>]

Formal ist die COLLATE-Klausel nicht Teil der Datentyp-Deklaration und ihre Position hängt von der Syntax der Anweisung ab.

NCHAR

NCHAR ist ein Zeichendatentyp fester Länge mit dem vordefinierten Zeichensatz ISO8859_1. Ansonsten ist es dasselbe wie CHAR.

Syntax
{NCHAR | NATIONAL {CHAR | CHARACTER}} [(length)]

Wenn keine Länge length angegeben ist, wird sie mit 1 angenommen.

Ein ähnlicher Datentyp ist für den String-Typ variabler Länge verfügbar: NATIONAL {CHAR | CHARAKTER} VERSCHIEDLICH.

3.6. Boolean-Datentyp

Firebird 3.0 führte einen vollwertigen booleschen Datentyp ein.

3.6.1. BOOLEAN

Der SQL:2008-konforme Datentyp BOOLEAN (8 Bit) umfasst die unterschiedlichen Wahrheitswerte TRUE und FALSE. Sofern nicht durch eine NOT NULL-Beschränkung verboten, unterstützt der BOOLEAN-Datentyp auch den Wahrheitswert UNKNOWN als Nullwert. Die Spezifikation macht keinen Unterschied zwischen dem NULL-Wert dieses Datentyps und dem Wahrheitswert UNKNOWN, der das Ergebnis eines SQL-Prädikats, einer Suchbedingung oder eines booleschen Wertausdrucks ist: Sie sind austauschbar und bedeuten das gleiche.

Wie bei vielen Programmiersprachen können die BOOLEAN-Werte von SQL mit impliziten Wahrheitswerten getestet werden. Beispielsweise sind field1 OR field2 und NOT field1 gültige Ausdrücke.

Der IS-Operator

Prädikate können den Operator Boolean IS [NOT] zum Abgleich verwenden. Zum Beispiel field1 IS FALSE oder field1 IS NOT TRUE.

  • Äquivalenzoperatoren (“=”, “!=”, “<>” und so weiter) sind in allen Vergleichen gültig.

BOOLEAN-Beispiele
  1. Einfügen und abfragen

    CREATE TABLE TBOOL (ID INT, BVAL BOOLEAN);
    COMMIT;
    
    INSERT INTO TBOOL VALUES (1, TRUE);
    INSERT INTO TBOOL VALUES (2, 2 = 4);
    INSERT INTO TBOOL VALUES (3, NULL = 1);
    COMMIT;
    
    SELECT * FROM TBOOL;
              ID    BVAL
    ============ =======
               1 <true>
               2 <false>
               3 <null>
  2. Test auf Wert TRUE

    SELECT * FROM TBOOL WHERE BVAL;
              ID    BVAL
    ============ =======
               1 <true>
  3. Test auf Wert FALSE

    SELECT * FROM TBOOL WHERE BVAL IS FALSE;
              ID    BVAL
    ============ =======
               2 <false>
  4. Test auf Wert UNKNOWN

    SELECT * FROM TBOOL WHERE BVAL IS UNKNOWN;
              ID    BVAL
    ============ =======
               3 <null>
  5. Boolean-Werte in SELECT-Anweisung

    SELECT ID, BVAL, BVAL AND ID < 2
      FROM TBOOL;
              ID    BVAL
    ============ ======= =======
               1 <true>  <true>
               2 <false> <false>
               3 <null>  <false>
  6. PSQL-Deklaration mit Startwert

    DECLARE VARIABLE VAR1 BOOLEAN = TRUE;
  7. Gültige Syntax, aber wie bei einem Vergleich mit NULL, wird nie ein Datensatz zurückgegeben

SELECT * FROM TBOOL WHERE BVAL = UNKNOWN;
SELECT * FROM TBOOL WHERE BVAL <> UNKNOWN;
Verwendung von Boolean gegen andere Datentypen

Obwohl BOOLEAN von Natur aus in keinen anderen Datentyp konvertierbar ist, werden ab Version 3.0.1 die Strings 'true' und 'false' (Groß-/Kleinschreibung nicht beachtet) in Wertausdrücken implizit in BOOLEAN umgewandelt, z.B.

if (true > 'false') then ...

'false' wird in BOOLEAN umgewandelt. Jeder Versuch, die booleschen Operatoren AND, NOT, OR und IS zu verwenden, schlägt fehl. NOT 'False' ist beispielsweise ungültig.

Ein BOOLEAN kann mit CAST explizit in und aus einem String umgewandelt werden. UNKNOWN ist für keine Form des Castings verfügbar.

Weitere Hinweise
  • Der Typ wird in der API mit dem Typ FB_BOOLEAN und den Konstanten FB_TRUE und FB_FALSE dargestellt.

  • Der Wert TRUE ist größer als der Wert FALSE.

3.7. Binärdatentypen

BLOBs (Binary Large Objects) sind komplexe Strukturen, die verwendet werden, um Text und binäre Daten undefinierter Länge, oft sehr groß, zu speichern.

Syntax
BLOB [SUB_TYPE <subtype>]
  [SEGMENT SIZE <segment size>]
  [CHARACTER SET <character set>]
  [COLLATE <collation name>]
Verkürzte Syntax
BLOB (<segment size>)
BLOB (<segment size>, <subtype>)
BLOB (, <subtype>)

Formal ist die COLLATE-Klausel nicht Teil der Datentyp-Deklaration und ihre Position hängt von der Syntax der Anweisung ab.

Segmentgröße
Die Angabe der BLOB-Segmentgröße ist ein Rückfall in vergangene Zeiten, als Anwendungen zum Arbeiten mit BLOB-Daten in C (Embedded SQL) mit Hilfe des Pre-Compilers gpre geschrieben wurden. Heutzutage ist es praktisch irrelevant. Die Segmentgröße für BLOB-Daten wird von der Clientseite bestimmt und ist in der Regel auf jeden Fall größer als die Datenseitengröße.

3.7.1. BLOB-Untertypen

Der optionale Parameter SUB_TYPE gibt die Art der in die Spalte geschriebenen Daten an. Firebird bietet zwei vordefinierte Untertypen zum Speichern von Benutzerdaten:

Subtyp 0: BINARY

Wenn kein Subtyp angegeben wird, wird angenommen, dass die Spezifikation für nicht typisierte Daten gilt, und der Standardwert SUB_TYPE 0 wird angewendet. Der Alias ​​für den Subtyp null ist BINARY. Dies ist der Untertyp, um anzugeben, ob es sich bei den Daten um eine Binärdatei oder einen Stream handelt: Bilder, Audio, Textverarbeitungsdateien, PDFs usw.

Untertyp 1: TEXT

Subtyp 1 hat einen Alias, TEXT, der in Deklarationen und Definitionen verwendet werden kann. Zum Beispiel BLOB SUB_TYPE TEXT. Es ist ein spezialisierter Untertyp, der verwendet wird, um Nur-Text-Daten zu speichern, die zu groß sind, um in einen String-Typ zu passen. Ein CHARACTER SET kann angegeben werden, wenn das Feld Text mit einer anderen Kodierung als der für die Datenbank angegebenen speichern soll. Ab Firebird 2.0 wird auch eine COLLATE-Klausel unterstützt.

Die Angabe eines CHARACTER SET ohne SUB_TYPE impliziert SUB_TYPE TEXT.

Benutzerdefinierte Untertypen

Es ist auch möglich, benutzerdefinierte Datenuntertypen hinzuzufügen, für die der Aufzählungsbereich von -1 bis -32.768 reserviert ist. Benutzerdefinierte Subtypen, die mit positiven Zahlen aufgezählt werden, sind nicht zulässig, da die Firebird-Engine die Zahlen ab 2 aufwärts für einige interne Subtypen in Metadaten verwendet.

3.7.2. BLOB-Besonderheiten

Größe

Die maximale Größe eines 'BLOB'-Feldes ist auf 4 GB begrenzt, unabhängig davon, ob der Server 32-Bit oder 64-Bit ist. (Die internen Strukturen, die sich auf BLOBs beziehen, unterhalten ihre eigenen 4-Byte-Zähler.) Bei einer Seitengröße von 4 KB (4096 Byte) ist die maximale Größe geringer – etwas weniger als 2 GB.

Operationen und Ausdrücke

Text-BLOBs beliebiger Länge und beliebiger Zeichensätze – auch Multibyte – können Operanden für praktisch jede Anweisung oder interne Funktion sein. Die folgenden Operatoren werden vollständig unterstützt:

=

(Zuordnung)

=, <>, <, ⇐, >, >=

(Vergeleich)

||

(Verkettung)

BETWEEN,

IS [NOT] DISTINCT FROM,

IN,

ANY | SOME,

ALL

 

Teilunterstützung:

  • Bei diesen tritt ein Fehler auf, wenn das Suchargument größer oder gleich 32 KB ist:

    STARTING [WITH],

    LIKE,

    CONTAINING

     

  • Aggregationsklauseln wirken sich nicht auf den Inhalt des Feldes selbst aus, sondern auf die BLOB-ID. Abgesehen davon gibt es einige Macken:

    SELECT DISTINCT

    gibt fälschlicherweise mehrere NULL-Werte zurück, wenn sie vorhanden sind

    ORDER BY

     — 

    GROUP BY

    verkettet dieselben Zeichenfolgen, wenn sie nebeneinander liegen, tut dies jedoch nicht, wenn sie voneinander entfernt sind

BLOB-Speicher
  • Standardmäßig wird für jedes BLOB ein regulärer Datensatz erstellt und auf einer ihm zugeordneten Datenseite gespeichert. Passt das gesamte BLOB auf diese Seite, wird es als level 0 BLOB bezeichnet. Die Nummer dieses Sondersatzes wird im Tabellensatz gespeichert und belegt 8 Byte.

  • Wenn ein BLOB nicht auf eine Datenseite passt, wird sein Inhalt auf separate, ihm exklusiv zugeordnete Seiten (Blob-Seiten) gelegt, während die Nummern dieser Seiten im BLOB-Record gespeichert werden. Dies ist ein Level 1 BLOB.

  • Wenn das Array von Seitennummern, das die BLOB-Daten enthält, nicht auf eine Datenseite passt, wird das Array auf separate Blob-Seiten gelegt, während die Nummern dieser Seiten in den BLOB-Datensatz geschrieben werden. Dies ist ein Level-2-BLOB.

  • Level höher als 2 werden nicht unterstützt.

Siehe auch

FILTER, DECLARE FILTER

3.7.3. ARRAY-Datentyp

Die Unterstützung von Arrays im Firebird DBMS ist eine Abkehr vom traditionellen relationalen Modell. Die Unterstützung von Arrays im DBMS könnte die Lösung einiger Datenverarbeitungsaufgaben mit großen Mengen ähnlicher Daten erleichtern.

Arrays in Firebird werden in BLOB eines spezialisierten Typs gespeichert. Arrays können eindimensional und mehrdimensional sein und jeden Datentyp außer BLOB und ARRAY haben.

Beispiel
CREATE TABLE SAMPLE_ARR (
  ID INTEGER NOT NULL PRIMARY KEY,
  ARR_INT INTEGER [4]
);

In diesem Beispiel wird eine Tabelle mit einem Feld vom Typ Array erstellt, das aus vier ganzen Zahlen besteht. Die Indizes dieses Arrays sind von 1 bis 4.

Angeben von expliziten Grenzen für Bemaßungen

Standardmäßig sind Dimensionen 1-basiert – tiefgestellte Indizes werden ab 1 nummeriert. Verwenden Sie die folgende Syntax, um explizite Ober- und Untergrenzen der tiefgestellten Werte anzugeben:

'[' <lower>:<upper> ']'
Hinzufügen weiterer Dimensionen

Eine neue Dimension wird mit einem Komma in der Syntax hinzugefügt. In diesem Beispiel erstellen wir eine Tabelle mit einem zweidimensionalen Array, wobei die Untergrenze der Indizes in beiden Dimensionen bei Null beginnt:

CREATE TABLE SAMPLE_ARR2 (
  ID INTEGER NOT NULL PRIMARY KEY,
  ARR_INT INTEGER [0:3, 0:3]
);

Das DBMS bietet nicht viel an Sprache oder Werkzeugen, um mit dem Inhalt von Arrays zu arbeiten. Die Datenbank employee.fdb, die sich im Verzeichnis ../examples/empbuild eines Firebird-Distributionspakets befindet, enthält eine gespeicherte Beispielprozedur, die einige einfache Arbeiten mit Arrays zeigt:

PSQL-Quelle für SHOW_LANGS, eine Prozedur mit einem Array
CREATE OR ALTER PROCEDURE SHOW_LANGS (
  CODE VARCHAR(5),
  GRADE SMALLINT,
  CTY VARCHAR(15))
RETURNS (LANGUAGES VARCHAR(15))
AS
  DECLARE VARIABLE I INTEGER;
BEGIN
  I = 1;
  WHILE (I <= 5) DO
  BEGIN
    SELECT LANGUAGE_REQ[:I]
    FROM JOB
    WHERE (JOB_CODE = :CODE)
      AND (JOB_GRADE = :GRADE)
      AND (JOB_COUNTRY = :CTY)
      AND (LANGUAGE_REQ IS NOT NULL))
    INTO :LANGUAGES;

    IF (LANGUAGES = '') THEN
    /* 'NULL' ANSTELLE VON LEERZEICHEN AUSGEBEN */
      LANGUAGES = 'NULL';
    I = I +1;
    SUSPEND;
  END
END

Wenn die beschriebenen Funktionen für Ihre Aufgaben ausreichen, können Sie in Ihren Projekten Arrays verwenden. Derzeit sind keine Verbesserungen geplant, um die Unterstützung für Arrays in Firebird zu verbessern.

3.8. Spezielle Datentypen

“Spezielle” Datentypen …​

3.8.1. SQL_NULL-Datentypen

Der Typ SQL_NULL enthält keine Daten, sondern nur einen Zustand: NULL oder NOT NULL. Als Datentyp zum Deklarieren von Tabellenfeldern, PSQL-Variablen oder Parameterbeschreibungen steht er nicht zur Verfügung. Es wurde hinzugefügt, um die Verwendung nicht typisierter Parameter in Ausdrücken zu unterstützen, die das Prädikat IS NULL beinhalten.

Ein Auswertungsproblem tritt auf, wenn optionale Filter verwendet werden, um Abfragen des folgenden Typs zu schreiben:

WHERE col1 = :param1 OR :param1 IS NULL

Nach der Verarbeitung auf API-Ebene sieht die Abfrage wie folgt aus:

WHERE col1 = ? OR ? IS NULL

Dies ist ein Fall, in dem der Entwickler eine SQL-Abfrage schreibt und :param1 als eine Variable betrachtet, auf die er zweimal verweisen kann. Auf API-Ebene enthält die Abfrage jedoch zwei separate und unabhängige _Parameter. Der Server kann den Typ des zweiten Parameters nicht bestimmen, da er mit IS NULL verknüpft ist.

Der Datentyp SQL_NULL löst dieses Problem. Immer wenn die Engine in einer Abfrage auf ein Prädikat “? IS NULL” stößt, weist sie dem Parameter den Typ SQL_NULL zu, was anzeigt, dass es sich bei dem Parameter nur um “Nulligkeit” und den Datentyp handelt oder der Wert muss nicht angesprochen werden.

Das folgende Beispiel zeigt die Anwendung in der Praxis. Es nimmt zwei benannte Parameter an — sagen wir :size und :colour — die zum Beispiel Werte aus Bildschirmtextfeldern oder Dropdown-Listen erhalten können. Jeder benannte Parameter entspricht zwei Positionsparametern in der Abfrage.

SELECT
  SH.SIZE, SH.COLOUR, SH.PRICE
FROM SHIRTS SH
WHERE (SH.SIZE = ? OR ? IS NULL)
  AND (SH.COLOUR = ? OR ? IS NULL)

Um zu erklären, was hier passiert, wird davon ausgegangen, dass der Leser mit der Firebird-API und der Übergabe von Parametern in XSQLVAR-Strukturen vertraut ist — was unter der Oberfläche passiert, ist für diejenigen nicht von Interesse, die keine Treiber oder Anwendungen schreiben, die mit der "nakten" API kommunizieren.

Die Anwendung übergibt die parametrisierte Anfrage an den Server in der üblichen positionellen ?-Form. Paare von “identischen” Parametern können nicht zu einem zusammengeführt werden, daher werden beispielsweise für zwei optionale Filter vier Positionsparameter benötigt: einer für jedes ? in unserem Beispiel.

Nach dem Aufruf von isc_dsql_describe_bind() wird der SQLTYPE des zweiten und vierten Parameters auf SQL_NULL gesetzt. Firebird hat keine Kenntnis von ihrer speziellen Beziehung zum ersten und dritten Parameter: Diese Verantwortung liegt vollständig auf der Anwendungsseite.

Nachdem die Werte für Größe und Farbe vom Benutzer festgelegt (oder nicht festgelegt) wurden und die Abfrage ausgeführt werden soll, muss jedes Paar von `XSQLVAR`s wie folgt gefüllt werden:

Der Benutzer hat einen Wert angegeben

Erster Parameter (Wertvergleich): setze *sqldata auf den angegebenen Wert und *sqlind auf 0 (für NOT NULL)

Zweiter Parameter (NULL Test): setze sqldata auf null (Nullzeiger, nicht SQL NULL) und *sqlind auf 0 (für NOT NULL)

Der Benutzer hat das Feld leer gelassen

Beide Parameter: setze sqldata auf null (Nullzeiger, nicht SQL NULL) und *sqlind auf -1 (zeigt NULL)

Mit anderen Worten: Der Parameter Wertvergleich wird immer wie gewohnt gesetzt. Der Parameter SQL_NULL wird gleich gesetzt, außer dass sqldata immer null bleibt.

3.9. Konvertierung von Datentypen

Beim Verfassen eines Ausdrucks oder der Angabe einer Operation sollte das Ziel sein, kompatible Datentypen für die Operanden zu verwenden. Wenn eine Mischung von Datentypen verwendet werden muss, sollten Sie nach einer Möglichkeit suchen, inkompatible Operanden zu konvertieren, bevor Sie sie der Operation unterziehen. Die Möglichkeit, Daten zu konvertieren, kann durchaus ein Problem darstellen, wenn Sie mit Dialekt-1-Daten arbeiten.

3.9.1. Explizite Datentypkonvertierung

Die CAST-Funktion ermöglicht die explizite Konvertierung zwischen vielen Paaren von Datentypen.

Syntax
CAST (<expression> AS <target_type>)

<target_type> ::= <domain_or_non_array_type> | <array_datatype>

<domain_or_non_array_type> ::=
  !! Vgl. Syntax für Scalardatentypen !!

<array_datatype> ::=
  !! Vgl. Syntax für Array-Datentypen !!

Siehe auch CAST() im Abschnitt Eingebaute Skalarfunktionen.

Casting auf eine Domain

Beim Casting in eine Domäne werden alle dafür deklarierten Constraints berücksichtigt, d. h. NOT NULL- oder CHECK-Constraints. Wenn der Wert die Prüfung nicht besteht, schlägt die Umwandlung fehl.

Wenn zusätzlich TYPE OF angegeben wird — Umwandlung in seinen Basistyp — werden alle Domäneneinschränkungen während der Umwandlung ignoriert. Wird TYPE OF mit einem Zeichentyp (CHAR/VARCHAR) verwendet, bleiben Zeichensatz und Kollatierung erhalten.

Casting in Spaltentyp

Wenn Operanden in den Typ einer Spalte umgewandelt werden, kann die angegebene Spalte aus einer Tabelle oder einer Sicht stammen.

Es wird nur der Typ der Spalte selbst verwendet. Bei Zeichentypen enthält die Besetzung den Zeichensatz, aber nicht die Sortierung. Die Einschränkungen und Standardwerte der Quellspalte werden nicht angewendet.

Beispiel
CREATE TABLE TTT (
  S VARCHAR (40)
  CHARACTER SET UTF8 COLLATE UNICODE_CI_AI
);
COMMIT;

SELECT
  CAST ('I have many friends' AS TYPE OF COLUMN TTT.S)
FROM RDB$DATABASE;
Konvertierungen für die CAST-Funktion möglich
Tabelle 8. Umwandlungen mit CAST
Von Datentyp Zu Datentyp

Numerische Typen

Numerische Typen, [VAR]CHAR, BLOB

[VAR]CHAR

[VAR]CHAR, BLOB, Numerische Typen, DATE, TIME, TIMESTAMP, BOOLEAN

BLOB

[VAR]CHAR, BLOB, Numerische Typen, DATE, TIME, TIMESTAMP, BOOLEAN

DATE, TIME

[VAR]CHAR, BLOB, TIMESTAMP

TIMESTAMP

[VAR]CHAR, BLOB, DATE, TIME

BOOLEAN

BOOLEAN, [VAR]CHAR, BLOB

Um String-Datentypen in den Typ BOOLEAN zu konvertieren, muss der Wert (ohne Berücksichtigung der Groß-/Kleinschreibung) 'true' oder 'false' oder NULL sein.

Beachten Sie, dass ein teilweiser Informationsverlust möglich ist. Wenn Sie beispielsweise den Datentyp TIMESTAMP in den Datentyp DATE umwandeln, geht der Zeitteil verloren.

Literale Formate

Um String-Datentypen in die Datentypen DATE, TIME oder TIMESTAMP umzuwandeln, muss das String-Argument eines der vordefinierten Datums- und Uhrzeitliterale sein (siehe Tabelle 9) oder eine Darstellung des Datums in einem der zulässigen Datum-Uhrzeit-Literal-Formate:

<timestamp_format> ::=
    { [YYYY<p>]MM<p>DD[<p>HH[<p>mm[<p>SS[<p>NNNN]]]]
    | MM<p>DD[<p>YYYY[<p>HH[<p>mm[<p>SS[<p>NNNN]]]]]
    | DD<p>MM[<p>YYYY[<p>HH[<p>mm[<p>SS[<p>NNNN]]]]]
    | MM<p>DD[<p>YY[<p>HH[<p>mm[<p>SS[<p>NNNN]]]]]
    | DD<p>MM[<p>YY[<p>HH[<p>mm[<p>SS[<p>NNNN]]]]]
    | NOW
    | TODAY
    | TOMORROW
    | YESTERDAY }

<date_format> ::=
    { [YYYY<p>]MM<p>DD
    | MM<p>DD[<p>YYYY]
    | DD<p>MM[<p>YYYY]
    | MM<p>DD[<p>YY]
    | DD<p>MM[<p>YY]
    | TODAY
    | TOMORROW
    | YESTERDAY }

<time_format> :=
    { HH[<p>mm[<p>SS[<p>NNNN]]]
    | NOW }

<p> ::= whitespace | . | : | , | - | /
Tabelle 9. Datums- und Uhrzeit-Literalformatargumente
Argument Beschreibung

timestamp_format

Format des Zeitstempelliterals

date_literal

Format des Datumsliterals

time_literal

Format des Zeitliterals

YYYY

Vierstelliges Jahr

YY

Zweistelliges Jahr

MM

Monat Kann 1 oder 2 Stellen enthalten (1-12 oder 01-12). Sie können auch den aus drei Buchstaben bestehenden Kurznamen oder den vollständigen Namen eines Monats in Englisch angeben. Groß-/Kleinschreibung nicht beachten

DD

Tag. Es kann 1 oder 2 Stellen enthalten (1-31 oder 01-31)

HH

Stunde. Es kann 1 oder 2 Stellen enthalten (0-23 oder 00-23)

mm

Minuten. Es kann 1 oder 2 Stellen enthalten (0-59 oder 00-59)

SS

Sekunden. Es kann 1 oder 2 Stellen enthalten (0-59 oder 00-59)

NNNN

Zehntausendstelsekunden. Es kann 1 bis 4 Stellen (0-9999) enthalten.

p

Ein Trennzeichen, eines der zulässigen Zeichen. Führende und nachgestellte Leerzeichen werden ignoriert

Tabelle 10. Literale mit vordefinierten Werten für Datum und Uhrzeit

Literal

Beschreibung

Datentyp

Dialekt 1

Dialekt 3

'NOW'

Aktuelles Datum und Zeit

DATE

TIMESTAMP

'TODAY'

Aktuelles Datum

DATE mit Nullzeit-Teil

DATE

'TOMORROW'

Aktualles Datum + 1 (Tag)

DATE mit Nullzeit-Teil

DATE

'YESTERDAY'

Aktualles Datum - 1 (Tag)

DATE mit Nullzeit-Teil

DATE

Die Verwendung der vollständigen Jahresangabe in vierstelliger Form — YYYY — wird dringend empfohlen, um Verwirrung bei Datumsberechnungen und Aggregationen zu vermeiden.

Beispielhafte Interpretationen der Datumsliterale
select
  cast('04.12.2014' as date) as d1, -- DD.MM.YYYY
  cast('04 12 2014' as date) as d2, -- MM DD YYYY
  cast('4-12-2014' as date) as d3,  -- MM-DD-YYYY
  cast('04/12/2014' as date) as d4, -- MM/DD/YYYY
  cast('04,12,2014' as date) as d5, -- MM,DD,YYYY
  cast('04.12.14' as date) as d6,   -- DD.MM.YY
  -- DD.MM mit aktuellem Jahr
  cast('04.12' as date) as d7,
  -- MM/DD mit aktuellem Jahr
  cast('04/12' as date) as d8,
  cast('2014/12/04' as date) as d9, -- YYYY/MM/DD
  cast('2014 12 04' as date) as d10, -- YYYY MM DD
  cast('2014.12.04' as date) as d11, -- YYYY.MM.DD
  cast('2014-12-04' as date) as d12, -- YYYY-MM-DD
  cast('4 Jan 2014' as date) as d13, -- DD MM YYYY
  cast('2014 Jan 4' as date) as dt14, -- YYYY MM DD
  cast('Jan 4, 2014' as date) as dt15, -- MM DD, YYYY
  cast('11:37' as time) as t1, -- HH:mm
  cast('11:37:12' as time) as t2, -- HH:mm:ss
  cast('11:31:12.1234' as time) as t3, -- HH:mm:ss.nnnn
  cast('11.37.12' as time) as t4, -- HH.mm.ss
  -- DD.MM.YYYY HH:mm
  cast('04.12.2014 11:37' as timestamp) as dt1,
  -- MM/DD/YYYY HH:mm:ss
  cast('04/12/2014 11:37:12' as timestamp) as dt2,
  -- DD.MM.YYYY HH:mm:ss.nnnn
  cast('04.12.2014 11:31:12.1234' as timestamp) as dt3,
  -- MM/DD/YYYY HH.mm.ss
  cast('04/12/2014 11.37.12' as timestamp) as dt4
from rdb$database
Kurzformumwandlungen für Datums- und Uhrzeitdatentypen

Firebird erlaubt die Verwendung einer abgekürzten Typsyntax im "C-Stil" für Umwandlungen von Strings in die Typen "DATE", "TIME" und "TIMESTAMP". Der SQL-Standard ruft diese Datetime-Literale auf.

Syntax
<data_type> 'date_literal_string'
Beispiel
-- 1
  UPDATE PEOPLE
  SET AGECAT = 'SENIOR'
  WHERE BIRTHDATE < DATE '1-Jan-1943';
-- 2
  INSERT INTO APPOINTMENTS
  (EMPLOYEE_ID, CLIENT_ID, APP_DATE, APP_TIME)
  VALUES (973, 8804, DATE 'today' + 2, TIME '16:00');
-- 3
  NEW.LASTMOD = TIMESTAMP 'now';

Diese Kurzausdrücke werden direkt beim Parsen ausgewertet, als ob die Anweisung bereits für die Ausführung vorbereitet wäre. Selbst wenn die Abfrage mehrmals ausgeführt wird, bleibt der Wert von beispielsweise timestamp 'now' gleich, egal wie viel Zeit vergeht.

Wenn die Zeit bei jeder Ausführung ausgewertet werden soll, verwenden Sie die vollständige CAST-Syntax. Ein Beispiel für die Verwendung eines solchen Ausdrucks in einem Trigger:

NEW.CHANGE_DATE = CAST('now' AS TIMESTAMP);

Firebird 4 lässt diese impliziten Datetime-Werte wie 'now'', ''today'' usw. in diesen Kurzformumsetzungen nicht mehr zu. Es ist ratsam, für implizite Werte auf die Verwendung des vollständigen `CAST-Ausdrucks umzusteigen.

3.9.2. Implizite Datentypkonvertierung

Eine implizite Datenkonvertierung ist in Dialekt 3 nicht möglich — die CAST-Funktion wird fast immer benötigt, um Datentypkonflikte zu vermeiden.

In Dialekt 1 wird in vielen Ausdrücken ein Typ implizit in einen anderen umgewandelt, ohne dass die CAST-Funktion verwendet werden muss. Zum Beispiel gilt die folgende Aussage in Dialekt 1:

UPDATE ATABLE
  SET ADATE = '25.12.2016' + 1

und das Datumsliteral wird implizit in den Datumstyp umgewandelt.

In Dialekt 3 wird diese Anweisung den Fehler 35544569 ausgeben, “`Dynamic SQL Error: expression evaluation not supported, Strings cannot be added or subtracted in dialect 3” — eine Umwandlung ist erforderlich:

UPDATE ATABLE
  SET ADATE = CAST ('25.12.2016' AS DATE) + 1

oder mit der kurzen Umwandlung:

UPDATE ATABLE
  SET ADATE = DATE '25.12.2016' + 1

In Dialekt 1 ist es normalerweise möglich, ganzzahlige Daten und numerische Zeichenfolgen zu mischen, da der Parser versucht, die Zeichenfolge implizit umzuwandeln. Beispielsweise,

2 + '1'

wird korrekt ausgeführt.

In Dialekt 3 führt ein solcher Ausdruck zu einem Fehler, daher müssen Sie ihn als CAST-Ausdruck schreiben:

2 + CAST('1' AS SMALLINT)

Die Ausnahme von der Regel ist während der String-Verkettung.

Implizite Konvertierung während der String-Verkettung

Wenn mehrere Datenelemente verkettet werden, werden alle Nicht-String-Daten nach Möglichkeit implizit in Strings umgewandelt.

Beispiel
SELECT 30||' days hath September, April, June and November' CONCAT$
  FROM RDB$DATABASE;

CONCAT$
------------------------------------------------
30 days hath September, April, June and November

3.10. Benutzerdefinierte Datentypen – Domains

In Firebird ist das Konzept eines “benutzerdefinierten Datentyps” in Form der Domain implementiert. Das Erstellen einer Domain erzeugt natürlich nicht wirklich einen neuen Datentyp. Eine Domain bietet die Möglichkeit, einen vorhandenen Datentyp mit einem Satz von Attributen zu kapseln und diese “Kapsel” für die mehrfache Verwendung in der gesamten Datenbank verfügbar zu machen. Wenn mehrere Tabellen Spalten mit identischen oder nahezu identischen Attributen benötigen, ist eine Domäne sinnvoll.

Die Domänenverwendung ist nicht auf Spaltendefinitionen für Tabellen und Ansichten beschränkt. Domänen können verwendet werden, um Eingabe- und Ausgabeparameter und Variablen in PSQL-Code zu deklarieren.

3.10.1. Domaineigenschaften

Eine Domaindefinition enthält erforderliche und optionale Attribute. Der Datentyp ist ein erforderliches Attribut. Zu den optionalen Attributen gehören:

  • ein Standardwert

  • um NULL zu erlauben oder zu verbieten

  • CHECK-Einschränkungen

  • Zeichensatz (für Zeichendatentypen und Text-BLOB-Felder)

  • Sortierung (für Zeichendatentypen)

Beispieldomaindefinition
CREATE DOMAIN BOOL3 AS SMALLINT
  CHECK (VALUE IS NULL OR VALUE IN (0, 1));
Siehe auch

Explizite Datentypumwandlung zur Beschreibung von Unterschieden im Datenkonvertierungsmechanismus, wenn Domänen für die Modifikatoren TYPE OF und TYPE OF COLUMN angegeben werden.

3.10.2. Domain-Überschreibung

Beim Definieren einer Spalte mithilfe einer Domäne ist es möglich, einige der von der Domäne geerbten Attribute zu überschreiben. Tabelle 3.9 fasst die Regeln für die Domänenüberschreibung zusammen.

Tabelle 11. Regeln zum Überschreiben von Domänenattributen in der Spaltendefinition
Attribute Überschreiben? Hinweise

Datentyp

Nein

 

Standardwert

Ja

 

Textzeichensatz

Ja

Es kann auch verwendet werden, um die Standarddatenbankwerte für die Spalte wiederherzustellen

Reihenfolge der Textsortierung

Ja

 

CHECK-Constraints

Ja

Um der Prüfung neue Bedingungen hinzuzufügen, können Sie die entsprechenden CHECK-Klauseln in den Anweisungen CREATE und ALTER auf Tabellenebene verwenden.

NOT NULL

Nein

Oft ist es besser, die Domain in ihrer Definition nullbar zu lassen und zu entscheiden, ob sie auf NOT NULL gesetzt werden soll, wenn die Domain zum Definieren von Spalten verwendet wird.

3.10.3. Erstellen und Verwalten von Domains

Eine Domain wird mit der DDL-Anweisung CREATE DOMAIN erstellt.

Kurzschreibweise
CREATE DOMAIN name [AS] <type>
  [DEFAULT {<const> | <literal> | NULL | <context_var>}]
  [NOT NULL] [CHECK (<condition>)]
  [COLLATE <collation>]
Siehe auch

CREATE DOMAIN im Abschnitt Datendefinitionssprache (DDL).

Domain ändern

Um die Attribute einer Domain zu ändern, verwenden Sie die DDL-Anweisung ALTER DOMAIN. Mit dieser Aussage können Sie:

  • die Domain umbenennen

  • den Datentyp ändern

  • den aktuellen Standardwert löschen

  • einen neuen Standardwert setzen

  • lösche die NOT NULL-Beschränkung

  • setze die NOT NULL-Beschränkung

  • eine bestehende CHECK-Einschränkung löschen

  • füge eine neue CHECK-Einschränkung hinzu

Kurzsyntax
ALTER DOMAIN name
  [{TO new_name}]
  [{SET DEFAULT { <literal> | NULL | <context_var> } |
    DROP DEFAULT}]
  [{SET | DROP} NOT NULL ]
  [{ADD [CONSTRAINT] CHECK (<dom_condition>) |
    DROP CONSTRAINT}]
  [{TYPE <datatype>}]
Beispiel
ALTER DOMAIN STORE_GRP SET DEFAULT -1;

Beim Wechsel einer Domain müssen deren Abhängigkeiten berücksichtigt werden: ob Tabellenspalten, beliebige Variablen, Ein- und/oder Ausgabeparameter mit dem im PSQL-Code deklarierten Typ dieser Domain vorhanden sind. Wenn Sie Domains in Eile ändern, ohne sie sorgfältig zu überprüfen, funktioniert Ihr Code möglicherweise nicht mehr!

Wenn Sie Datentypen in einer Domain konvertieren, dürfen Sie keine Konvertierungen durchführen, die zu Datenverlusten führen können. Wenn Sie beispielsweise VARCHAR in INTEGER konvertieren, prüfen Sie sorgfältig, ob alle Daten, die diese Domain verwenden, erfolgreich konvertiert werden können.

Siehe auch

ALTER DOMAIN im Abschnitt Datendefinitionssprache (DDL).

Löschen (Dropping) einer Domain

Die DDL-Anweisung DROP DOMAIN löscht eine Domain aus der Datenbank, sofern sie nicht von anderen Datenbankobjekten verwendet wird.

Syntax
DROP DOMAIN name

Jeder mit der Datenbank verbundene Benutzer kann eine Domäne löschen.

Beispiel
DROP DOMAIN Test_Domain
Siehe auch

DROP DOMAIN im Abschnitt Datendefinitionssprache (DDL).

3.11. Syntax der Datentyp-Deklaration

In diesem Abschnitt wird die Syntax der Deklaration von Datentypen dokumentiert. Die Datentypdeklaration erfolgt am häufigsten in DDL-Anweisungen, aber auch in CAST und <<fblangref30-dml-execblock-de,EXECUTE BLOCK> >.

Auf die unten dokumentierte Syntax wird von anderen Teilen dieser Sprachreferenz verwiesen.

3.11.1. Syntax für Skalardatentypen

Die skalaren Datentypen sind einfache Datentypen, die einen einzelnen Wert enthalten. Aus organisatorischen Gründen wird die Syntax der BLOB-Typen separat in Syntax der BLOB-Datentypen definiert.

Syntax für skalare Datentypen
<domain_or_non_array_type> ::=
    <scalar_datatype>
  | <blob_datatype>
  | [TYPE OF] domain
  | TYPE OF COLUMN rel.col

<scalar_datatype> ::=
    SMALLINT | INT[EGER] | BIGINT
  | FLOAT | DOUBLE PRECISION
  | BOOLEAN
  | DATE | TIME | TIMESTAMP
  | {DECIMAL | NUMERIC} [(precision [, scale])]
  | {VARCHAR | {CHAR | CHARACTER} VARYING} (length)
    [CHARACTER SET charset]
  | {CHAR | CHARACTER} [(length)] [CHARACTER SET charset]
  | {NCHAR | NATIONAL {CHARACTER | CHAR}} VARYING (length)
  | {NCHAR | NATIONAL {CHARACTER | CHAR}} [(length)]
Tabelle 12. Argumente für die Syntax der skalaren Datentypen
Argument Beschreibung

domain

Domain (nur Nicht-Array-Domains)

rel

Name einer Tabelle oder Ansicht (View)

col

Name einer Spalte in einer Tabelle oder Ansicht (nur Spalten eines Nicht-Array-Typs)

precision

Numerische Genauigkeit in Dezimalstellen. Von 1 bis 18

scale

Skalierung oder Anzahl der Dezimalstellen. Von 0 bis 18. Sie muss kleiner oder gleich precision sein.

length

Die maximale Länge einer Zeichenfolge in Zeichen

charset

Zeichensatz

domain_or_non_array_type

Nicht-Array-Typen, die in PSQL-Code und -Casts verwendet werden können

Verwendung von Domains in Deklarationen

Ein Domainname kann als Typ eines PSQL-Parameters oder einer lokalen Variablen angegeben werden. Der Parameter oder die Variable erbt alle Domänenattribute. Wenn für den Parameter oder die Variable ein Standardwert angegeben wird, überschreibt er den in der Domaindefinition angegebenen Standardwert.

Wenn die TYPE OF-Klausel vor dem Domainnamen hinzugefügt wird, wird nur der Datentyp der Domain verwendet: alle anderen Attribute der Domain — NOT NULL-Einschränkung, CHECK-Einschränkungen, Standardwert — sind weder geprüft noch benutzt. Handelt es sich bei der Domain jedoch um einen Texttyp, werden immer deren Zeichensatz und Kollatierungsreihenfolge verwendet.

Verwendung des Spaltentyps in Deklarationen

Ein- und Ausgabeparameter oder lokale Variablen können auch über den Datentyp von Spalten in bestehenden Tabellen und Views deklariert werden. Dafür wird die TYPE OF COLUMN-Klausel verwendet, die relationname.columnname als Argument angibt.

Wenn TYPE OF COLUMN verwendet wird, erbt der Parameter oder die Variable nur den Datentyp und – bei String-Typen – den Zeichensatz und die Kollatierungssequenz. Die Einschränkungen und der Standardwert der Spalte werden ignoriert.

3.11.2. Syntax der BLOB-Datentypen

Die BLOB-Datentypen enthalten Binär-, Zeichen- oder benutzerdefinierte Formatdaten unbestimmter Größe. Weitere Informationen finden Sie unter Binärdatentypen.

Syntax der BLOB-Datentypen
<blob_datatype> ::=
    BLOB [SUB_TYPE {subtype_num | subtype_name}]
    [SEGMENT SIZE seglen] [CHARACTER SET charset]
  | BLOB [(seglen [, subtype_num])]
Tabelle 13. Argumente für die Syntax der Blob-Datentypen
Argument Beschreibung

charset

Zeichensatz (wird für andere Untertypen als TEXT/1 ignoriert)

subtype_num

BLOB-Untertypnummer

subtype_name

mnemonischer Name des 'BLOB'-Untertyps; dies kann TEXT, BINARY oder einer der (anderen) Standard- oder benutzerdefinierten Namen sein, die in RDB$TYPES für RDB$FIELD_NAME = 'RDB$FIELD_SUB_TYPE' definiert sind.

seglen

Segmentgröße, darf nicht größer als 65.535 sein, Standardwert 80, wenn nicht angegeben. Siehe auch Segmentgröße

3.11.3. Syntax der Array-Datentypen

Die Array-Datentypen enthalten mehrere Skalarwerte in einem ein- oder mehrdimensionalen Array. Weitere Informationen finden Sie unter ARRAY-Datentyp

Syntax der Array-Datentypen
<array_datatype> ::=
    {SMALLINT | INT[EGER] | BIGINT} <array_dim>
  | {FLOAT | DOUBLE PRECISION} <array_dim>
  | BOOLEAN <array_dim>
  | {DATE | TIME | TIMESTAMP} <array_dim>
  | {DECIMAL | NUMERIC} [(precision [, scale])] <array_dim>
  | {VARCHAR | {CHAR | CHARACTER} VARYING} (length)
    <array_dim> [CHARACTER SET charset]
  | {CHAR | CHARACTER} [(length)] <array_dim>
    [CHARACTER SET charset]
  | {NCHAR | NATIONAL {CHARACTER | CHAR}} VARYING (length)
    <array_dim>
  | {NCHAR | NATIONAL {CHARACTER | CHAR}}
    [(length)] <array_dim>

<array_dim> ::= '[' [m:]n [,[m:]n ...] ']'
Tabelle 14. Argumente für die Syntax der Array-Datentypen
Argument Beschreibung

array_dim

Array-Dimensionen

precision

Numerische Genauigkeit in Dezimalstellen. Von 1 bis 18

scale

Skala oder Anzahl der Dezimalstellen. Von 0 bis 18. Sie muss kleiner oder gleich precision sein.

length

Die maximale Länge einer Zeichenfolge in Zeichen; optional für Zeichentypen mit fester Breite, standardmäßig 1

charset

Zeichensatz

m, n

Ganzzahlen, die den Indexbereich einer Array-Dimension definieren

4. Allgemeine Sprachelemente

Dieser Abschnitt behandelt die Elemente, die in der SQL-Sprache als allgemeingültig betrachtet werden können — die Ausdrücke, die verwendet werden um Fakten aus Daten zu extrahieren, diese zu verarbeiten und die Prädikate, die den Wahrheitswert dieser Fakten prüfen.

4.1. Ausdrücke

SQL-Ausdrücke bieten formelle Methoden zum Auswerten, Transformieren und Vergleichen von Werten. SQL-Ausdrücke können Tabellenspalten, Variablen, Konstanten, Literale, andere Statements und Prädikate sowie andere Ausdrücke enthalten. Folgend die vollständige Liste möglicher Elemente.

Beschreibung der Ausdruck-Elemente
Spaltenname

Kennung einer Spalte aus einer angegebenen Tabelle, die in Auswertungen oder als Suchbedingung verwendet wird. Eine Spalte des Array-Typs kann kein Element innerhalb eines Ausdrucks sein, es sei denn sie wird mit dem IS [NOT] NULL-Prädikat verwendet.

Array-Element

Ein Ausdruck kann einen Verweis auf ein Array-Element enthalten.

Arithmetische Operatoren

Die Zeichen +, -, *, / werden verwendet um Berechnungen durchzuführen.

Verkettungsoperator

Der Operator || (“Doppel-Pipe”) wird verwendet um Strings zu verketten.

Logische Operatoren

Die reservierten Wörter NOT, AND sowie OR werden verwendet um einfache Suchbedingungen oder komplexere Behauptungen zu erstellen.

Vergleichsoperatoren

Die Zeichen =, <>, !=, ~=, ^=, <, <=, >, >=, !<, ~<, ^<, !>, ~> und ^>

Vergleichsprädikate

LIKE, STARTING WITH, CONTAINING, SIMILAR TO, BETWEEN, IS [NOT] NULL und IS [NOT] DISTINCT FROM

Existenzprädikate

Prädikate, die für die Existenzprüfung von Werten Verwendung finden. Das Prädikat IN kann sowohl innerhalb von Listen kommagetrennter Konstanten als auch mit Unterabfragen, die nur eine Spalte zurückgeben, verwendet werden. Die Prädikate EXISTS, SINGULAR, ALL, ANY und SOME können nur mit Unterabfragen verwendet werden.

Konstante oder Litaral

In Apostrophen eingeschlossene Zahlen oder String-Literale, Boolesche Werte TRUE, FALSE und UNKOWN, `NULL

Datum-/Zeitliterale

Ein Ausdruck, ähnlich zu Zeichenketten, eingeschlossen in Apostrophs, der als Datum, Zeit oder Zeitstempel interpretiert wird. Datumsliterale können vordefinierte Literale ('TODAY', 'NOW', etc.) oder Zeichenketten aus Buchstaben oder Zahlen sein, wie zum Beispiel '30.12.2016 15:30:35', die zu einem Datum und/oder einer Zeit aufgelöst werden können.

Kontextvariablen

Ein intern definierte Kontextvariable

Lokale Variablen

Deklarierte lokale Variablen, Über- und Rückgabeparameter eines PSQL-Moduls (Stored Procedure, Trigger, unbenannter PSQL-Block in DSQL)

Positionale Parameter

Ein Mitglied innerhalb einer geordneten Gruppe von einem oder mehreren unbenannten Parametern, die an eine gespeicherte Prozedur oder eine vorbereitete Abfrage übergeben wurden.

Unterabfrage

Eine SELECT-Anweisung, die in Klammern eingeschlossen ist, die einen einzelnen (skalaren) Wert zurückgibt oder, wenn er in existenziellen Prädikaten verwendet wird, einen Satz von Werten.

Funktionskennung

Die Kennung einer internen oder externen Funktion in einem Funktionsausdruck

Type-Cast

Ein Ausdruck, der explizit Daten von einem in einen anderen Datentyp unter Verwendung der CAST-Funktion (CAST (<value> AS <datatype>)) konvertiert. Nur für Datum-/Zeit-Literale ist die Kurzschreibweise <datatype> <value> (DATE '30.12.2016') möglich.

Bedingter Ausdruck

Ausdrücke mit CASE und verwandten internen Funktionen

Klammern

Klammernpaare (…​) werden verwendet, um Ausdrücke zu gruppieren. Operationen innerhalb der Klammern werden vor Operationen außerhalb von ihnen durchgeführt. Wenn eingebettete Klammern verwendet werden, werden die tiefsten eingebetteten Ausdrücke zuerst ausgewertet und dann bewegen sich die Auswertungen von innen nach außen durch die Einbettungsstufen.

COLLATE-Klausel

Klausel, die für CHAR- und VARCHAR-Datentypen angewendet werden kann, um die Collation für String-Vergleiche festzulegen.

NEXT VALUE FOR sequence

Ausdruck zum Ermitteln des nächsten Wertes eines bestimmten Generators (Sequenz). Die interne Funktion GEN_ID() tut das Gleiche.

4.1.1. Konstanten

Eine Konstante ist ein Wert der direkt in einem SQL-Statement verwendet wird und weder von einem Ausdruck, einem Parameter, einem Spaltenverweis noch einer Variablen abgeleitet wird. Dies kann eine Zeichenkette oder eine Zahl sein.

Zeichenkonstanten (Literale)

Eine String-Konstante ist eine Aneinanderreihung von Zeichen, die zwischen einem Paar von Apostrophen (“einfache Anführungszeichen”) eingeschlossen werden. Die größtmögliche Länge dieser Zeichenketten ist 32.767 Bytes; die maximale Anzahl der Zeichen wird durch die verwendete Zeichenkodierung bestimmt.

  • Doppelte Anführungszeichen sind NICHT GÜLTIG für das Anführungszeichen von Zeichenfolgen. Der SQL-Standard reserviert doppelte Anführungszeichen für einen anderen Zweck: das Anführen von Bezeichnern.

  • Wenn ein literaler Apostroph innerhalb einer String-Konstante erforderlich ist, wird er “escaped”, indem ihm ein anderer Apostroph vorangestellt wird. Zum Beispiel ’Mutter O''Reillys hausgemachter Hooch''.

  • Bei der Stringlänge ist Vorsicht geboten, wenn der Wert in eine CHAR- oder VARCHAR-Spalte geschrieben werden soll. Die maximale Länge für ein CHAR- oder VARCHAR`-Literal beträgt 32.765 Byte.

Es wird angenommen, dass der Zeichensatz einer Zeichenkonstanten der gleiche ist wie der Zeichensatz seines Bestimmungsspeichers.

Stringkonstanten in Hexadezimalnotation

Ab Firebird 2.5 können String-Literale in hexadezimaler Notation eingegeben werden, sogenannte “binary strings”. Jedes Paar von Hex-Ziffern definiert ein Byte in der Zeichenfolge. Auf diese Weise eingegebene Zeichenfolgen haben standardmäßig den Zeichensatz OCTETS, aber die Einführer-Syntax kann verwendet werden, um zu erzwingen, dass ein String als ein anderer Zeichensatz interpretiert wird.

Syntax
{x|X}'<hexstring>'

<hexstring> ::= eine gerade Anzahl von <hexdigit>
<hexdigit>  ::= eines aus 0..9, A..F, a..f
Beispiele
select x'4E657276656E' from rdb$database
-- liefert 4E657276656E, ein 6-Byte 'Binärstring'

select _ascii x'4E657276656E' from rdb$database
-- liefert 'Nerven' (gleiche Zeichenfolge, jetzt als ASCII-Text interpretiert)

select _iso8859_1 x'53E46765' from rdb$database
-- liefert 'Säge' (4 Zeichen, 4 Bytes)

select _utf8 x'53C3A46765' from rdb$database
-- liefert 'Säge' (4 Zeichen, 5 Bytes)
Hinweise

Die Client-Schnittstelle legt fest, wie Binärzeichenfolgen dem Benutzer angezeigt werden. Das isql-Werkzeug beispielsweise, nutzt großgeschriebene Buchstaben A-F, während FlameRobin Kleinschreibung verwendet. Andere Client-Applikationen könnten andere Konventionen bevorzugen, zum Beispiel Leerzeichen zwischen den Bytepaaren: '4E 65 72 76 65 6E'.

Mit der hexadezimalen Notation kann jeder Bytewert (einschließlich 00) an beliebiger Stelle im String eingefügt werden. Allerdings, wenn Sie diesen auf etwas anderes als OCTETS erzwingen wollen, liegt es in Ihrer Verantwortung, die Bytes in einer Sequenz zu liefern, die für den Zielzeichensatz gültig ist.

Alternative String-Literale

Seit Firebird 3.0 ist es möglich, ein anderes Zeichen oder Zeichenpaar als das doppelte (escaped) Apostroph zu verwenden, um einen String in Anführungszeichen in einen anderen String einzubetten. Das Schlüsselwort q oder Q vor einem String in Anführungszeichen informiert den Parser darüber, dass bestimmte Links-Rechts-Paare oder Paare identischer Zeichen innerhalb des Strings die Begrenzer des eingebetteten String-Literals sind.

Syntax
<alternative string literal> ::=
    { q | Q } <quote> <start char> [<char> ...] <end char> <quote>
Regeln

Wenn <start char> `(’, ‘{’, ‘[’ oder ‘<’ ist, `< end char> wird mit seinem jeweiligen “partner” gepaart, nämlich ‘)’, ‘}’, ‘]’ und '` >'. In anderen Fällen ist `<end char> dasselbe wie <start char>.

Innerhalb des Strings, d. h. <char>-Elemente, können einfache (nicht maskierte) Anführungszeichen verwendet werden. Jedes Anführungszeichen ist Teil der Ergebniszeichenfolge.

Beispiel
select q'{abc{def}ghi}' from rdb$database;        -- Ergebnis: abc{def}ghi
select q'!That's a string!' from rdb$database;    -- Ergebnis: That's a string
Introducer-Syntax für String-Literale

Gegebenenfalls kann einem Zeichenfolgenliteral ein Zeichensatzname vorangestellt werden, dem ein Unterstrich “_” vorangestellt ist. Dies ist als Introducer-Syntax bekannt. Sein Zweck besteht darin, die Engine darüber zu informieren, wie die eingehende Zeichenfolge zu interpretieren und zu speichern ist.

Beispiel

INSERT INTO People
VALUES (_ISO8859_1 'Hans-Jörg Schäfer')
Zahlenkonstanten

Eine Zahlkonstante ist eine gültige Zahl in einer unterstützten Notation:

  • In SQL wird der Dezimalpunkt, für Zahlen in der Standard-Dezimal-Notation, immer durch das Punkt-Zeichen dargestellt. Tausender werden nicht getrennt. Einbeziehung von Komma, Leerzeichen usw. führt zu Fehlern.

  • Exponentielle Notation wird unterstützt. Zum Beispiel kann 0.0000234 auch als 2.34e-5 geschrieben werden.

  • Hexadezimal-Notation wird von Firebird 2.5 und höheren Versionen unterstützt — siehe unten.

Das Format des Literals bestimmt den Typ (<d> für eine Dezimalziffer, <h> für eine Hexadezimalziffer):

Format Typ

<d>[<d> …​]

INTEGER oder BIGINT (hängt davon ab, ob der Wert in den Typ passt)

0{x|X} <h><h>[<h><h> …​]

INTEGER für 1-8 <h><h> Paare oder BIGINT für 9-16 Paare

<d>[<d> …​] "." [<d> …​]

`NUMERIC(18, n)`wobei n von der Anzahl der Nachkommastellen abhängt

<d>[<d> …​]["." [<d> …​]] E <d>[<d> …​]

DOUBLE PRECISION

Hexadezimale Notation für Ziffern

Von Firebird 2.5 aufwärts können ganzzahlige Werte in hexadezimaler Notation eingegeben werden. Zahlen mit 1-8 Hex-Ziffern werden als Typ INTEGER interpretiert; Zahlen mit 9-16 Hex-Ziffern als Typ BIGINT.

Syntax
0{x|X}<hexdigits>

<hexdigits>  ::=  1-16 of <hexdigit>
<hexdigit>   ::=  one of 0..9, A..F, a..f
Beispiele
select 0x6FAA0D3 from rdb$database          -- liefert 117088467
select 0x4F9 from rdb$database              -- liefert 1273
select 0x6E44F9A8 from rdb$database         -- liefert 1850014120
select 0x9E44F9A8 from rdb$database         -- liefert -1639646808 (an INTEGER)
select 0x09E44F9A8 from rdb$database        -- liefert 2655320488 (a BIGINT)
select 0x28ED678A4C987 from rdb$database    -- liefert 720001751632263
select 0xFFFFFFFFFFFFFFFF from rdb$database -- liefert -1
Hexadezimale Wertebereiche
  • Hex-Nummern im Bereich 0 .. 7FFF FFFF sind positive INTEGER mit Dezimalwerten zwischen 0 .. 2147483647. Um eine Zahl als BIGINT zu erzwingen, müssen Sie genügend Nullen voranstellen, um die Gesamtzahl der Hex-Ziffern auf neun oder mehr zu bringen. Das ändert den Typ, aber nicht den Wert.

  • Hex-Nummern zwischen 8000 0000 .. FFFF FFFF erfordern etwas Aufmerksamkeit:

    • Bei der Eingabe mit acht Hex-Ziffern, wie in 0x9E44F9A8, wird ein Wert als 32-Bit-INTEGER interpretiert. Da das erste Bit (Vorzeichenbit) gesetzt ist, wird es dem negativen Dezimalbereich -2147483648 .. -1 zugeordnet.

    • Bei einer oder mehreren Nullen, die wie in 0x09E44F9A8 vorangestellt werden, wird ein Wert als 64-Bit-BIGINT im Bereich 0000 0000 8000 0000 .. 0000 0000 FFFF FFFF interpretiert. Das Zeichen-Bit ist jetzt nicht gesetzt, also wird der Dezimalwert dem positiven Bereich 2147483648 .. 4294967295 zugewiesen.

    So ergibt sich in diesem Bereich — und nur in diesem Bereich — anhand einer mathematisch unbedeutenden 0 ein gänzlich anderer Wert. Dies ist zu beachten.

  • Hex-Zahlen zwischen 1 0000 0000 .. 7FFF FFFF FFFF FFFF sind alle positiv BIGINT.

  • Hex-Zahlen zwischen 8000 0000 0000 0000 .. FFFF FFFF FFFF FFFF sind alle negativ BIGINT.

  • Ein SMALLINT kann nicht in Hex geschrieben werden, streng genommen zumindest, da sogar 0x1 als INTEGER ausgewertet wird. Wenn Sie jedoch eine positive Ganzzahl innerhalb des 16-Bit-Bereichs 0x0000 (Dezimal-Null) bis 0x7FFF (Dezimalzahl 32767) schreiben, wird sie transparent in SMALLINT umgewandelt.

    Es ist möglich einen negativen SMALLINT in Hex zu schreiben, wobei eine 4-Byte-Hexadezimalzahl im Bereich 0xFFFF8000 (Dezimal -32768) bis 0xFFFFFFFF (Dezimal -1) verwendet wird.

Boolesche Literale

Ein boolesches Literal ist eines von TRUE, FALSE oder UNKNOWN.

4.1.2. SQL-Operatoren

SQL-Operatoren umfassen Operatoren zum Vergleichen, Berechnen, Auswerten und Verketten von Werten.

Vorrang der Operatoren

SQL Operatoren sind in vier Typen unterteilt. Jeder Operator-Typ hat eine Priorität, eine Rangfolge, die die Reihenfolge bestimmt, in der die Operatoren und die mit ihrer Hilfe erhaltenen Werte in einem Ausdruck ausgewertet werden. Je höher der Vorrang des Operator-Typs ist, desto früher wird er ausgewertet. Jeder Operator hat seine eigene Priorität innerhalb seines Typs, der die Reihenfolge bestimmt, in der sie in einem Ausdruck ausgewertet werden.

Operatoren der gleichen Rangfolge werden von links nach rechts ausgewertet. Um dieses Verhalten zu beeinflussen, können Gruppen mittels Klammern erstellt werden.

Tabelle 15. Vorrang der Operatortypen
Operatortyp Vorrang Erläuterung

Verkettung

1

Strings werden verkettet, bevor andere Operationen stattfinden

Arithmetik

2

Arithmetische Operationen werden durchgeführt, nachdem Strings verkettet sind, aber vor Vergleichs- und logischen Operationen

Vergleiche

3

Vergleichsoperationen erfolgen nach String-Verkettung und arithmetischen Operationen, aber vor logischen Operationen

Logical

4

Logische Operatoren werden nach allen anderen Operatortypen ausgeführt

Verkettungsoperator

Der Verkettungsoperator, zwei Pipe-Zeichen, auch “Doppel-Pipe” — ‘||’ — verkettet (verbindet) zwei Zeichenketten zu einer einzigen Zeichenkette. Zeichenketten können dabei Konstante Werte oder abgeleitet von einer Spalte oder einem Ausdruck sein.

Beispiel
SELECT LAST_NAME || ', ' || FIRST_NAME AS FULL_NAME
FROM EMPLOYEE
Arithmetische Operatoren
Tabelle 16. Vorrang arithmetischer Operatoren
Operator Zweck Vorrang

+Zahl mit Vorzeichen

unäres Plus

1

-Zahl mit Vorzeichen

unäres Minus

1

*

Multiplikation

2

/

Division

2

+

Addition

3

-

Subtraktion

3

Beispiel
UPDATE T
    SET A = 4 + 1/(B-C)*D

Wenn Operatoren den gleichen Vorrang besitzen, werden diese von links nach rechts ausgewertet.

Vergleichsoperatoren
Tabelle 17. Prioritäten der Vergleichsoperatoren
Operator Zweck Priorität

IS

Überprüft, ob der Ausdruck auf der linken Seite (nicht) NULL oder der boolesche Wert auf der rechten Seite ist

1

=

Ist gleich, ist identisch mit

2

<>, !=, ~=, ^=

Ist ungleich zu

2

>

Ist größer als

2

<

Ist kleiner als

2

>=

Ist größer gleich als

2

<=

Ist kleiner gleich als

2

!>, ~>, ^>

Ist nicht größer als

2

!<, ~<, ^<

Ist nicht kleiner als

2

Diese Gruppe umfasst auch Vergleichsprädikate BETWEEN, LIKE, CONTAINING, SIMILAR TO und andere.

Beispiel
IF (SALARY > 1400) THEN
…
Logische Operatoren
Tabelle 18. Prioritäten logischer Operatoren
Operator Zweck Priorität

NOT

Negierung eines Suchkriteriums

1

AND

Kombiniert zwei oder mehr Prädikate, wobei jedes als wahr angesehen werden muss, damit der Gesamtausdruck ebenfalls als wahr aufgelöst wird

2

OR

Kombiniert zwei oder mehr Prädikate, wobei mindestens eines als wahr angesehen werden muss, damit der Gesamtausdruck ebenfalls als wahr aufgelöst wird

3

Beispiel
IF (A < B OR (A > C AND A > D) AND NOT (C = D)) THEN …
NEXT VALUE FOR
Verfügbar in

DSQL, PSQL

Syntax
NEXT VALUE FOR Sequenzname

NEXT VALUE FOR gibt den nächsten Wert einer Sequenz zurück. SEQUENCE ist ein SQL-konformer Begriff für Generatoren in Firebird und dessen Vorgänger, InterBase. Der Operator NEXT VALUE FOR ist equivalent zur ursprünglichen Funktion GEN_ID (…​, 1) und ist die empfohlene Syntax zum Holen des nächsten Wertes.

Anders als GEN_ID (…​, 1) verwendet NEXT VALUE FOR keine Parameter, wodurch es nicht möglich ist den aktuellen Wert einer Sequenz zu ermitteln sowie eine andere Schrittweite als 1 zu nutzen. GEN_ID (…​, <step value>) wird noch immer für diesen Zweck verwendet. Eine Schrittweite von 0 gibt den aktuellen Sequenzwert zurück.

Beispiel
NEW.CUST_ID = NEXT VALUE FOR CUSTSEQ;

4.1.3. Bedingte Ausdrücke

Ein bedingter Ausdruck ist einer der verschiedene Werte zurückgibt, je nach verwendeter Bedingung. Es besteht aus einem bedingten Funktionskonstrukt, wovon Firebird mehrere unterstützt. Dieser Abschnitt beschreibt nur ein bedingtes Ausdruckskonstrukt: CASE. Alle anderen bedingten Ausdrücke sind interne Funktionen und leiten sich von CASE ab und werden in Bedingte Funktionen beschrieben.

CASE
Verfügbar in

DSQL, PSQL

Das CASE-Konstrukt gibt einen einzigen Wert aus einer Reihe von Werten zurück. Zwei syntaktische Varianten werden unterstützt:

  • Das einfache CASE, vergleichbar zu einem CASE-Konstrukt in Pascal oder einem Switch in C

  • Das gesuchte CASE, welches wie eine Reihe aus “if …​ else if …​ else if”-Klauseln funktioniert.

Einfaches CASE
Syntax
…
CASE <test-expr>
  WHEN <expr> THEN <result>
  [WHEN <expr> THEN <result> ...]
  [ELSE <defaultresult>]
END
…

Bei dieser Variante wird test-expr mit dem ersten expr, dem zweiten expr usw. verglichen, bis eine Übereinstimmung gefunden wird und das entsprechende Ergebnis zurückgegeben wird. Wenn keine Übereinstimmung gefunden wird, wird defaultresult aus der optionalen ELSE-Klausel zurückgegeben. Wenn es keine Übereinstimmungen und keine ELSE-Klausel gibt, wird NULL zurückgegeben.

Das Matching funktioniert genauso wie der Operator “=”. Das heißt, wenn test-expr NULL ist, stimmt es mit keinem expr überein, nicht einmal mit einem Ausdruck, der in NULL aufgelöst wird.

Das zurückgegebene Ergebnis muss kein Literalwert sein: Es kann ein Feld- oder Variablenname, ein zusammengesetzter Ausdruck oder ein NULL-Literal sein.

Beispiel
SELECT
  NAME,
  AGE,
  CASE UPPER(SEX)
    WHEN 'M' THEN 'Male'
    WHEN 'F' THEN 'Female'
    ELSE 'Unknown'
  END GENDER,
RELIGION
    FROM PEOPLE

Eine Kurzform des einfachen CASE-Konstrukts wird auch in der DECODE -Funktion verwendet.

Gesuchtes CASE
Syntax
CASE
  WHEN <bool_expr> THEN <result>
  [WHEN <bool_expr> THEN <result> …]
  [ELSE <defaultresult>]
END

Der bool_expr-Ausdruck gibt ein ternäres logisches Ergebnis zurück: TRUE, FALSE oder NULL. Der erste Ausdruck, der TRUE ermittelt, wird als Ergebnis verwendet. Gibt kein Ausdruck TRUE zurück, kommt defaultresult aus der optionalen ELSE-Klausel zum Einsatz. Gibt kein Ausdruck TRUE zurück und gibt es keine ELSE-Klausel, ist der Rückgabewert NULL.

So wie im einfachen CASE-Konstrukt, muss das Ergebnis nicht zwangsläufig ein Literal sein: es kann ein Feld- oder Variablenname, ein zusammengesetzter Ausdruck oder NULL sein.

Beispiel
CANVOTE = CASE
  WHEN AGE >= 18 THEN 'Yes'
  WHEN AGE < 18 THEN 'No'
  ELSE 'Unsure'
END

4.1.4. NULL in Ausdrücken

NULL ist in SQL kein Wert, sondern ein state, der anzeigt, dass der Wert des Elements entweder unbekannt ist oder nicht existiert. Es ist weder eine Null, noch ein Leerzeichen, noch ein “leerer String”, und es verhält sich nicht wie ein Wert.

Wenn Sie NULL in numerischen, String- oder Datums-/Uhrzeit-Ausdrücken verwenden, ist das Ergebnis immer NULL. Wenn Sie NULL in logischen (booleschen) Ausdrücken verwenden, hängt das Ergebnis vom Typ der Operation und von anderen beteiligten Werten ab. Wenn Sie einen Wert mit NULL vergleichen, ist das Ergebnis unbekannt.

NULL heißt NULL, jedoch gilt in Firebird, dass das logische Ergebnis unknown ebenfalls durch NULL repräsentiert wird.

Ausdrücke die NULL zurückgeben

Ausdrücke in dieser Liste werden immer NULL zurückgeben:

1 + 2 + 3 + NULL
'Home ' || 'sweet ' || NULL
MyField = NULL
MyField <> NULL
NULL = NULL
not (NULL)

Wenn es Ihnen schwerfällt dies zu verstehen, beachten Sie, dass NULL ein Status ist, der für “unknown” (unbekannt) steht.

NULL in logischen Ausdrücken

Es wurde bereits gezeigt, dass not (NULL) in NULL aufgeht. Dieser Effekt ist etwas komplizierter für logische AND- sowie logische OR-Operatoren:

NULL or false  → NULL
NULL or true   → true
NULL or NULL   → NULL
NULL and false → false
NULL and true  → NULL
NULL and NULL  → NULL

Als grundlegende Faustregel gilt: Wenn die Anwendung von TRUE anstelle von NULL zu einem anderen Ergebnis führt als die Anwendung von FALSE, dann ist das Ergebnis des ursprünglichen Ausdrucks unknown oder NULL.

Beispiele
(1 = NULL) or (1 <> 1)    -- Ergebnis NULL
(1 = NULL) or FALSE       -- Ergebnis NULL
(1 = NULL) or (1 = 1)     -- Ergebnis TRUE
(1 = NULL) or TRUE        -- Ergebnis TRUE
(1 = NULL) or (1 = NULL)  -- Ergebnis NULL
(1 = NULL) or UNKNOWN     -- Ergebnis NULL
(1 = NULL) and (1 <> 1)   -- Ergebnis FALSE
(1 = NULL) and FALSE      -- Ergebnis FALSE
(1 = NULL) and (1 = 1)    -- Ergebnis NULL
(1 = NULL) and TRUE       -- Ergebnis NULL
(1 = NULL) and (1 = NULL) -- Ergebnis NULL
(1 = NULL) and UNKNOWN    -- Ergebnis NULL

4.1.5. Unterabfragen

Eine Unterabfrage ist eine spezielle Form eines Ausdrucks, die innerhalb einer anderen Abfrage eingebettet wird. Unterabfragen werden in der gleichen Weise geschrieben wie reguläre SELECT-Abfragen, werden jedoch von Klammern umschlossen. Unterabfrage-Ausdrücke können in folgender Art und Weise verwendet werden:

  • Um eine Ausgabespalte in der SELECT-Liste anzugeben

  • Um Werte zu holen oder als Kriterium für Suchprädikate (die WHERE- und HAVING-Klauseln)

  • Um ein Set zu erstellen, das die Eltern-Abfrage verwenden kann, so als wäre dies eine reguläre Tabelle oder View. Unterabfragen wie diese erscheinen in der FROM-Klausel (Derived Tables) oder in einer Common Table Expression (CTE)

Korrelierte Unterabfragen

Eine Unterabfrage kann korreliert sein. Eine Abfrage ist korreliert, wenn die Unterabfrage und die Hauptabfrage voneinander abhängig sind. Um jeden Datensatz in der Unterabfrage zu verarbeiten, muss ein Datensatz in der Hauptabfrage abgerufen werden; d.h. die Unterabfrage hängt vollständig von der Hauptabfrage ab.

Beispiel einer korrelierten Unterabfrage
SELECT *
FROM Customers C
WHERE EXISTS
  (SELECT *
   FROM Orders O
   WHERE C.cnum = O.cnum
     AND O.adate = DATE '10.03.1990');

Werden Unterabfragen verwendet um Werte einer Ausgabespalte aus einer SELECT-Liste zu holen, muss die Unterabfrage ein skalares Ergebnis zurückliefern.

Skalare Ergebnisse

Unterabfragen, die in Suchprädikaten verwendet werden, mit Ausnahme von existenziellen und quantifizierten Prädikaten, müssen ein skalares Ergebnis zurückgeben; Das heißt, nicht mehr als eine Spalte von nicht mehr als einer passenden Zeile oder Aggregation. Sollte mehr zurückgegeben werden, wird es zu einem Laufzeitfehler kommen (“Multiple rows in a singleton select…​”).

Obwohl es einen echten Fehler berichtet, kann die Nachricht etwas irreführend sein. Ein “singleton SELECT” ist eine Abfrage, die nicht mehr als eine Zeile zurückgeben kann. Jedoch sind “singleton” und “skalar” nicht gleichzusetzen: nicht alle singleton SELECTs müssen zwangsläufig skalar sein; und Einspalten-SELECTs können mehrere Zeilen für existenzielle und quantifizierte Prädikate zurückgeben.

Unterabfrage-Beispiele
  1. Eine Unterabfrage als Ausgabespalte in einer SELECT-Liste:

    SELECT
      e.first_name,
      e.last_name,
      (SELECT
           sh.new_salary
       FROM
           salary_history sh
       WHERE
           sh.emp_no = e.emp_no
       ORDER BY sh.change_date DESC ROWS 1) AS last_salary
    FROM
      employee e
  2. Eine Unterabfrage in der WHERE-Klausel, um das höchste Gehalt eines Mitarbeiters zu ermitteln und hierauf zu filtern:

    SELECT
      e.first_name,
      e.last_name,
      e.salary
    FROM employee e
    WHERE
      e.salary = (
        SELECT MAX(ie.salary)
        FROM employee ie
      )

4.2. Prädikate

Ein Prädikat ist ein einfacher Ausdruck, der eine Behauptung aufstellt, wir nennen sie P. Wenn P zu TRUE (wahr) aufgelöst wird, ist die Behauptung erfolgreich. Wird sie zu FALSE (unwahr, falsch) oder NULL (UNKNOWN) aufgelöst, ist die Behauptung falsch. Hier gibt es einen Fallstrick: Nehmen wir an, das Prädikat P gibt FALSE zurück. In diesem Falle gilt, dass NOT(P) TRUE zurückgeben wird. Andererseits gilt, falls P NULL (unknown) zurückgibt, dann gibt NOT(P) ebenfalls NULL zurück.

In SQL können Prädikate in CHECK-Constraints auftreten, WHERE- und HAVING-Klauseln, CASE-Ausdrücken, der IIF()-Funktion und in der ON-Bedingung der JOIN-Klausel.

4.2.1. Bedingungen

Eine Behauptung ist ein Statement über Daten, die, wie ein Prädikat, zu TRUE, FALSE oder NULL aufgelöst werden können. Behauptungen bestehen aus einem oder mehr Prädikaten, möglicherweise mittels NOT negiert und verbunden durch AND- sowie OR-Operatoren. Klammern können verwendet werden um Prädikate zu gruppieren und die Ausführungsreihenfolge festzulegen.

Ein Prädikat kann andere Prädikate einbetten. Die Ausführung ist nach außen gerichtet, das heißt, das innenliegendste Prädikat wird zuerst ausgeführt. Jede “Ebene” wird in ihrer Rangfolge ausgewertet bis der Wahrheitsgehalt der endgültigen Behauptung aufgelöst wird.

4.2.2. Vergleichs-Prädikate

Ein Vergleichsprädikat besteht aus zwei Ausdrücken, die mit einem Vergelichsoperator verbunden sind. Es existieren traditionel sechs Vergleichsoperatoren:

=, >, <, >=, <=, <>

Für die vollständige Liste der Vergleichsoperatoren mit ihren Variantenformen siehe Vergleichsoperatoren.

Wenn eine der Seiten (links oder rechts) eines Vergleichsprädikats NULL darin hat, wird der Wert des Prädikats UNKNOWN.

Beispiele
  1. Abrufen von Informationen über Computer mit der CPU-Frequenz nicht weniger als 500 MHz und der Preis niedriger als $800:

    SELECT *
    FROM Pc
    WHERE speed >= 500 AND price < 800;
  2. Abrufen von Informationen über alle Punktmatrixdrucker, die weniger als $300 kosten:

    SELECT *
    FROM Printer
    WHERE ptrtype = 'matrix' AND price < 300;
  3. Die folgende Abfrage gibt keine Daten zurück, auch nicht wenn es Drucker ohne zugewiesenen Typ gibt, da ein Prädikat, das NULL mit NULL vergleicht, NULL zurückgibt:

    SELECT *
    FROM Printer
    WHERE ptrtype = NULL AND price < 300;

    Andererseits kann ptrtype auf NULL getestet werden und ein Ergebnis zurückgeben: es ist nur kein _Vergleichstest:

    SELECT *
    FROM Printer
    WHERE ptrtype IS NULL AND price < 300;

     — Siehe auch IS [NOT] NULL.

Hinweis zu String-Vergleichen

Wenn die Felder CHAR und VARCHAR auf Gleichheit verglichen werden, werden abschließende Leerzeichen in allen Fällen ignoriert.

Andere Vergleichsprädikate

Andere Vergleichsprädikate werden durch Schlüsselwörter gekennzeichnet.

BETWEEN
Verfügbar in

DSQL, PSQL, ESQL

Syntax
<value> [NOT] BETWEEN <value_1> AND <value_2>

Das Prädikat BETWEEN testet, ob ein Wert in einen angegebenen Bereich von zwei Werten fällt. (NOT BETWEEN testet, ob der Wert nicht in diesen Bereich fällt.)

Die Operanden für das Prädikat BETWEEN sind zwei Argumente kompatibler Datentypen. Im Gegensatz zu einigen anderen DBMS ist das Prädikat BETWEEN in Firebird nicht symmetrisch — wenn der niedrigere Wert nicht das erste Argument ist, gibt das Prädikat BETWEEN immer FALSE zurück. Die Suche ist inklusiv (die von beiden Argumenten repräsentierten Werte werden in die Suche eingeschlossen). Mit anderen Worten, das Prädikat BETWEEN könnte umgeschrieben werden:

<value> >= <value_1> AND <value> <= <value_2>

Wenn BETWEEN in den Suchbedingungen von DML-Abfragen verwendet wird, kann der Firebird-Optimierer einen Index für die durchsuchte Spalte verwenden, falls dieser verfügbar ist.

Beispiel
SELECT *
FROM EMPLOYEE
WHERE HIRE_DATE BETWEEN date '1992-01-01' AND CURRENT_DATE
LIKE
Verfügbar in

DSQL, PSQL, ESQL

Syntax
<match_value> [NOT] LIKE <pattern>
   [ESCAPE <escape character>]

<match_value>      ::= character-type expression
<pattern>          ::= search pattern
<escape character> ::= escape character

Das Prädikat LIKE vergleicht den zeichenartigen Ausdruck mit dem im zweiten Ausdruck definierten Muster. Die Groß-/Kleinschreibung oder Akzent-Sensitivität für den Vergleich wird durch die verwendete Kollatierung bestimmt. Bei Bedarf kann für jeden Operanden eine Kollatierung angegeben werden.

Wildcards

Zwei Wildcard-Zeichen sind für die Suche verfügbar:

  • Das Prozentzeichen (%) berücksichtigt alle Sequenzen von null oder mehr Zeichen im getesteten Wert

  • Das Unterstrichzeichen (_) berücksichtigt jedes beliebige Einzelzeichen im getesteten Wert

Wenn der getestete Wert dem Muster entspricht, unter Berücksichtigung von Wildcard-Zeichen ist das Prädikat TRUE.

Verwendung der ESCAPE-Zeichen-Option

Wenn der Such-String eines der Wildcard-Zeichen beinhaltet, kann die ESCAPE-Klausel verwendet werden, um ein Escape-Zeichen zu definieren. Das Escape-Zeichen muss dem ‘%’ oder ‘_’ Symbol im Suchstring vorangestellt werden, um anzuzeigen, dass das Symbol als wörtliches Zeichen interpretiert werden soll.

Beispiele für LIKE
  1. Finde die Nummern der Abteilung, deren Namen mit dem Wort “Software” starten:

    SELECT DEPT_NO
    FROM DEPT
    WHERE DEPT_NAME LIKE 'Software%';

    Es ist möglich einen Index für das Feld DEPT_NAME zu verwenden, sofern dieser existiert.

    Über LIKE und den Optimizer

    Eigentlich verwendet das LIKE-Prädikat keinen Index. Wird das Prädikat jedoch in Form von LIKE 'string%' verwendet, wird dieses zum Prädikat STARTING WITH konvertiert, welches einen Index verwendet.

    Somit gilt — wenn Sie nach einem Wortanfang suchen, sollten Sie das Prädikat STARTING WITH anstelle von LIKE verwenden.

  2. Suchen Sie nach Mitarbeitern, deren Namen aus 5 Buchstaben bestehen, mit den Buchstaben “Sm” beginnen und mit “th” enden. Das Prädikat gilt für Namen wie “Smith” und “Smyth”.

    SELECT
      first_name
    FROM
      employee
    WHERE first_name LIKE 'Sm_th'
  3. Suche nach allen Mandanten, deren Adresse den String “Rostov” enthält:

    SELECT *
    FROM CUSTOMER
    WHERE ADDRESS LIKE '%Rostov%'

    Benötigen Sie eine Suche, die Groß- und Kleinschreibung innerhalb einer Zeichenkette ignoriert (LIKE '%Abc%'), sollten Sie das CONTAINING-Prädikat, anstelle des LIKE-Prädikates, verwenden.

  4. Suchen Sie nach Tabellen, die den Unterstrich im Namen enthalten. Als Escape-Zeichen wird das Zeichen ‘#’ verwendet:

    SELECT
      RDB$RELATION_NAME
    FROM RDB$RELATIONS
    WHERE RDB$RELATION_NAME LIKE '%#_%' ESCAPE '#'
STARTING WITH
Verfügbar in

DSQL, PSQL, ESQL

Syntax
<value> [NOT] STARTING WITH <value>

Das Prädikat STARTING WITH sucht nach einer Zeichenkette oder einem zeichenkettenähnlichen Datentyp, die mit den Zeichen des Argumentes value beginnt. Die Suche unterscheidet zwischen Groß- und Kleinschreibung.

Wenn STARTING WITH als Suchkriterium in DML-Abfragen verwendet wird, nutzt der Firebird-Optimizer einen Index auf der Suchspalte, sofern vorhanden.

Beispiel

Suche nach Mitarbeitern deren Namen mit “Jo” beginnen:

SELECT LAST_NAME, FIRST_NAME
FROM EMPLOYEE
WHERE LAST_NAME STARTING WITH 'Jo'
Siehe auch

LIKE

CONTAINING
Verfügbar in

DSQL, PSQL, ESQL

Syntax
<value> [NOT] CONTAINING <value>

Das Prädikat CONTAINING sucht nach einem String oder einem stringähnlichen Typ und sucht nach der Zeichenfolge, die seinem Argument entspricht. Es kann für eine alphanumerische (stringartige) Suche nach Zahlen und Datumsangaben verwendet werden. Bei einer CONTAINING-Suche wird die Groß-/Kleinschreibung nicht beachtet. Wenn jedoch eine akzentsensitive Sortierung verwendet wird, erfolgt die Suche akzentsensitiver.

Beispiele
  1. Suche nach Projekten, deren Namen die Teilzeichenfolge “Map” enthalten:

    SELECT *
    FROM PROJECT
    WHERE PROJ_NAME CONTAINING 'Map';

    Zwei Zeilen mit den Namen “AutoMap” und “MapBrowser port” werden zurückgegeben.

  2. Suche nach Änderungen in den Gehältern, die die Zahl 84 im Datum enthalten (in diesem Falle heißt dies, Änderungen im Jahr 1984):

    SELECT *
    FROM SALARY_HISTORY
    WHERE CHANGE_DATE CONTAINING 84;
Siehe auch

LIKE

SIMILAR TO
Verfügbar in

DSQL, PSQL

Syntax
string-expression [NOT] SIMILAR TO <pattern> [ESCAPE <escape-char>]

<pattern>     ::= an SQL regular expression
<escape-char> ::= a single character

SIMILAR TO findet eine Zeichenkette anhand eines Regulären Ausdruck-Musters in SQL (engl. SQL Regular Expression Pattern). Anders als in einigen anderen Sprachen muss das Muster mit der gesamten Zeichenkette übereinstimmen, um erfolgreich zu sein — die Übereinstimmung eines Teilstrings reicht nicht aus. Ist ein Operand NULL, ist auch das Ergebnis NULL. Andernfalls ist das Ergebnis TRUE oder FALSE.

Syntax: SQL Reguläre Ausdrücke

Die folgende Syntax definiert das SQL-Standardausdruckformat. Es ist eine komplette und korrekte Top-down-Definition. Es ist auch sehr formell, ziemlich lang und vermutlich perfekt geeignet, jeden zu entmutigen, der nicht schon Erfahrungen mit Regulären Ausdrücken (oder mit sehr formalen, eher langen Top-down-Definitionen) gesammelt hat. Fühlen Sie sich frei, dies zu überspringen und den nächsten Abschnitt, Aufbau Regulärer Ausdrücke, zu lesen, der einen Bottom-up-Ansatz verfolgt und sich an den Rest von uns richtet.

<regular expression> ::= <regular term> ['|' <regular term> ...]

<regular term> ::= <regular factor> ...

<regular factor> ::= <regular primary> [<quantifier>]

<quantifier> ::= ? | * | + | '{' <m> [,[<n>]] '}'

<m>, <n> ::= unsigned int, mit <m> <= <n> wenn beide vorhanden

<regular primary> ::=
    <character> | <character class> | %
  | (<regular expression>)

<character> ::= <escaped character> | <non-escaped character>

<escaped character> ::=
  <escape-char> <special character> | <escape-char> <escape-char>

<special character> ::= eines der Zeichen []()|^-+*%\?{}

<non-escaped character> ::=
  ein Zeichen, das nicht ein <special character> ist
  und nicht gleich <escape-char> (wenn definiert)_

<character class> ::=
    '' | '[' <member> ... ']' | '[^' <non-member> ... ']'
  | '[' <member> ... '^' <non-member> ... ']'

<member>, <non-member> ::= <character> | <range> | <predefined class>

<range> ::= <character>-<character>

<predefined class> ::= '[:' <predefined class name> ':]'

<predefined class name> ::=
  ALPHA | UPPER | LOWER | DIGIT | ALNUM | SPACE | WHITESPACE
Aufbau Regulärer Ausdrücke

Dieser Abschnitt behandelt die Elemente und Regeln zum Aufbau Regulärer Ausdrücke in SQL.

Zeichen

Innerhalb Regulärer Ausdrücke repräsentieren die meisten Zeichen sich selbst. Die einzige Ausnahme bilden die folgenden Zeichen:

[ ] ( ) | ^ - + * % _ ? { }

... und das Escape-Zeichen, sofern definiert.

Ein Regulärer Ausdruck, der keine Sonderzeichen oder Escape-Zeichen beinhaltet, findet nur Strings, die identisch zu sich selbst sind (abhängig von der verwendeten Collation). Das heißt, es agiert wie der ‘=’-Operator:

'Apple' similar to 'Apple'  -- true
'Apples' similar to 'Apple' -- false
'Apple' similar to 'Apples' -- false
'APPLE' similar to 'Apple'  -- abhängig von der Collation
Wildcards

Die bekannten SQL-Wildcards ‘_’ und ‘%’ finden beliebige Einzelzeichen und Strings beliebiger Länge:

'Birne' similar to 'B_rne'   -- true
'Birne' similar to 'B_ne'    -- false
'Birne' similar to 'B%ne'    -- true
'Birne' similar to 'Bir%ne%' -- true
'Birne' similar to 'Birr%ne' -- false

Beachten Sie, wie ‘%’ auch den leeren String berücksichtigt.

Zeichenklassen

Ein Bündel von Zeichen, die in Klammern eingeschlossen sind, definiert eine Zeichenklasse. Ein Zeichen in der Zeichenfolge entspricht einer Klasse im Muster, wenn das Zeichen Mitglied der Klasse ist:

'Citroen' similar to 'Cit[arju]oen'     -- true
'Citroen' similar to 'Ci[tr]oen'        -- false
'Citroen' similar to 'Ci[tr][tr]oen'    -- true

Wie aus der zweiten Zeile ersichtlich ist, entspricht die Klasse nur einem einzigen Zeichen, nicht einer Sequenz.

Innerhalb einer Klassendefinition definieren zwei Zeichen, die durch einen Bindestrich verbunden sind, einen Bereich. Ein Bereich umfasst die beiden Endpunkte und alle Zeichen, die zwischen ihnen in der aktiven Sortierung liegen. Bereiche können überall in der Klassendefinition ohne spezielle Begrenzer platziert werden, um sie von den anderen Elementen zu trennen.

'Datte' similar to 'Dat[q-u]e'          -- true
'Datte' similar to 'Dat[abq-uy]e'       -- true
'Datte' similar to 'Dat[bcg-km-pwz]e'   -- false
Vordefinierte Zeichenklassen

Die folgenden vordefinierten Zeichenklassen können auch in einer Klassendefinition verwendet werden:

[:ALPHA:]

Lateinische Buchstaben a..z und A..Z. Mit einer akzentunempfindlichen Sortierung stimmt diese Klasse auch mit akzentuierten Formen dieser Zeichen überein.

[:DIGIT:]

Dezimalziffern 0..9.

[:ALNUM:]

Gesamtheit aus [:ALPHA:] und [:DIGIT:].

[:UPPER:]

Großgeschriebene Form der lateinischen Buchstaben A..Z. Findet auch kleingeschriebene Strings mit groß- und kleinschreibunempfindlicher Collation sowie akzentunempfindlicher Collation.

[:LOWER:]

Kleingeschriebene Form der lateinischen Buchstaben A..Z. Findet auch großgeschriebene Strings mit groß- und kleinschreibunempfindlicher Collation sowie akzentunempfindlicher Collation.

[:SPACE:]

Findet das Leerzeichen (ASCII 32).

[:WHITESPACE:]

Findet horizontalen Tabulator (ASCII 9), Zeilenvorschub (ASCII 10), vertikalen Tabulator (ASCII 11), Seitenvorschub (ASCII 12), Wagenrücklauf (ASCII 13) und Leerzeichen (ASCII 32).

Das Einbinden einer vordefinierten Klasse hat den gleichen Effekt wie das Einbinden all seiner Mitglieder. Vordefinierte Klassen sind nur in Klassendefinitionen erlaubt. Wenn Sie gegen eine vordefinierte Klasse prüfen und gegen nichts sonst, platzieren Sie ein zusätzliches Paar von Klammern um sie herum.

'Erdbeere' similar to 'Erd[[:ALNUM:]]eere'     -- true
'Erdbeere' similar to 'Erd[[:DIGIT:]]eere'     -- false
'Erdbeere' similar to 'Erd[a[:SPACE:]b]eere'   -- true
'Erdbeere' similar to [[:ALPHA:]]              -- false
'E'        similar to [[:ALPHA:]]              -- true

Wenn eine Klassendefinition mit einem Caret-Zeichen beginnt, wird alles, was folgt, aus der Klasse ausgeschlossen. Alle anderen Zeichen stimmen überein:

'Framboise' similar to 'Fra[^ck-p]boise'       -- false
'Framboise' similar to 'Fr[^a][^a]boise'       -- false
'Framboise' similar to 'Fra[^[:DIGIT:]]boise'  -- true

If the caret is not placed at the start of the sequence, the class contains everything before the caret, except for the elements that also occur after the caret:

'Grapefruit' similar to 'Grap[a-m^f-i]fruit'   -- true
'Grapefruit' similar to 'Grap[abc^xyz]fruit'   -- false
'Grapefruit' similar to 'Grap[abc^de]fruit'    -- false
'Grapefruit' similar to 'Grap[abe^de]fruit'    -- false

'3' similar to '[[:DIGIT:]^4-8]'               -- true
'6' similar to '[[:DIGIT:]^4-8]'               -- false

Zuletzt sei noch erwähnt, dass die Wildcard-Zeichen ‘_’ eine eigene Zeichenklasse sind, die einem beliebigen einzelnen Zeichen entspricht.

Bezeichner

Ein Fragezeichen, direkt von einem weiteren Zeichen oder Klasse gefolgt, gibt an, dass das folgende Element gar nicht oder einmalig vorkommen darf:

'Hallon' similar to 'Hal?on'                   -- false
'Hallon' similar to 'Hal?lon'                  -- true
'Hallon' similar to 'Halll?on'                 -- true
'Hallon' similar to 'Hallll?on'                -- false
'Hallon' similar to 'Halx?lon'                 -- true
'Hallon' similar to 'H[a-c]?llon[x-z]?'        -- true

Ein Sternchen (‘*’) unmittelbar nach einem Zeichen oder einer Klasse zeigt an, dass das vorangehende Element 0-mal oder öfter vorkommen kann, damit es übereinstimmt:

'Icaque' similar to 'Ica*que'                  -- true
'Icaque' similar to 'Icar*que'                 -- true
'Icaque' similar to 'I[a-c]*que'               -- true
'Icaque' similar to '_*'                       -- true
'Icaque' similar to '[[:ALPHA:]]*'             -- true
'Icaque' similar to 'Ica[xyz]*e'               -- false

Ein Pluszeichen (‘+’) unmittelbar nach einem Zeichen oder einer Klasse gibt an, dass das vorangehende Element mindestens einmal vorkommen muss, damit es übereinstimmt:

'Jujube' similar to 'Ju_+'                     -- true
'Jujube' similar to 'Ju+jube'                  -- true
'Jujube' similar to 'Jujuber+'                 -- false
'Jujube' similar to 'J[jux]+be'                -- true
'Jujube' sililar to 'J[[:DIGIT:]]+ujube'       -- false

Wenn auf ein Zeichen oder eine Klasse eine Zahl in geschweiften Klammern folgt (‘{’ und ‘}’), muss sie genau so oft wiederholt werden, damit sie übereinstimmt:

'Kiwi' similar to 'Ki{2}wi'                    -- false
'Kiwi' similar to 'K[ipw]{2}i'                 -- true
'Kiwi' similar to 'K[ipw]{2}'                  -- false
'Kiwi' similar to 'K[ipw]{3}'                  -- true

Wenn der Zahl ein Komma folgt (‘,’), muss das Element mindestens so oft wiederholt werden, damit es übereinstimmt:

'Limone' similar to 'Li{2,}mone'               -- false
'Limone' similar to 'Li{1,}mone'               -- true
'Limone' similar to 'Li[nezom]{2,}'            -- true

Wenn die geschweiften Klammern zwei durch ein Komma getrennte Zahlen enthalten, wobei die zweite Zahl nicht kleiner als die erste ist, muss das Element mindestens die erste Zahl und höchstens die zweite Zahl wiederholt werden, um zu entsprechen:

'Mandarijn' similar to 'M[a-p]{2,5}rijn'       -- true
'Mandarijn' similar to 'M[a-p]{2,3}rijn'       -- false
'Mandarijn' similar to 'M[a-p]{2,3}arijn'      -- true

Die Bezeichner ‘?’, ‘*’ und ‘+’ sind Kurzschreibweisen für {0,1}, {0,} und {1,}.

Oder-verknüpfte Terme

Reguläre Ausdrücke können Oder-verknüpft werden mittels ‘|’-Operator. Eine Gesamtübereinstimmung tritt auf, wenn die Argumentzeichenkette mit mindestens einem Term übereinstimmt.

'Nektarin' similar to 'Nek|tarin'              -- false
'Nektarin' similar to 'Nektarin|Persika'       -- true
'Nektarin' similar to 'M_+|N_+|P_+'            -- true
Unterausdrücke

Ein oder mehrere Teile der regulären Ausdrücke können in Unterausdrücke gruppiert werden (auch Untermuster genannt), indem diese in runde Klammern eingeschlossen werden. Ein Unterausdruck ist ein eigener regulärer Ausdruck. Dieser kann alle erlaubten Elemente eines regulären Ausdrucks enthalten, und auch eigene Bezeichner.

'Orange' similar to 'O(ra|ri|ro)nge'           -- true
'Orange' similar to 'O(r[a-e])+nge'            -- true
'Orange' similar to 'O(ra){2,4}nge'            -- false
'Orange' similar to 'O(r(an|in)g|rong)?e'      -- true
Sonderzeichen escapen

Um mit einem Sonderzeichen in regulären Ausdrücken abzugleichen, muss dieses Zeichen mit Escapezeichen versehen werden. Es gibt kein Standard-Escape-Zeichen; Stattdessen gibt der Benutzer bei Bedarf eine an:

'Peer (Poire)' similar to 'P[^ ]+ \(P[^ ]+\)' escape '\'    -- true
'Pera [Pear]'  similar to 'P[^ ]+ #[P[^ ]+#]' escape '#'    -- true
'Päron-äppledryck' similar to 'P%$-ä%' escape '$'           -- true
'Pärondryck' similar to 'P%--ä%' escape '-'                 -- false

Die letzte Zeile demonstriert, dass das Escape-Zeichen auch sich selbst escapen kann, wenn notwendig.

IS [NOT] DISTINCT FROM
Verfügbar in

DSQL, PSQL

Syntax
<operand1> IS [NOT] DISTINCT FROM <operand2>

Zwei Operanden werden als DISTINCT angesehen, wenn sie unterschiedliche Werte besitzen oder wenn einer NULL ist und der andere nicht-NULL. Sie werden als NOT DISTINCT angesehen, wenn sie den gleichen Wert besitzen oder beide Operanden NULL sind.

IS [NOT] DISTINCT FROM liefert immer TRUE oder FALSE und niemals UNKNOWN (NULL) (unbekannter Wert). Die Operatoren ‘=’ und ‘<>’ geben umgekehrt UNKNOWN (NULL) zurück, wenn einer oder beide Operanden NULL sind.

Tabelle 19. Ergebnisse verschiedener Vergleichsprädikate

Operandenwerte

Ergebnis verschiedener Prädikate

=

IS NOT DISTINCT FROM

<>

IS DISTINCT FROM

Gleiche Werte

TRUE

TRUE

FALSE

FALSE

Verschiedene Werte

FALSE

FALSE

TRUE

TRUE

Beide NULL

UNKNOWN

TRUE

UNKNOWN

FALSE

Einer NULL, einer nicht-NULL

UNKNOWN

FALSE

UNKNOWN

TRUE

Beispiele
SELECT ID, NAME, TEACHER
FROM COURSES
WHERE START_DAY IS NOT DISTINCT FROM END_DAY;

-- PSQL-Fragment
IF (NEW.JOB IS DISTINCT FROM OLD.JOB)
THEN POST_EVENT 'JOB_CHANGED';
Boolesches IS [NOT]
Verfügbar in

DSQL, PSQL

Syntax
<value> IS [NOT] { TRUE | FALSE | UNKNOWN }

Das IS-Prädikat mit booleschen Literalwerten prüft, ob der Ausdruck auf der linken Seite mit dem booleschen Wert auf der rechten Seite übereinstimmt. Der Ausdruck auf der linken Seite muss vom Typ BOOLEAN sein, sonst kommt es zu einer Ausnahme.

Das IS [NOT] UNKNOWN entspricht IS [NOT] NULL.

Die rechte Seite des Prädikats akzeptiert nur die Literale TRUE, FALSE und UNKNOWN (und NULL). Es akzeptiert keine Ausdrücke.

Verwenden des IS-Prädikats mit einem booleschen Datentyp
-- FALSE-Wert prüfen
SELECT * FROM TBOOL WHERE BVAL IS FALSE;

ID            BVAL
============= =======
2             <false>

-- UNKNOWN-Wert prüfen
SELECT * FROM TBOOL WHERE BVAL IS UNKNOWN;

ID            BVAL
============= =======
3             <null>
Siehe auch

IS [NOT] NULL

IS [NOT] NULL
Verfügbar in

DSQL, PSQL, ESQL

Syntax
<value> IS [NOT] NULL

Da NULL kein Wert ist, sind diese Operatoren keine Vergleichsoperatoren. Das Prädikat IS [NOT] NULL prüft die Behauptung, dass der Ausdruck auf der linken Seite einen Wert (IS NOT NULL) oder keinen Wert hat (IS NULL).

Beispiel

Suche nach Verkäufen, die kein Versanddatum besitzen:

SELECT * FROM SALES
WHERE SHIP_DATE IS NULL;
Hinweis bezüglich des IS-Prädikates

Bis einschließlich Firebird 2.5, hat das Prädikat IS, wie andere Vergleichsprädikate, keinen Vorrang gegenüber anderer. Ab Firebird 3.0 hat dieses Prädikat Vorrang gegenüber den anderen.

4.2.3. Existenzprädikate

Diese Gruppe von Prädikaten umfasst diejenigen, die Unterabfragen verwenden, um Werte für alle Arten von Zusicherungen in Suchbedingungen zu übermitteln. Existenzielle Prädikate werden so genannt, weil sie verschiedene Methoden verwenden, um auf existence oder non-existence einer Bedingung zu testen, und TRUE zurückgeben, wenn die Existenz oder Nichtexistenz bestätigt wird oder FALSE andernfalls.

EXISTS
Verfügbar in

DSQL, PSQL, ESQL

Syntax
[NOT] EXISTS (<select_stmt>)

Das Prädikat EXISTS verwendet als Argument einen Unterabfrageausdruck. Es gibt TRUE zurück, wenn das Ergebnis der Unterabfrage mindestens eine Zeile enthalten würde; andernfalls gibt es FALSE zurück.

NOT EXISTS gibt FALSE zurück, wenn das Ergebnis der Unterabfrage mindestens eine Zeile enthalten würde; andernfalls gibt es TRUE zurück.

Die Unterabfrage kann mehrere Spalten enthalten, oder SELECT *, da die Prüfung anhand der zurückgegebenen Datenzeilen vorgenommen wird, die die Bedingungen erfüllen.

Beispiele
  1. Finde die Mitarbeiter, die Projekte haben.

    SELECT *
    FROM employee
    WHERE EXISTS(SELECT *
                 FROM  employee_project ep
                 WHERE ep.emp_no = employee.emp_no)
  2. Finde die Mitarbeiter, die keine Projekte haben.

    SELECT *
    FROM employee
    WHERE NOT EXISTS(SELECT *
                     FROM employee_project ep
                     WHERE ep.emp_no = employee.emp_no)
IN
Verfügbar in

DSQL, PSQL, ESQL

Syntax
<value> [NOT] IN (<select_stmt> | <value_list>)

<value_list> ::= <value_1> [, <value_2> …]

Das Prädikat IN prüft, ob der Wert des Ausdrucks auf der linken Seite im Wertesatz der rechten Seite vorkommt. Der Wertesatz darf nicht mehr als 1500 Elemente enthalten. Das IN-Prädikat kann mit folgender äquivalenter Form ersetzt werden:

(<value> = <value_1> [OR <value> = <value_2> …])

<value> = { ANY | SOME } (<select_stmt>)

Wenn das Prädikat IN als Suchbedingung in DML-Abfragen verwendet wird, kann der Firebird-Optimizer einen Index auf die Suchspalte nutzen, sofern einer vorhanden ist.

In seiner zweiten Form prüft das Prädikat IN, ob der linke Ausdruckswert im Ergebnis der Unterabfrage vorhanden ist (oder nicht vorhanden, wenn NOT IN verwendet wird).

Die Unterabfrage darf nur eine Spalte abfragen, andernfalls wird es zum Fehler “count of column list and variable list do not match” kommen.

Abfragen, die das Prädikat IN mit einer Unterabfrage verwenden, können durch eine ähnliche Abfrage mittels des EXISTS-Prädikates ersetzt werden. Zum Beispiel folgende Abfrage:

SELECT
  model, speed, hd
FROM PC
WHERE
model IN (SELECT model
          FROM product
          WHERE maker = 'A');

kann ersetzt werden mittels EXISTS-Prädikat:

SELECT
  model, speed, hd
FROM PC
WHERE
 EXISTS (SELECT *
         FROM product
         WHERE maker = 'A'
           AND product.model = PC.model);

Jedoch gilt zu beachten, dass eine Abfrage mittels NOT IN und einer Unterabfrage nicht immer das gleiche Ergebnis zurückliefert wie sein Gegenpart mit NOT EXISTS. Dies liegt daran, dass EXISTS immer TRUE oder FALSE zurückgibt, wohingegen IN NULL in diesen beiden Fällen zurückliefert:

  1. wenn der geprüfte Wert NULL ist und die IN ()-Liste nicht leer ist

  2. wenn der geprüfte Wert keinen Treffer in der IN ()-Liste enthält und mindestens ein Element NULL ist.

Nur in diesen beiden Fällen wird IN () NULL zurückgeben, während das EXISTS-Prädikat FALSE zurückgibt ('keine passende Zeile gefunden', engl. 'no matching row found'). In einer Suche oder, zum Beispiel in einem IF (…​)-Statement, bedeuten beide Ergebnisse einen “Fehler” und es macht damit keinen Unterschied.

Aber für die gleichen Daten gibt NOT IN () NULL zurück, während NOT EXISTS TRUE zurückgibt, was das Gegenteilige Ergebnis ist.

Schauen wir uns das folgendes Beispiel an:

-- Suche nach Bürgern die nicht am gleichen Tag wie eine
-- berühmte New Yorker Persönlichkeit geboren wurden
SELECT P1.name AS NAME
FROM Personnel P1
WHERE P1.birthday NOT IN (SELECT C1.birthday
                          FROM Celebrities C1
                          WHERE C1.birthcity = 'New York');

Nehmen wir nun an, dass die Liste der New Yorker Berühmtheiten nicht leer ist und mindestens einen NULL-Geburtstag aufweist. Dann gilt für alle Bürger, die nicht am gleichen Tag mit einer Berühmtheit Geburtstag haben, dass NOT IN NULL zurückgibt, da dies genau das ist was IN tut. Die Suchbedingung wurde nicht erfüllt und die Bürger werden nicht im Ergebnis des SELECT berücksichtigt, da die Aussage falsch ist.

Bürger, die am gleichen Tag wie eine Berühmtheit Geburtstag feiern, wird NOT IN korrekterweise FALSE zurückgeben, womit diese ebenfalls aussortiert werden, und damit keine Zeile zurückgegeben wird.

Wird die Form NOT EXISTS verwendet:

-- Suche nach Bürgern, die nicht am gleichen Tag wie eine
-- berühmte New Yorker Persönlichkeit geboren wurden
SELECT P1.name AS NAME
FROM Personnel P1
WHERE NOT EXISTS (SELECT *
                  FROM Celebrities C1
                  WHERE C1.birthcity = 'New York'
                    AND C1.birthday = P1.birthday);

nicht-Übereinstimmungen werden im NOT EXISTS-Ergebnis TRUE erhalten und ihre Datensätze landen im Rückgabesatz.

Wenn bei der Suche nach einer Nichtübereinstimmung die Möglichkeit besteht, dass NULL gefunden wird, sollten Sie NOT EXISTS verwenden.

Beispiele für die Verwendung
  1. Finde Mitarbeiter mit den Namen “Pete”, “Ann” und “Roger”:

    SELECT *
    FROM EMPLOYEE
    WHERE FIRST_NAME IN ('Pete', 'Ann', 'Roger');
  2. Finde alle Computer, die deren Hersteller mit dem Buchstaben “A” beginnt:

    SELECT
      model, speed, hd
    FROM PC
    WHERE
      model IN (SELECT model
                FROM product
                WHERE maker STARTING WITH 'A');
Siehe auch

EXISTS

SINGULAR
Verfügbar in

DSQL, PSQL, ESQL

Syntax
[NOT] SINGULAR (<select_stmt>)

Das Prädikat SINGULAR nimmt eine Unterabfrage als Argument und wertet sie als TRUE, wenn die Unterabfrage genau eine Ergebniszeile zurückgibt; andernfalls wird das Prädikat als FALSE ausgewertet. Die Unterabfrage kann mehrere Ausgabespalten auflisten, da die Zeilen sowieso nicht zurückgegeben werden. Sie werden nur auf (singuläre) Existenz geprüft. Der Kürze halber wird normalerweise ‘SELECT *’ angegeben. Das Prädikat SINGULAR kann nur zwei Werte zurückgeben: TRUE oder FALSE.

Beispiel

Finden Sie die Mitarbeiter, die nur ein Projekt haben.

SELECT *
FROM employee
WHERE SINGULAR(SELECT *
               FROM employee_project ep
               WHERE ep.emp_no = employee.emp_no)

4.2.4. Quantifizierte Unterabfrage-Prädikate

Ein Quantifizierer ist ein logischer Operator, der die Anzahl der Objekte festlegt, für die diese Behauptung wahr ist. Es ist keine numerische Größe, sondern eine logische, die die Behauptung mit dem vollen Satz möglicher Objekte verbindet. Solche Prädikate basieren auf logischen universellen und existentiellen Quantifizierern, die in der formalen Logik erkannt werden.

In Unterabfrageausdrücken ermöglichen quantifizierte Prädikate den Vergleich einzelner Werte mit den Ergebnissen von Unterabfragen; sie haben die folgende gemeinsame Form:

<value expression> <comparison operator> <quantifier> <subquery>
ALL
Verfügbar in

DSQL, PSQL, ESQL

Syntax
<value> <op> ALL (<select_stmt>)

Wenn der ALL-Quantifizierer verwendet wird, ist das Prädikat TRUE, wenn jeder Wert, der von der Unterabfrage zurückgegeben wird, die Bedingung des Prädikates in der Hauptabfrage erfüllt ist.

Beispiel

Zeige nur jene Kunden an, deren Bewertungen höher sind als die Bewertung jedes Kunden in Paris.

SELECT c1.*
FROM Customers c1
WHERE c1.rating > ALL
      (SELECT c2.rating
       FROM Customers c2
       WHERE c2.city = 'Paris')

Wenn die Unterabfrage einen leeren Satz zurückgibt, ist das Prädikat TRUE für jeden linken Wert, unabhängig vom Operator. Dies mag widersprüchlich erscheinen, denn jeder linke Wert wird gegenüber dem rechten betrachtet als: kleiner als, größer als, gleich sowie ungleich.

Dennoch passt dies perfekt in die formale Logik: Wenn der Satz leer ist, ist das Prädikat 0 mal wahr, d.h. für jede Zeile im Satz.

ANY and SOME
Verfügbar in

DSQL, PSQL, ESQL

Syntax
<value> <op> {ANY | SOME} (<select_stmt>)

Die Quantifizierer ANY und SOME sind in ihrem Verhalten identisch. Offensichtlich sind beide im SQL-Standard vorhanden, so dass sie austauschbar verwendet werden können, um die Lesbarkeit der Operatoren zu verbessern. Wird der ANY- oder SOME-Quantifizierer verwendet, ist das Prädikat TRUE, wenn einer der zurückgegebenen Werte der Unterabfrage die Suchbedingung der Hauptabfrage erfüllt. Gibt die Unterabfrage keine Zeile zurück, wird das Prädikat automtisch als FALSE angesehen.

Beispiel

Zeige nur die Kunden, deren Bewertungen höher sind als die eines oder mehrerer Kunden in Rom.

SELECT *
FROM Customers
WHERE rating > ANY
      (SELECT rating
       FROM Customers
       WHERE city = 'Rome')

5. Anweisungen der Datendefinitionssprache (DDL)

DDL ist die Teilmenge der Datendefinitionssprache der SQL-Sprache von Firebird. DDL-Anweisungen werden verwendet, um von Benutzern erstellte Datenbankobjekte zu erstellen, zu ändern und zu löschen. Wenn eine DDL-Anweisung festgeschrieben wird, werden die Metadaten für das Objekt erstellt, geändert oder gelöscht.

5.1. DATABASE

In diesem Abschnitt wird beschrieben, wie Sie eine Datenbank erstellen, eine Verbindung zu einer vorhandenen Datenbank herstellen, die Dateistruktur einer Datenbank ändern und eine Datenbank löschen. Es erklärt auch, wie Sie eine Datenbank auf zwei ganz unterschiedliche Weisen sichern und wie Sie die Datenbank in den "kopiersicheren" Modus schalten, um eine externe Sicherung sicher durchzuführen.

5.1.1. CREATE DATABASE

Verwendet für

Erstellen einer neuen Datenbank

Verfügbar in

DSQL, ESQL

Syntax
CREATE {DATABASE | SCHEMA} <filespec>
  [<db_initial_option> [<db_initial_option> ...]]
  [<db_config_option> [<db_config_option> ...]]

<db_initial_option> ::=
    USER username
  | PASSWORD 'password'
  | ROLE rolename
  | PAGE_SIZE [=] size
  | LENGTH [=] num [PAGE[S]]
  | SET NAMES 'charset'

<db_config_option> ::=
    DEFAULT CHARACTER SET default_charset
      [COLLATION collation] -- not supported in ESQL
  | <sec_file>
  | DIFFERENCE FILE 'diff_file' -- not supported in ESQL

<filespec> ::= "'" [server_spec]{filepath | db_alias} "'"

<server_spec> ::=
    host[/{port | service}]:
  | \\host\
  | <protocol>://[host[:{port | service}]/]

<protocol> ::= inet | inet4 | inet6 | wnet | xnet

<sec_file> ::=
  FILE 'filepath'
  [LENGTH [=] num [PAGE[S]]
  [STARTING [AT [PAGE]] pagenum]

Jede db_initial_option und db_config_option kann höchstens einmal vorkommen, außer sec_file, die null oder mehrmals vorkommen kann.

Tabelle 20. CREATE DATABASE-Anweisungsparameter
Parameter Beschreibung

filespec

Dateispezifikation für primäre Datenbankdatei

server_spec

Spezifikation des Remote-Servers. Einige Protokolle erfordern die Angabe eines Hostnamens. Enthält optional eine Portnummer oder einen Dienstnamen. Erforderlich, wenn die Datenbank auf einem Remoteserver erstellt wird.

filepath

Vollständiger Pfad und Dateiname einschließlich seiner Erweiterung. Der Dateiname muss gemäß den Regeln des verwendeten Plattform-Dateisystems angegeben werden.

db_alias

Datenbankalias, der zuvor in der Datei databases.conf erstellt wurde

host

Hostname oder IP-Adresse des Servers, auf dem die Datenbank erstellt werden soll

port

Die Portnummer, auf der der Remote-Server lauscht (Parameter RemoteServicePort in der Datei firebird.conf)

service

Dienstname. Muss mit dem Parameterwert von RemoteServiceName in der Datei firebird.conf übereinstimmen

username

Benutzername des Besitzers der neuen Datenbank. Er kann aus bis zu 31 Zeichen bestehen. Der Benutzername kann wahlweise in einfache oder doppelte Anführungszeichen eingeschlossen werden. Wenn ein Benutzername in doppelte Anführungszeichen eingeschlossen ist, muss die Groß-/Kleinschreibung entsprechend den Regeln für Bezeichner in Anführungszeichen beachtet werden. Wenn es in einfache Anführungszeichen eingeschlossen ist, verhält es sich so, als ob der Wert ohne Anführungszeichen angegeben wurde. Der Benutzer muss ein Administrator sein oder die Berechtigung CREATE DATABASE besitzen.

password

Passwort des Benutzers als Datenbankbesitzer. Bei Verwendung des Authentifizierungs-Plugins Legacy_Auth werden nur die ersten 8 Zeichen verwendet. Groß-/Kleinschreibung beachten

rolename

Der Name der Rolle, deren Rechte beim Anlegen einer Datenbank berücksichtigt werden sollen. Der Rollenname kann in einfache oder doppelte Anführungszeichen eingeschlossen werden. Wenn der Rollenname in doppelte Anführungszeichen eingeschlossen ist, muss die Groß-/Kleinschreibung entsprechend den Regeln für Bezeichner in Anführungszeichen beachtet werden. Wenn es in einfache Anführungszeichen eingeschlossen ist, verhält es sich so, als ob der Wert ohne Anführungszeichen angegeben wurde.

size

Seitengröße für die Datenbank in Byte. Mögliche Werte sind 4096, 8192 und 16384. Die Standardseitengröße ist 8192.

num

Maximale Größe der primären Datenbankdatei oder einer sekundären Datei in Seiten

charset

Gibt den Zeichensatz der Verbindung an, die einem Client zur Verfügung steht, nachdem die Datenbank erfolgreich erstellt wurde. Einzelne Anführungszeichen sind erforderlich.

default_charset

Gibt den Standardzeichensatz für Zeichenfolgendatentypen an

collation

Standardsortierung für den Standardzeichensatz

sec_file

Dateispezifikation für eine Sekundärdatei

pagenum

Anfangsseitennummer für eine sekundäre Datenbankdatei

diff_file

Dateipfad und Name für Differenzdateien (.delta-Dateien) für den Sicherungsmodus

Die Anweisung CREATE DATABASE erstellt eine neue Datenbank. Sie können CREATE DATABASE oder CREATE SCHEMA verwenden. Sie sind synonym, aber wir empfehlen immer CREATE DATABASE zu verwenden, da sich dies in einer zukünftigen Version von Firebird ändern kann.

Eine Datenbank kann aus einer oder mehreren Dateien bestehen. Die erste (Haupt-)Datei wird als Primärdatei bezeichnet, nachfolgende Dateien werden als Sekundärdatei(en) bezeichnet.

Mehrdatei-Datenbanken

Heutzutage gelten Multi-File-Datenbanken als Anachronismus. Es war sinnvoll, Multi-File-Datenbanken auf alten Dateisystemen zu verwenden, bei denen die Größe einer Datei begrenzt ist. Sie können beispielsweise auf FAT32 keine Datei erstellen, die größer als 4 GB ist.

Die primäre Dateispezifikation ist der Name der Datenbankdatei und ihre Erweiterung mit dem vollständigen Pfad zu ihr gemäß den Regeln des verwendeten Dateisystems der OS-Plattform. Die Datenbankdatei darf zum Zeitpunkt der Datenbankerstellung noch nicht vorhanden sein. Wenn sie vorhanden ist, erhalten Sie eine Fehlermeldung und die Datenbank wird nicht erstellt.

Wenn kein vollständiger Pfad zur Datenbank angegeben ist, wird die Datenbank in einem der Systemverzeichnisse erstellt. Das jeweilige Verzeichnis hängt vom Betriebssystem ab. Geben Sie daher beim Erstellen einer Datenbank immer entweder den absoluten Pfad oder einen Alias an, es sei denn, Sie haben einen triftigen Grund, diese Situation zu bevorzugen.

Einen Datenbankalias verwenden

Sie können Aliasse anstelle des vollständigen Pfads zur primären Datenbankdatei verwenden. Aliase werden in der Datei databases.conf im folgenden Format definiert:

alias = filepath

Die Ausführung einer CREATE DATABASE-Anweisung erfordert besondere Überlegungen in der Client-Anwendung oder dem Datenbanktreiber. Daher ist es nicht immer möglich, eine CREATE DATABASE-Anweisung auszuführen. Einige Treiber bieten andere Möglichkeiten zum Erstellen von Datenbanken. Jaybird stellt zum Beispiel die Klasse org.firebirdsql.management.FBManager bereit, um programmgesteuert eine Datenbank zu erstellen.

Bei Bedarf können Sie jederzeit auf isql zurückgreifen, um eine Datenbank zu erstellen.

Erstellen einer Datenbank auf einem Remote-Server

Wenn Sie eine Datenbank auf einem Remote-Server erstellen, müssen Sie die Remote-Server-Spezifikation angeben. Die Spezifikation des Remote-Servers hängt vom verwendeten Protokoll ab. Wenn Sie das TCP/IP-Protokoll verwenden, um eine Datenbank zu erstellen, sollte die primäre Dateispezifikation wie folgt aussehen:

host[/{port|service}]:{filepath | db_alias}

Wenn Sie das Named Pipes-Protokoll verwenden, um eine Datenbank auf einem Windows-Server zu erstellen, sollte die primäre Dateispezifikation so aussehen:

\\host\{filepath | db_alias}

Seit Firebird 3.0 gibt es auch eine einheitliche URL-ähnliche Syntax für die Remote-Server-Spezifikation. In dieser Syntax gibt der erste Teil den Namen des Protokolls an, dann einen Hostnamen oder eine IP-Adresse, eine Portnummer und einen Pfad der primären Datenbankdatei oder einen Alias.

Als Protokoll können folgende Werte angegeben werden:

INET

TCP/IP (versucht zuerst eine Verbindung über das IPv6-Protokoll herzustellen, wenn dies fehlschlägt, dann IPv4)

INET4

TCP/IP v4 (seit Firebird 3.0.1)

INET6

TCP/IP v6 (seit Firebird 3.0.1)

WNET

NetBEUI oder Named Pipes Protocol

XNET

lokales Protokoll (enthält keinen Host-, Port- und Servicenamen)

<protocol>://[host[:{port | service}]/]{filepath | db_alias}
Optionale Parameter für CREATE DATABASE
USER und PASSWORD

Klauseln zur Angabe des Benutzernamens bzw. des Passworts eines bestehenden Benutzers in der Sicherheitsdatenbank (security3.fdb oder was auch immer in der SecurityDatabase Konfiguration konfiguriert ist). Sie müssen den Benutzernamen und das Kennwort nicht angeben, wenn die Umgebungsvariablen ISC_USER und ISC_PASSWORD gesetzt sind. Der beim Erstellen der Datenbank angegebene Benutzer ist der Eigentümer. Dies ist wichtig, wenn Sie Datenbank- und Objektberechtigungen berücksichtigen.

ROLE

Die ROLE-Klausel gibt den Namen der Rolle an (normalerweise RDB$ADMIN), die beim Erstellen der Datenbank berücksichtigt wird. Die Rolle muss dem Benutzer in der entsprechenden Sicherheitsdatenbank zugewiesen werden.

PAGE_SIZE

Klausel zum Angeben der Datenbankseitengröße. Diese Größe wird für die Primärdatei und alle Sekundärdateien der Datenbank festgelegt. Wenn Sie eine Datenbankseitengröße von weniger als 4.096 angeben, wird sie automatisch auf 4.096 aufgerundet. Andere Werte, die nicht gleich 4.096, 8.192 oder 16.384 sind, werden auf den nächst kleineren unterstützten Wert geändert. Wenn die Datenbankseitengröße nicht angegeben wird, wird sie auf den Standardwert 8.192 gesetzt.

LENGTH

Klausel, die die maximale Größe der primären oder sekundären Datenbankdatei in Seiten angibt. Wenn eine Datenbank erstellt wird, belegen ihre Primär- und Sekundärdateien die minimale Anzahl von Seiten, die zum Speichern der Systemdaten erforderlich sind, unabhängig von dem in der LENGTH-Klausel angegebenen Wert. Der Wert LENGTH hat keinen Einfluss auf die Größe der einzigen (oder letzten, in einer Datenbank mit mehreren Dateien) Datei. Die Datei wird bei Bedarf automatisch weiter vergrößert.

SET NAMES

Klausel, die den Zeichensatz der verfügbaren Verbindung angibt, nachdem die Datenbank erfolgreich erstellt wurde. Standardmäßig wird der Zeichensatz NONE verwendet. Beachten Sie, dass der Zeichensatz in ein Paar Apostrophe (einfache Anführungszeichen) eingeschlossen werden sollte.

DEFAULT CHARACTER SET

Klausel, die den Standardzeichensatz zum Erstellen von Datenstrukturen von Zeichenfolgendatentypen angibt. Zeichensätze werden für die Datentypen CHAR, VARCHAR und BLOB SUB_TYPE TEXT verwendet. Standardmäßig wird der Zeichensatz NONE verwendet. Es ist auch möglich, die Standard-"COLLATION" für den Standardzeichensatz anzugeben, wodurch diese Kollatierungssequenz zum Standard für den Standardzeichensatz wird. Der Standardwert wird für die gesamte Datenbank verwendet, es sei denn, ein alternativer Zeichensatz mit oder ohne festgelegter Sortierung wird explizit für ein Feld, eine Domäne, eine Variable, einen Umwandlungsausdruck usw. verwendet.

STARTING AT

Klausel, die die Seitennummer der Datenbank angibt, bei der die nächste sekundäre Datenbankdatei beginnen soll. Wenn die vorherige Datei gemäß der angegebenen Seitenzahl vollständig mit Daten gefüllt ist, beginnt das System, der nächsten Datenbankdatei neue Daten hinzuzufügen.

DIFFERENCE FILE

Klausel, die den Pfad und den Namen für das Datei-Delta angibt, das alle Mutationen in der Datenbankdatei speichert, nachdem sie durch die Anweisung ALTER DATABASE BEGIN BACKUP in den "kopiersicheren" Modus geschaltet wurde. Eine detaillierte Beschreibung dieser Klausel finden Sie unter ALTER DATABASE.

Angabe des Datenbankdialekts

Datenbanken werden standardmäßig in Dialekt 3 erstellt. Damit die Datenbank im SQL-Dialekt 1 erstellt wird, müssen Sie die Anweisung SET SQL DIALECT 1 aus dem Skript oder der Client-Anwendung ausführen, z.B. in isql vor der CREATE DATABASE-Anweisung.

Wer kann eine Datenbank erstellen?

Die CREATE DATABASE-Anweisung kann ausgeführt werden durch:

Beispiele für die Verwendung von CREATE DATABASE
  1. Erstellen einer Datenbank in Windows, die sich auf Datenträger D mit einer Seitengröße von 4.096 befindet. Der Eigentümer der Datenbank ist der Benutzer wizard. Die Datenbank ist in Dialekt und verwendet WIN1251 als Standardzeichensatz.

    SET SQL DIALECT 1;
    CREATE DATABASE 'D:\test.fdb'
    USER 'wizard' PASSWORD 'player'
    PAGE_SIZE = 4096 DEFAULT CHARACTER SET WIN1251;
  2. Erstellen einer Datenbank im Linux-Betriebssystem mit einer Seitengröße von 8.192 (Standard). Der Eigentümer der Datenbank ist der Benutzer wizard. Die Datenbank wird in Dialekt 3 sein und verwendet UTF8 als Standardzeichensatz mit UNICODE_CI_AI als Standardsortierung.

    CREATE DATABASE '/home/firebird/test.fdb'
    USER 'wizard' PASSWORD 'player'
    DEFAULT CHARACTER SET UTF8 COLLATION UNICODE_CI_AI;
  3. Erstellen einer Datenbank auf dem entfernten Server “baseserver” mit dem im Alias “test” angegebenen Pfad, der zuvor in der Datei databases.conf definiert wurde. Es wird das TCP/IP-Protokoll verwendet. Der Eigentümer der Datenbank ist der Benutzer wizard. Die Datenbank wird in Dialekt 3 sein und verwendet UTF8 als Standardzeichensatz.

    CREATE DATABASE 'baseserver:test'
    USER 'wizard' PASSWORD 'player'
    DEFAULT CHARACTER SET UTF8;
  4. Erstellen einer Datenbank in Dialekt 3 mit UTF8 als Standardzeichensatz. Die Primärdatei enthält bis zu 10.000 Seiten mit einer Seitengröße von 8.192. Sobald die Primärdatei die maximale Seitenzahl erreicht hat, beginnt Firebird damit, Seiten der Sekundärdatei test.fdb2 zuzuordnen. Wenn auch diese Datei maximal gefüllt ist, wird test.fdb3 der Empfänger aller neuen Seitenzuweisungen. Als letzte Datei hat Firebird keine Seitenbegrenzung. Neue Zuweisungen werden so lange fortgesetzt, wie das Dateisystem dies zulässt oder bis auf dem Speichergerät kein freier Speicherplatz mehr vorhanden ist. Wenn für diese letzte Datei ein LENGTH-Parameter angegeben würde, würde er ignoriert.

    SET SQL DIALECT 3;
    CREATE DATABASE 'baseserver:D:\test.fdb'
    USER 'wizard' PASSWORD 'player'
    PAGE_SIZE = 8192
    DEFAULT CHARACTER SET UTF8
    FILE 'D:\test.fdb2'
    STARTING AT PAGE 10001
    FILE 'D:\test.fdb3'
    STARTING AT PAGE 20001;
  5. Erstellen einer Datenbank in Dialekt 3 mit UTF8 als Standardzeichensatz. Die Primärdatei enthält bis zu 10.000 Seiten mit einer Seitengröße von 8.192. In Bezug auf die Dateigröße und die Verwendung von Sekundärdateien verhält sich diese Datenbank genau wie im vorherigen Beispiel.

    SET SQL DIALECT 3;
    CREATE DATABASE 'baseserver:D:\test.fdb'
    USER 'wizard' PASSWORD 'player'
    PAGE_SIZE = 8192
    LENGTH 10000 PAGES
    DEFAULT CHARACTER SET UTF8
    FILE 'D:\test.fdb2'
    FILE 'D:\test.fdb3'
    STARTING AT PAGE 20001;

5.1.2. ALTER DATABASE

Verwendet für

Ändern der Dateiorganisation einer Datenbank, Umschalten ihres "kopiersicheren" Zustands, Verwalten der Verschlüsselung und anderer datenbankweiter Konfigurationen

Verfügbar in

DSQL, ESQL — eingeschränkter Funktionsumfang

Syntax
ALTER {DATABASE | SCHEMA} <alter_db_option> [<alter_db_option> ...]

<alter_db_option> :==
    <add_sec_clause>
  | {ADD DIFFERENCE FILE 'diff_file' | DROP DIFFERENCE FILE}
  | {BEGIN | END} BACKUP
  | SET DEFAULT CHARACTER SET charset
  | SET LINGER TO linger_duration
  | DROP LINGER
  | {ENCRYPT WITH plugin_name [KEY key_name] | DECRYPT}

<add_sec_clause> ::= ADD <sec_file> [<sec_file> ...]

<sec_file> ::=
  FILE 'filepath'
  [STARTING [AT [PAGE]] pagenum]
  [LENGTH [=] num [PAGE[S]]

Mehrere Dateien können in einer ADD-Klausel hinzugefügt werden:

ALTER DATABASE
  ADD FILE x LENGTH 8000
    FILE y LENGTH 8000
    FILE z

Mehrfaches Vorkommen von add_sec_clause (ADD FILE-Klauseln) ist erlaubt; eine ADD FILE-Klausel, die mehrere Dateien hinzufügt (wie im obigen Beispiel), kann mit anderen gemischt werden, die nur eine Datei hinzufügen. Die Anweisung wurde in der alten InterBase 6 Language Reference falsch dokumentiert.

Tabelle 21. ALTER DATABASE-Anweisungsparameter
Parameter Beschreibung

add_sec_clause

Hinzufügen einer sekundären Datenbankdatei

sec_file

Dateispezifikation für Sekundärdatei

filepath

Vollständiger Pfad und Dateiname der Deltadatei oder sekundären Datenbankdatei

pagenum

Seitennummer, ab der die sekundäre Datenbankdatei beginnen soll

num

Maximale Größe der Sekundärdatei in Seiten

diff_file

Dateipfad und Name der .delta-Datei (Differenzdatei)

charset

Neuer Standardzeichensatz der Datenbank

linger_duration

Dauer der linger Verzögerung in Sekunden; muss größer oder gleich 0 (null) sein

plugin_name

Der Name des Verschlüsselungs-Plugins

key_name

Der Name des Verschlüsselungsschlüssels

Die ALTER DATABASE-Anweisung kann:

  • Sekundärdateien zu einer Datenbank hinzufügen

  • Umschalten einer Einzeldatei-Datenbank in den “copy-safe”-Modus (nur DSQL)

  • Pfad und Name der Delta-Datei für physische Backups setzen oder aufheben (nur DSQL)

SCHEMA ist derzeit ein Synonym für DATABASE; dies kann sich in einer zukünftigen Version ändern, daher empfehlen wir immer DATABASE zu verwenden

Wer kann die Datenbank ändern?

Die ALTER DATABASE-Anweisung kann ausgeführt werden durch:

Paramter für ALTER DATABASE
ADD (FILE)

Fügt der Datenbank sekundäre Dateien hinzu. Es ist notwendig, den vollständigen Pfad zur Datei und den Namen der Sekundärdatei anzugeben. Die Beschreibung für die Sekundärdatei ähnelt der für die Anweisung CREATE DATABASE.

ADD DIFFERENCE FILE

Gibt den Pfad und den Namen der Delta-Datei an, die alle Mutationen in der Datenbank speichert, wenn sie in den "kopiersicheren" Modus geschaltet wird. Diese Klausel fügt tatsächlich keine Datei hinzu. Es überschreibt nur den Standardnamen und -pfad der .delta-Datei. Um die bestehenden Einstellungen zu ändern, sollten Sie die zuvor angegebene Beschreibung der .delta-Datei mit der DROP DIFFERENCE FILE-Klausel löschen, bevor Sie die neue Beschreibung der Delta-Datei angeben. Wenn Pfad und Name der .delta-Datei nicht überschrieben werden, hat die Datei denselben Pfad und Namen wie die Datenbank, jedoch mit der Dateierweiterung .delta.

Wird nur ein Dateiname angegeben, wird die .delta-Datei im aktuellen Verzeichnis des Servers erstellt. Unter Windows ist dies das Systemverzeichnis – ein sehr unkluger Ort, um flüchtige Benutzerdateien zu speichern und im Gegensatz zu den Windows-Dateisystemregeln.

DROP DIFFERENCE FILE

Löscht die Beschreibung (Pfad und Name) der .delta-Datei, die zuvor in der Klausel ADD DIFFERENCE FILE angegeben wurde. Die Datei wird nicht wirklich gelöscht. DROP DIFFERENCE FILE löscht den Pfad und den Namen der .delta-Datei aus dem Datenbank-Header. Wenn die Datenbank das nächste Mal in den “copy-safe”-Modus geschaltet wird, werden die Standardwerte verwendet (d. h. der gleiche Pfad und Name wie die der Datenbank, aber mit der Erweiterung .delta).

BEGIN BACKUP

Schaltet die Datenbank in den “kopiersicher” Modus. ALTER DATABASE mit dieser Klausel friert die Hauptdatenbankdatei ein, sodass sie mit Dateisystemtools sicher gesichert werden kann, selbst wenn Benutzer verbunden sind und Operationen mit Daten ausführen. Bis der Sicherungsstatus der Datenbank auf NORMAL zurückgesetzt wird, werden alle an der Datenbank vorgenommenen Änderungen in die .delta (Differenz)-Datei geschrieben.

Trotz ihrer Syntax startet eine Anweisung mit der BEGIN BACKUP-Klausel keinen Backup-Prozess, sondern schafft lediglich die Bedingungen für die Ausführung einer Aufgabe, die erfordert, dass die Datenbankdatei temporär schreibgeschützt ist.

END BACKUP

Schaltet die Datenbank vom “kopiersicheren” Modus in den normalen Modus um. Eine Anweisung mit dieser Klausel führt die .delta-Datei mit der Hauptdatenbankdatei zusammen und stellt den normalen Betrieb der Datenbank wieder her. Nach dem Start des Prozesses END BACKUP sind die Voraussetzungen für die Erstellung sicherer Backups mittels Dateisystemtools nicht mehr gegeben.

Die Verwendung von BEGIN BACKUP und END BACKUP und das Kopieren der Datenbankdateien mit Dateisystemtools ist bei Mehrdateidatenbanken nicht sicher! Verwenden Sie diese Methode nur für Datenbanken mit einer einzigen Datei.

Eine sichere Sicherung mit dem Dienstprogramm gbak ist jederzeit möglich, es wird jedoch nicht empfohlen, gbak auszuführen, während sich die Datenbank im Status LOCKED oder MERGE befindet.

SET DEFAULT CHARACTER SET

Ändert den Standardzeichensatz der Datenbank. Diese Änderung wirkt sich nicht auf vorhandene Daten oder Spalten aus. Der neue Standardzeichensatz wird nur in nachfolgenden DDL-Befehlen verwendet.

SET LINGER TO

Setzt die linger-Verzögerung. Die linger-Verzögerung gilt nur für Firebird SuperServer und gibt an, wie viele Sekunden der Server eine Datenbankdatei (und ihre Caches) geöffnet hält, nachdem die letzte Verbindung zu dieser Datenbank geschlossen wurde. Dies kann dazu beitragen, die Leistung kostengünstig zu verbessern, wenn die Datenbank häufig geöffnet und geschlossen wird, indem Ressourcen für die nächste Verbindung "warm" gehalten werden.

Dieser Modus kann für Webanwendungen - ohne Verbindungspool - nützlich sein, bei denen die Verbindung zur Datenbank normalerweise nur für sehr kurze Zeit "lebt".

Die Klauseln SET LINGER TO und DROP LINGER können in einer einzigen Anweisung kombiniert werden, aber die letzte Klausel "gewinnt". Zum Beispiel setzt ALTER DATABASE SET LINGER TO 5 DROP LINGER die linger-Verzögerung auf 0 (kein linger), während ALTER DATABASE DROP LINGER SET LINGER to 5 die linger-Verzögerung auf 5 Sekunden setzt.

DROP LINGER

Löscht die linger-Verzögerung (setzt sie auf Null). Die Verwendung von DROP LINGER entspricht der Verwendung von SET LINGER TO 0.

Das Löschen von 'LINGER' ist keine ideale Lösung für die gelegentliche Notwendigkeit, es für einen einmaligen Zustand auszuschalten, in dem der Server erzwungenes Herunterfahren benötigt. Das Dienstprogramm gfix hat jetzt den Schalter -NoLinger, der die angegebene Datenbank sofort schließt, nachdem der letzte Anhang verschwunden ist, unabhängig von der LINGER-Einstellung in der Datenbank. Die Einstellung 'LINGER' wird beibehalten und funktioniert beim nächsten Mal normal.

Dieselbe einmalige Überschreibung ist auch über die Dienste-API unter Verwendung des Tags isc_spb_prp_nolinger verfügbar, z. (in einer Zeile):

fbsvcmgr host:service_mgr user sysdba password xxx
       action_properties dbname employee prp_nolinger

Die Klauseln DROP LINGER und SET LINGER TO können in einer einzigen Anweisung kombiniert werden, aber die letzte Klausel "gewinnt".

ENCRYPT WITH

Vgl. Eine Datenbank verschlüsseln im Abschnitt Sicherheit.

DECRYPT

Vgl. Eine Datenbank entschlüsseln im Abschnitt Sicherheit.

Beispiele zur Verwendung von ALTER DATABASE
  1. Hinzufügen einer sekundären Datei zur Datenbank. Sobald 30000 Seiten in der vorherigen Primär- oder Sekundärdatei gefüllt sind, beginnt die Firebird-Engine, Daten in die Sekundärdatei test4.fdb hinzuzufügen.

    ALTER DATABASE
      ADD FILE 'D:\test4.fdb'
        STARTING AT PAGE 30001;
  2. Angabe von Pfad und Name der Delta-Datei:

    ALTER DATABASE
      ADD DIFFERENCE FILE 'D:\test.diff';
  3. Löschen der Beschreibung der Delta-Datei:

    ALTER DATABASE
      DROP DIFFERENCE FILE;
  4. Umschalten der Datenbank in den “kopiersicher” Modus:

    ALTER DATABASE
      BEGIN BACKUP;
  5. Zurückschalten der Datenbank vom “copy-safe”-Modus in den normalen Betriebsmodus:

    ALTER DATABASE
      END BACKUP;
  6. Ändern des Standardzeichensatzes für eine Datenbank in WIN1251

    ALTER DATABASE
      SET DEFAULT CHARACTER SET WIN1252;
  7. Einstellen einer linger-verzögerung von 30 Sekunden

    ALTER DATABASE
      SET LINGER TO 30;
  8. Verschlüsseln der Datenbank mit einem Plugin namens DbCrypt

    ALTER DATABASE
      ENCRYPT WITH DbCrypt;
  9. Entschlüsseln der Datenbank

    ALTER DATABASE
      DECRYPT;

5.1.3. DROP DATABASE

Verwendet für

Löschen der Datenbank, mit der Sie gerade verbunden sind

Verfügbar in

DSQL, ESQL

Syntax
DROP DATABASE

Die Anweisung DROP DATABASE löscht die aktuelle Datenbank. Bevor Sie eine Datenbank löschen, müssen Sie sich mit ihr verbinden. Die Anweisung löscht die Primärdatei, alle Sekundärdateien und alle Shadow-Dateien.

Im Gegensatz zu CREATE DATABASE und ALTER DATABASE ist DROP SCHEMA kein gültiger Alias für DROP DATABASE. Dies ist beabsichtigt.

Wer kann eine Datenbank löschen?

Die DROP DATABASE-Anweisung kann ausgeführt werden durch:

Beispiel zur Verwendung von DROP DATABASE
Löschen der aktuellen Datenbank
DROP DATABASE;

5.2. SHADOW

Ein shadow ist eine exakte, seitenweise Kopie einer Datenbank. Sobald ein Schatten erstellt wurde, werden alle in der Datenbank vorgenommenen Änderungen sofort im Schatten widergespiegelt. Wenn die primäre Datenbankdatei aus irgendeinem Grund nicht verfügbar ist, wechselt das DBMS zum Shadow.

In diesem Abschnitt wird beschrieben, wie Sie Schattendateien erstellen und löschen.

5.2.1. CREATE SHADOW

Verwendet für

Schatten für die aktuelle Datenbank erstellen

Verfügbar in

DSQL, ESQL

Syntax
CREATE SHADOW <sh_num> [{AUTO | MANUAL}] [CONDITIONAL]
  'filepath' [LENGTH [=] num [PAGE[S]]]
  [<secondary_file> ...]

<secondary_file> ::=
  FILE 'filepath'
  [STARTING [AT [PAGE]] pagenum]
  [LENGTH [=] num [PAGE[S]]]
Tabelle 22. CREATE SHADOW-Anweisungsparameter
Parameter Beschreibung

sh_num

Schattennummer – eine positive Zahl, die den Schattensatz identifiziert

filepath

Der Name der Schattendatei und der Pfad zu ihr gemäß den Regeln des Betriebssystems

num

Maximale Schattengröße in Seiten

secondary_file

Sekundärdateispezifikation

page_num

Die Nummer der Seite, bei der die sekundäre Schattendatei beginnen soll

Die Anweisung CREATE SHADOW erstellt einen neuen Schatten. Der Shadow beginnt sofort mit dem Duplizieren der Datenbank. Es ist einem Benutzer nicht möglich, sich mit einem Shadow zu verbinden.

Wie eine Datenbank kann ein Schatten aus mehreren Dateien bestehen. Die Anzahl und Größe der Dateien eines Shadows hängt nicht von der Anzahl und Größe der Dateien der Datenbank ab, die er beschattet.

Die Seitengröße für Schattendateien ist gleich der Seitengröße der Datenbank und kann nicht geändert werden.

Wenn eine Katastrophe mit der Originaldatenbank auftritt, konvertiert das System den Schatten in eine Kopie der Datenbank und wechselt zu dieser. Der Schatten ist dann unverfügbar. Was als nächstes passiert, hängt von der Option MODE ab.

AUTO | MANUAL-Modus

Wenn ein Schatten in eine Datenbank konvertiert wird, ist er nicht verfügbar. Alternativ kann ein Schatten nicht mehr verfügbar sein, weil jemand seine Datei versehentlich löscht oder der Speicherplatz, auf dem die Schattendateien gespeichert sind, erschöpft oder selbst beschädigt ist.

  • Wenn der AUTO-Modus ausgewählt ist (Standardwert), wird das Spiegeln automatisch beendet, alle Verweise darauf werden aus dem Datenbankheader gelöscht und die Datenbank funktioniert normal weiter.

    Wenn die Option CONDITIONAL gesetzt wurde, versucht das System, einen neuen Schatten zu erstellen, um den verlorenen zu ersetzen. Es gelingt jedoch nicht immer, und möglicherweise muss manuell ein neues erstellt werden.

  • Wenn das MANUAL-Modusattribut gesetzt ist, wenn der Shadow nicht verfügbar ist, werden alle Versuche, eine Verbindung zur Datenbank herzustellen und sie abzufragen, Fehlermeldungen erzeugen. Auf die Datenbank kann nicht zugegriffen werden, bis entweder der Shadow wieder verfügbar ist oder der Datenbankadministrator ihn mit der DROP SHADOW-Anweisung löscht. MANUAL sollte gewählt werden, wenn kontinuierliches Shadowing wichtiger ist als ein unterbrechungsfreier Betrieb der Datenbank.

Optionen für CREATE SHADOW
LENGTH

Gibt die maximale Größe der primären oder sekundären Schattendatei in Seiten an. Der Wert LENGTH hat keinen Einfluss auf die Größe der einzigen Schattendatei oder der letzten, wenn es sich um eine Menge handelt. Die letzte (oder einzige) Datei wächst automatisch weiter, solange es notwendig ist.

STARTING AT

Gibt die Schattenseitennummer an, bei der die nächste Schattendatei beginnen soll. Das System beginnt mit dem Hinzufügen neuer Daten zur nächsten Schattendatei, wenn die vorherige Datei bis zur angegebenen Seitenzahl mit Daten gefüllt ist.

Sie können die Größe, die Namen und den Speicherort der Schattendateien überprüfen, indem Sie mit isql eine Verbindung zur Datenbank herstellen und den Befehl SHOW DATABASE; . ausführen

Wer kann einen Schatten erstellen

Die CREATE SHADOW-Anweisung kann ausgeführt werden durch:

Beispiele für die Verwendung von CREATE SHADOW
  1. Erstellen eines Schattens für die aktuelle Datenbank als “Schattennummer 1”:

    CREATE SHADOW 1 'g:\data\test.shd';
  2. Erstellen eines Schattens mit mehreren Dateien für die aktuelle Datenbank als “Schattennummer 2”:

    CREATE SHADOW 2 'g:\data\test.sh1'
      LENGTH 8000 PAGES
      FILE 'g:\data\test.sh2';

5.2.2. DROP SHADOW

Verwendet für

Löschen eines Schattens aus der aktuellen Datenbank

Verfügbar in

DSQL, ESQL

Syntax
DROP SHADOW sh_num
  [{DELETE | PRESERVE} FILE]
Tabelle 23. DROP SHADOW-Anweisungsparameter
Parameter Beschreibung

sh_num

Schattennummer – eine positive Zahl, die den Schattensatz identifiziert

Die DROP SHADOW-Anweisung löscht den angegebenen Schatten für die aktuelle Datenbank. Wenn ein Shadow gelöscht wird, werden alle damit verbundenen Dateien gelöscht und das Shadowing auf die angegebene sh_num wird beendet. Die optionale DELETE FILE-Klausel macht dieses Verhalten explizit. Im Gegensatz dazu entfernt die PRESERVE FILE-Klausel den Schatten aus der Datenbank, aber die Datei selbst wird nicht gelöscht.

Wer kann einen Schatten löschen

Die DROP SHADOW-Anweisung kann ausgeführt werden durch:

Beispiel für DROP SHADOW
Löschen von “Schatten mit der Nummer 1”.
DROP SHADOW 1;
Siehe auch

CREATE SHADOW

5.3. DOMAIN

DOMAIN ist einer der Objekttypen in einer relationalen Datenbank. Eine Domain wird als ein bestimmter Datentyp mit einigen daran angehängten Attributen erstellt. Nachdem es in der Datenbank definiert wurde, kann es wiederholt verwendet werden, um Tabellenspalten, PSQL-Argumente und lokale PSQL-Variablen zu definieren. Diese Objekte erben alle Attribute der Domain. Einige Attribute können bei Bedarf überschrieben werden, wenn das neue Objekt definiert wird.

In diesem Abschnitt wird die Syntax von Anweisungen zum Erstellen, Ändern und Löschen von Domainn beschrieben. Eine detaillierte Beschreibung der Domains und ihrer Verwendung finden Sie in Benutzerdefinierte Datentypen — Domains.

5.3.1. CREATE DOMAIN

Verwendet für

Erstellen einer neuen Domain

Verfügbar in

DSQL, ESQL

Syntax
CREATE DOMAIN name [AS] <datatype>
  [DEFAULT {<literal> | NULL | <context_var>}]
  [NOT NULL] [CHECK (<dom_condition>)]
  [COLLATE collation_name]

<datatype> ::=
  <scalar_datatype> | <blob_datatype> | <array_datatype>

<scalar_datatype> ::=
  !! Siehe auch Skalardatentyp-Syntax !!

<blob_datatype> ::=
  !! Siehe auch BLOB-Datentyp-Syntax !!

<array_datatype> ::=
  !! Siehe auch Array-Datentyp-Syntax !!

<dom_condition> ::=
    <val> <operator> <val>
  | <val> [NOT] BETWEEN <val> AND <val>
  | <val> [NOT] IN ({<val> [, <val> ...] | <select_list>})
  | <val> IS [NOT] NULL
  | <val> IS [NOT] DISTINCT FROM <val>
  | <val> [NOT] CONTAINING <val>
  | <val> [NOT] STARTING [WITH] <val>
  | <val> [NOT] LIKE <val> [ESCAPE <val>]
  | <val> [NOT] SIMILAR TO <val> [ESCAPE <val>]
  | <val> <operator> {ALL | SOME | ANY} (<select_list>)
  | [NOT] EXISTS (<select_expr>)
  | [NOT] SINGULAR (<select_expr>)
  | (<dom_condition>)
  | NOT <dom_condition>
  | <dom_condition> OR <dom_condition>
  | <dom_condition> AND <dom_condition>

<operator> ::=
    <> | != | ^= | ~= | = | < | > | <= | >=
  | !< | ^< | ~< | !> | ^> | ~>

<val> ::=
    VALUE
  | <literal>
  | <context_var>
  | <expression>
  | NULL
  | NEXT VALUE FOR genname
  | GEN_ID(genname, <val>)
  | CAST(<val> AS <cast_type>)
  | (<select_one>)
  | func([<val> [, <val> ...]])

<cast_type> ::= <domain_or_non_array_type> | <array_datatype>

<domain_or_non_array_type> ::=
  !! See Syntax für Skalardatentypen !!
Tabelle 24. CREATE DOMAIN-Anweisungsparameter
Parameter Beschreibung

name

Domainname bestehend aus bis zu 31 Zeichen

datatype

SQL-Datentyp

literal

Ein Literalwert, der mit datatype kompatibel ist

context_var

Jede Kontextvariable, deren Typ mit datatype kompatibel ist

dom_condition

Domain-Bedingung

collation_name

Name einer Kollatierungssequenz, die für charset_name gültig ist, wenn sie mit datatype versorgt wird oder ansonsten für den Standardzeichensatz der Datenbank gültig ist

select_one

Eine skalare SELECT-Anweisung – Auswahl einer Spalte und Rückgabe nur einer Zeile

select_list

Eine SELECT-Anweisung, die eine Spalte auswählt und null oder mehr Zeilen zurückgibt

select_expr

Eine SELECT-Anweisung, die eine oder mehrere Spalten auswählt und null oder mehr Zeilen zurückgibt

expression

Ein Ausdruck, der in einen Wert aufgelöst wird, der mit datatype kompatibel ist

genname

Sequenzname (Generatorname)

func

Interne Funktion oder UDF

Die CREATE DOMAIN-Anweisung erstellt eine neue Domain.

Als Domaintyp kann jeder SQL-Datentyp angegeben werden.

Typspezifische Details
Array-Typen
  • Wenn die Domain ein Array sein soll, kann der Basistyp ein beliebiger SQL-Datentyp außer BLOB und Array sein.

  • Die Dimensionen des Arrays sind in eckigen Klammern angegeben. (Im Syntaxblock erscheinen diese Klammern in Anführungszeichen, um sie von den eckigen Klammern zu unterscheiden, die optionale Syntaxelemente kennzeichnen.)

  • Für jede Array-Dimension definieren eine oder zwei ganze Zahlen die untere und obere Grenze ihres Indexbereichs:

    • Arrays sind standardmäßig 1-basiert. Die untere Grenze ist implizit und nur die obere Grenze muss angegeben werden. Eine einzelne Zahl kleiner als 1 definiert den Bereich num..1 und eine Zahl größer als 1 definiert den Bereich 1..num.

    • Zwei durch einen Doppelpunkt getrennte Zahlen (‘:’) und optionales Leerzeichen, das zweite größer als das erste, können verwendet werden, um den Bereich explizit zu definieren. Eine oder beide Grenzen können kleiner als Null sein, solange die obere Grenze größer als die untere ist.

  • Wenn das Array mehrere Dimensionen hat, müssen die Bereichsdefinitionen für jede Dimension durch Kommas und optionales Leerzeichen getrennt werden.

  • Indizes werden nur validiert, wenn tatsächlich ein Array existiert. Das bedeutet, dass keine Fehlermeldungen bezüglich ungültiger Indizes zurückgegeben werden, wenn die Auswahl eines bestimmten Elements nichts zurückgibt oder wenn ein Array-Feld NULL ist.

String-Typen

Mit der CHARACTER SET-Klausel können Sie den Zeichensatz für die Typen CHAR, VARCHAR und BLOB (SUB_TYPE TEXT) angeben. Wenn der Zeichensatz nicht angegeben ist, wird der als DEFAULT CHARACTER SET angegebene Zeichensatz der Datenbank verwendet. Wenn kein Zeichensatz angegeben wurde, wird beim Erstellen einer Zeichendomäne standardmäßig der Zeichensatz NONE verwendet.

Mit dem Zeichensatz NONE werden Zeichendaten so gespeichert und abgerufen, wie sie übermittelt wurden. Daten in einer beliebigen Codierung können einer Spalte basierend auf einer solchen Domain hinzugefügt werden, aber es ist unmöglich, diese Daten zu einer Spalte mit einer anderen Codierung hinzuzufügen. Da keine Transliteration zwischen den Quell- und Zielcodierungen durchgeführt wird, können Fehler auftreten.

DEFAULT-Klausel

Mit der optionalen DEFAULT-Klausel können Sie einen Standardwert für die Domain angeben. Dieser Wert wird der Tabellenspalte hinzugefügt, die diese Domain erbt, wenn die INSERT-Anweisung ausgeführt wird, wenn kein Wert dafür in der DML-Anweisung angegeben ist. Lokale Variablen und Argumente in PSQL-Modulen, die auf diese Domain verweisen, werden mit dem Standardwert initialisiert. Verwenden Sie als Standardwert ein Literal eines kompatiblen Typs oder eine Kontextvariable eines kompatiblen Typs.

NOT NULL Constraint

Spalten und Variablen, die auf einer Domain mit der Einschränkung NOT NULL basieren, werden daran gehindert, als NULL geschrieben zu werden, d.h. ein Wert ist erforderlich.

Achten Sie beim Anlegen einer Domain darauf, keine Einschränkungen anzugeben, die sich widersprechen würden. Zum Beispiel sind NOT NULL und DEFAULT NULL widersprüchlich.

CHECK Constraint(s)

Die optionale CHECK-Klausel gibt Einschränkungen für die Domain an. Eine Domainneinschränkung gibt Bedingungen an, die von den Werten von Tabellenspalten oder Variablen erfüllt werden müssen, die von der Domain erben. Eine Bedingung muss in Klammern eingeschlossen werden. Eine Bedingung ist ein logischer Ausdruck (auch Prädikat genannt), der die booleschen Ergebnisse TRUE, FALSE und UNKNOWN zurückgeben kann. Eine Bedingung gilt als erfüllt, wenn das Prädikat den Wert TRUE oder „unknown value“ (entspricht NULL) zurückgibt. Liefert das Prädikat FALSE, ist die Annahmebedingung nicht erfüllt.

VALUE Keyword

Das Schlüsselwort VALUE in einer Domainneinschränkung ersetzt die Tabellenspalte, die auf dieser Domain basiert, oder eine Variable in einem PSQL-Modul. Es enthält den Wert, der der Variablen oder der Tabellenspalte zugewiesen wurde. VALUE kann überall in der CHECK-Bedingung verwendet werden, obwohl es normalerweise im linken Teil der Bedingung verwendet wird.

COLLATE

Mit der optionalen COLLATE-Klausel können Sie die Kollatierungssequenz angeben, wenn die Domain auf einem der String-Datentypen basiert, einschließlich BLOBs mit Textuntertypen. Wenn keine Kollatierungssequenz angegeben ist, ist die Kollatierungssequenz diejenige, die zum Zeitpunkt der Erstellung der Domain für den angegebenen Zeichensatz voreingestellt ist.

Wer kann eine Domain erstellen

Die CREATE DOMAIN-Anweisung kann ausgeführt werden durch:

CREATE DOMAIN-Beispiele
  1. Erstellen einer Domain, die Werte über 1.000 annehmen kann, mit einem Standardwert von 10.000.

    CREATE DOMAIN CUSTNO AS
      INTEGER DEFAULT 10000
      CHECK (VALUE > 1000);
  2. Erstellen einer Domain, die die Werte 'Yes' und 'No' in dem beim Erstellen der Datenbank angegebenen Standardzeichensatz annehmen kann.

    CREATE DOMAIN D_BOOLEAN AS
      CHAR(3) CHECK (VALUE IN ('Yes', 'No'));
  3. Erstellen einer Domain mit dem Zeichensatz UTF8 und der Kollatierungssequenz UNICODE_CI_AI.

    CREATE DOMAIN FIRSTNAME AS
      VARCHAR(30) CHARACTER SET UTF8
      COLLATE UNICODE_CI_AI;
  4. Erstellen einer Domain vom Typ DATE, die NULL nicht akzeptiert und das aktuelle Datum als Standardwert verwendet.

    CREATE DOMAIN D_DATE AS
      DATE DEFAULT CURRENT_DATE
      NOT NULL;
  5. Erstellen einer Domain, die als Array aus 2 Elementen des Typs NUMERIC(18, 3) definiert ist. Der Array-Startindex ist 1.

    CREATE DOMAIN D_POINT AS
      NUMERIC(18, 3) [2];

    Über einen Array-Typ definierte Domainn können nur zum Definieren von Tabellenspalten verwendet werden. Sie können keine Arraydomänen verwenden, um lokale Variablen in PSQL-Modulen zu definieren.

  6. Erstellen einer Domain, deren Elemente nur Ländercodes sein können, die in der Tabelle COUNTRY definiert sind.

    CREATE DOMAIN D_COUNTRYCODE AS CHAR(3)
      CHECK (EXISTS(SELECT * FROM COUNTRY
             WHERE COUNTRYCODE = VALUE));

    Das Beispiel wird nur gegeben, um die Möglichkeit zu zeigen, Prädikate mit Abfragen in der Domainntestbedingung zu verwenden. Es wird nicht empfohlen, diesen Domainnstil in der Praxis zu erstellen, es sei denn, die Nachschlagetabelle enthält Daten, die niemals gelöscht werden.

5.3.2. ALTER DOMAIN

Verwendet für

Die aktuellen Attribute einer Domain ändern oder umbenennen

Verfügbar in

DSQL, ESQL

Syntax
ALTER DOMAIN domain_name
  [TO new_name]
  [TYPE <datatype>]
  [{SET DEFAULT {<literal> | NULL | <context_var>} | DROP DEFAULT}]
  [{SET | DROP} NOT NULL]
  [{ADD [CONSTRAINT] CHECK (<dom_condition>) | DROP CONSTRAINT}]

<datatype> ::=
   <scalar_datatype> | <blob_datatype>

<scalar_datatype> ::=
  !! Vgl. Syntax für Skalardatentypen !!

<blob_datatype> ::=
  !! Vgl. Syntax für BLOB-Datentypen !!

!! Siehe auch CREATE DOMAIN-Syntax !!
Tabelle 25. ALTER DOMAIN-Anweisungsparameter
Parameter Beschreibung

new_name

Neuer Name für Domain, bestehend aus bis zu 31 Zeichen

literal

Ein Literalwert, der mit datatype kompatibel ist

context_var

Jede Kontextvariable, deren Typ mit datatype kompatibel ist

Die ALTER DOMAIN-Anweisung ermöglicht Änderungen an den aktuellen Attributen einer Domain, einschließlich ihres Namens. Sie können beliebig viele Domain-Änderungen in einer ALTER DOMAIN-Anweisung vornehmen.

ALTER DOMAIN-Klausel
TO name

Verwenden Sie die TO-Klausel, um die Domain umzubenennen, solange keine Abhängigkeiten von der Domain bestehen, d.h. Tabellenspalten, lokale Variablen oder Prozedurargumente, die darauf verweisen.

SET DEFAULT

Mit der SET DEFAULT-Klausel können Sie einen neuen Standardwert setzen. Wenn die Domain bereits einen Standardwert hat, muss dieser nicht zuerst gelöscht werden – er wird durch den neuen ersetzt.

DROP DEFAULT

Verwenden Sie diese Klausel, um einen zuvor angegebenen Standardwert zu löschen und durch NULL zu ersetzen.

SET NOT NULL

Verwenden Sie diese Klasse, um der Domain eine NOT NULL-Einschränkung hinzuzufügen; Spalten oder Parameter dieser Domain werden daran gehindert, als NULL geschrieben zu werden, d.h. ein Wert ist erforderlich.

Das Hinzufügen einer NOT NULL-Einschränkung zu einer vorhandenen Domain unterzieht alle Spalten, die diese Domain verwenden, einer vollständigen Datenvalidierung. Stellen Sie daher sicher, dass die Spalten keine Nullen enthalten, bevor Sie die Änderung vornehmen.

DROP NOT NULL

Löschen Sie die Einschränkung NOT NULL aus der Domain.

Eine explizite NOT NULL-Einschränkung für eine Spalte, die von einer Domain abhängt, hat Vorrang vor der Domain. In dieser Situation wird die Änderung der Domain, um sie auf NULL zu setzen, nicht an die Spalte weitergegeben.

ADD CONSTRAINT CHECK

Verwenden Sie die ADD CONSTRAINT CHECK-Klausel, um der Domain eine CHECK-Beschränkung hinzuzufügen. Wenn die Domain bereits eine CHECK-Beschränkung hat, muss sie zuerst mit einer ALTER DOMAIN-Anweisung gelöscht werden, die eine DROP CONSTRAINT-Klausel enthält.

TYPE

Die TYPE-Klausel wird verwendet, um den Datentyp der Domain in einen anderen, kompatiblen zu ändern. Das System verbietet jede Änderung des Typs, die zu Datenverlust führen könnte. Ein Beispiel wäre, wenn die Anzahl der Zeichen im neuen Typ kleiner wäre als im bestehenden.

Wenn Sie die Attribute einer Domain ändern, kann vorhandener PSQL-Code ungültig werden. Informationen zur Erkennung finden Sie im Artikel Das RDB$VALID_BLR-Feld in Anhang A.

Was kann ALTER DOMAIN nicht ändern
  • Wenn die Domain als Array deklariert wurde, ist es nicht möglich, ihren Typ oder ihre Dimensionen zu ändern; auch kann kein anderer Typ in einen Array-Typ geändert werden.

  • Es gibt keine Möglichkeit, die Standardsortierung zu ändern, ohne die Domain zu löschen und mit den gewünschten Attributen neu zu erstellen.

Wer kann eine Domain ändern

Die ALTER DOMAIN-Anweisung kann ausgeführt werden durch:

  • Administratoren

  • Der Inhaber der Domain

  • Benutzer mit der Berechtigung ALTER ANY DOMAIN

Domainnänderungen können durch Abhängigkeiten von Objekten verhindert werden, für die der Benutzer nicht über ausreichende Berechtigungen verfügt.

ALTER DOMAIN-Beispiele
  1. Ändern des Datentyps auf INTEGER und Einstellen oder Ändern des Standardwerts auf 2.000:

    ALTER DOMAIN CUSTNO
      TYPE INTEGER
      SET DEFAULT 2000;
  2. Domainnamen ändern

    ALTER DOMAIN D_BOOLEAN TO D_BOOL;
  3. Löschen des Standardwerts und Hinzufügen einer Einschränkung für die Domain:

    ALTER DOMAIN D_DATE
      DROP DEFAULT
      ADD CONSTRAINT CHECK (VALUE >= date '01.01.2000');
  4. Ändern der CHECK-Beschränkung:

    ALTER DOMAIN D_DATE
      DROP CONSTRAINT;
    
    ALTER DOMAIN D_DATE
      ADD CONSTRAINT CHECK
        (VALUE BETWEEN date '01.01.1900' AND date '31.12.2100');
  5. Ändern des Datentyps, um die zulässige Zeichenanzahl zu erhöhen:

    ALTER DOMAIN FIRSTNAME
      TYPE VARCHAR(50) CHARACTER SET UTF8;
  6. Hinzufügen einer NOT NULL-Einschränkung:

    ALTER DOMAIN FIRSTNAME
      SET NOT NULL;
  7. Entfernen einer NOT NULL-Einschränkung:

    ALTER DOMAIN FIRSTNAME
      DROP NOT NULL;

5.3.3. DROP DOMAIN

Verwendet für

Löschen einer bestehenden Domain

Verfügbar in

DSQL, ESQL

Syntax
DROP DOMAIN domain_name

Die DROP DOMAIN-Anweisung löscht eine in der Datenbank vorhandene Domain. Es ist nicht möglich, eine Domain zu löschen, wenn sie von Datenbanktabellenspalten referenziert oder in einem PSQL-Modul verwendet wird. Um eine verwendete Domain zu löschen, müssen alle Spalten in allen Tabellen, die auf die Domain verweisen, gelöscht und alle Verweise auf die Domain aus den PSQL-Modulen entfernt werden.

Wer kann eine Domain löschen

Die DROP DOMAIN-Anweisung kann ausgeführt werden durch:

  • Administratoren

  • Der Inhaber der Domain

  • Benutzer mit dem DROP ANY DOMAIN-Privileg

Example of DROP DOMAIN
Löschen der COUNTRYNAME-Domain
DROP DOMAIN COUNTRYNAME;

5.4. TABLE

Als relationales DBMS speichert Firebird Daten in Tabellen. Eine Tabelle ist eine flache, zweidimensionale Struktur, die eine beliebige Anzahl von Zeilen enthält. Tabellenzeilen werden oft als records bezeichnet.

Alle Zeilen einer Tabelle haben die gleiche Struktur und bestehen aus Spalten. Tabellenspalten werden oft als fields bezeichnet. Eine Tabelle muss mindestens eine Spalte haben. Jede Spalte enthält einen einzelnen Typ von SQL-Daten.

In diesem Abschnitt wird beschrieben, wie Sie Tabellen in einer Datenbank erstellen, ändern und löschen.

5.4.1. CREATE TABLE

Verwendet für

Erstellen einer neuen Tabelle (Relation)

Verfügbar in

DSQL, ESQL

Syntax
CREATE [GLOBAL TEMPORARY] TABLE tablename
  [EXTERNAL [FILE] 'filespec']
  (<col_def> [, {<col_def> | <tconstraint>} ...])
  [ON COMMIT {DELETE | PRESERVE} ROWS]

<col_def> ::=
    <regular_col_def>
  | <computed_col_def>
  | <identity_col_def>

<regular_col_def> ::=
  colname {<datatype> | domainname}
  [DEFAULT {<literal> | NULL | <context_var>}]
  [<col_constraint> ...]
  [COLLATE collation_name]

<computed_col_def> ::=
  colname [{<datatype> | domainname}]
  {COMPUTED [BY] | GENERATED ALWAYS AS} (<expression>)

<identity_col_def> ::=
  colname {<datatype> | domainname}
  GENERATED BY DEFAULT AS IDENTITY [(START WITH startvalue)]
  [<col_constraint> ...]

<datatype> ::=
    <scalar_datatype> | <blob_datatype> | <array_datatype>

<scalar_datatype> ::=
  !! Siehe auch Skalardatentypensyntax !!

<blob_datatype> ::=
  !! Siehe auch BLOB-Datentypensyntax !!

<array_datatype> ::=
  !! Siehe auch Array-Datentypensyntax !!

<col_constraint> ::=
  [CONSTRAINT constr_name]
    { PRIMARY KEY [<using_index>]
    | UNIQUE      [<using_index>]
    | REFERENCES other_table [(colname)] [<using_index>]
        [ON DELETE {NO ACTION | CASCADE | SET DEFAULT | SET NULL}]
        [ON UPDATE {NO ACTION | CASCADE | SET DEFAULT | SET NULL}]
    | CHECK (<check_condition>)
    | NOT NULL }

<tconstraint> ::=
  [CONSTRAINT constr_name]
    { PRIMARY KEY (<col_list>) [<using_index>]
    | UNIQUE      (<col_list>) [<using_index>]
    | FOREIGN KEY (<col_list>)
        REFERENCES other_table [(<col_list>)] [<using_index>]
        [ON DELETE {NO ACTION | CASCADE | SET DEFAULT | SET NULL}]
        [ON UPDATE {NO ACTION | CASCADE | SET DEFAULT | SET NULL}]
    | CHECK (<check_condition>) }

<col_list> ::= colname [, colname ...]

<using_index> ::= USING
  [ASC[ENDING] | DESC[ENDING]] INDEX indexname

<check_condition> ::=
    <val> <operator> <val>
  | <val> [NOT] BETWEEN <val> AND <val>
  | <val> [NOT] IN (<val> [, <val> ...] | <select_list>)
  | <val> IS [NOT] NULL
  | <val> IS [NOT] DISTINCT FROM <val>
  | <val> [NOT] CONTAINING <val>
  | <val> [NOT] STARTING [WITH] <val>
  | <val> [NOT] LIKE <val> [ESCAPE <val>]
  | <val> [NOT] SIMILAR TO <val> [ESCAPE <val>]
  | <val> <operator> {ALL | SOME | ANY} (<select_list>)
  | [NOT] EXISTS (<select_expr>)
  | [NOT] SINGULAR (<select_expr>)
  | (<check_condition>)
  | NOT <check_condition>
  | <check_condition> OR <check_condition>
  | <check_condition> AND <check_condition>

<operator> ::=
    <> | != | ^= | ~= | = | < | > | <= | >=
  | !< | ^< | ~< | !> | ^> | ~>

<val> ::=
    colname ['['array_idx [, array_idx ...]']']
  | <literal>
  | <context_var>
  | <expression>
  | NULL
  | NEXT VALUE FOR genname
  | GEN_ID(genname, <val>)
  | CAST(<val> AS <cast_type>)
  | (<select_one>)
  | func([<val> [, <val> ...]])

<cast_type> ::= <domain_or_non_array_type> | <array_datatype>

<domain_or_non_array_type> ::=
  !! Siehe Skalardatentypensyntax !!
Tabelle 26. CREATE TABLE-Anweisungsparameter
Parameter Beschreibung

tablename

Name (Bezeichner) für die Tabelle. Sie darf bis zu 31 Zeichen lang sein und muss in der Datenbank eindeutig sein.

filespec

Dateispezifikation (nur für externe Tabellen). Vollständiger Dateiname und Pfad in einfachen Anführungszeichen, korrekt für das lokale Dateisystem und auf einem Speichergerät, das physisch mit dem Host-Computer von Firebird verbunden ist.

colname

Name (Bezeichner) für eine Spalte in der Tabelle. Darf bis zu 31 Zeichen lang sein und muss in der Tabelle eindeutig sein.

datatype

SQL-Datentyp

domain_name

Domainname

start_value

Der Anfangswert der Identitätsspalte

col_constraint

Spaltenbeschränkung

tconstraint

Tabellenbeschränkung

constr_name

Der Name (Bezeichner) einer Einschränkung. Kann aus bis zu 31 Zeichen bestehen.

other_table

Der Name der Tabelle, auf die von der Fremdschlüsseleinschränkung verwiesen wird

other_col

Der Name der Spalte in other_table, auf die der Fremdschlüssel verweist

literal

Ein Literalwert, der im angegebenen Kontext zulässig ist

context_var

Jede Kontextvariable, deren Datentyp im angegebenen Kontext zulässig ist

check_condition

Die auf eine CHECK-Einschränkung angewendete Bedingung, die entweder als wahr, falsch oder NULL aufgelöst wird

collation

Collation

select_one

Eine skalare SELECT-Anweisung – Auswahl einer Spalte und Rückgabe nur einer Zeile

select_list

Eine SELECT-Anweisung, die eine Spalte auswählt und null oder mehr Zeilen zurückgibt

select_expr

Eine SELECT-Anweisung, die eine oder mehrere Spalten auswählt und null oder mehr Zeilen zurückgibt

expression

Ein Ausdruck, der zu einem Wert auflöst, der im gegebenen Kontext zulässig ist

genname

Name der Sequenz (Generator)

func

Interne Funktion oder UDF

Die Anweisung CREATE TABLE erstellt eine neue Tabelle. Jeder Benutzer kann es erstellen und sein Name muss unter den Namen aller Tabellen, Ansichten und gespeicherten Prozeduren in der Datenbank eindeutig sein.

Eine Tabelle muss mindestens eine nicht berechnete Spalte enthalten, und die Namen der Spalten müssen in der Tabelle eindeutig sein.

Eine Spalte muss entweder einen expliziten SQL-Datentyp haben, den Namen einer Domäne, deren Attribute für die Spalte kopiert werden, oder als COMPUTED BY einen Ausdruck (ein berechnetes Feld) definiert sein.

Eine Tabelle kann eine beliebige Anzahl von Tabelleneinschränkungen haben, einschließlich keiner.

Zeichenspalten

Sie können die Klausel CHARACTER SET verwenden, um den Zeichensatz für die Typen CHAR, VARCHAR und BLOB (Textsubtyp) anzugeben. Wenn der Zeichensatz nicht angegeben ist, wird der Standardzeichensatz der Datenbank - zum Zeitpunkt der Erstellung der Spalte - verwendet. Wenn die Datenbank keinen Standardzeichensatz hat, wird der Zeichensatz NONE angewendet. In diesem Fall werden die Daten so gespeichert und abgerufen, wie sie übermittelt wurden. Einer solchen Spalte können Daten in einer beliebigen Codierung hinzugefügt werden, es ist jedoch nicht möglich, diese Daten einer Spalte mit einer anderen Codierung hinzuzufügen. Zwischen den Quell- und Zielkodierungen wird keine Transliteration durchgeführt, was zu Fehlern führen kann.

Mit der optionalen COLLATE-Klausel können Sie die Kollatierungssequenz für Zeichendatentypen angeben, einschließlich BLOB SUB_TYPE TEXT. Wenn keine Kollatierungssequenz angegeben ist, wird die Standardkollatierungssequenz für den angegebenen Zeichensatz - zum Zeitpunkt der Erstellung der Spalte - angewendet.

Einstellen eines DEFAULT-Wertes

Mit der optionalen DEFAULT-Klausel können Sie den Standardwert für die Tabellenspalte angeben. Dieser Wert wird der Spalte hinzugefügt, wenn eine INSERT-Anweisung ausgeführt wird, wenn kein Wert dafür angegeben wurde und diese Spalte im INSERT-Befehl weggelassen wurde.

Der Standardwert kann ein Literal eines kompatiblen Typs sein, eine Kontextvariable, die mit dem Datentyp der Spalte typkompatibel ist, oder NULL, wenn die Spalte dies zulässt. Wenn kein Standardwert explizit angegeben wird, wird NULL impliziert.

Ein Ausdruck kann nicht als Standardwert verwendet werden.

Domainenbasierte Spalten

Um eine Spalte zu definieren, können Sie eine zuvor definierte Domäne verwenden. Wenn die Definition einer Spalte auf einer Domäne basiert, kann sie einen neuen Standardwert, zusätzliche CHECK-Einschränkungen und eine COLLATE-Klausel enthalten, die die in der Domänendefinition angegebenen Werte überschreibt. Die Definition einer solchen Spalte kann zusätzliche Spaltenbeschränkungen enthalten (zB NOT NULL), wenn die Domäne sie nicht hat.

Es ist nicht möglich, eine domänenbasierte Spalte zu definieren, die NULL-Werte zulässt, wenn die Domäne mit dem Attribut NOT NULL definiert wurde. Wenn Sie eine Domäne haben möchten, die sowohl zum Definieren von nullbaren als auch nicht-nullbaren Spalten und Variablen verwendet werden kann, ist es besser, die Domäne nullable zu definieren und NOT NULL in den nachfolgenden Spaltendefinitionen und Variablendeklarationen anzuwenden.

Identitätsspalten (autoinkrement)

Identitätsspalten können mit der GENERATED BY DEFAULT AS IDENTITY-Klausel definiert werden. Die Identitätsspalte ist die Spalte, die dem internen Sequenzgenerator zugeordnet ist. Sein Wert wird jedes Mal automatisch gesetzt, wenn er nicht in der INSERT-Anweisung angegeben wird. Mit der optionalen START WITH-Klausel können Sie einen anderen Anfangswert als 1 angeben.

Falsches START WITH-Verhalten

Der SQL-Standard verlangt, dass START WITH den ersten zu generierenden Wert angibt. Leider verwendet die aktuelle Implementierung in Firebird stattdessen den angegebenen Wert als Anfangswert des internen Generators, der die Identitätsspalte unterstützt. Das bedeutet, dass es im Moment den Wert vor dem ersten generierten Wert angibt.

Dies wird in Firebird 4 behoben, siehe auch CORE-6376.

Regeln
  • Der Datentyp einer Identitätsspalte muss ein exakter Zahlentyp mit Nullskala sein. Erlaubte Typen sind somit SMALLINT, INTEGER, BIGINT, NUMERIC(p[,0]) und DECIMAL(p[,0]).

  • Eine Identitätsspalte darf keinen DEFAULT- oder COMPUTED-Wert haben.

  • Eine Identitätsspalte kann nicht in eine reguläre Spalte geändert werden. Das Umgekehrte gilt auch. Firebird 4 führt die Option ein, eine Identitätsspalte in eine normale Spalte zu ändern.

  • Identitätsspalten sind implizit NOT NULL (non-nullable).

  • Eindeutigkeit wird nicht automatisch erzwungen. Eine UNIQUE- oder PRIMARY KEY-Beschränkung ist erforderlich, um die Eindeutigkeit zu garantieren.

  • Die Verwendung anderer Methoden zur Generierung von Schlüsselwerten für Identitätsspalten, z. B. durch Trigger-Generator-Code oder indem Benutzern erlaubt wird, sie zu ändern oder hinzuzufügen, wird davon abgeraten, unerwartete Schlüsselverletzungen zu vermeiden.

Berechnete Felder

Berechnete Felder können mit der COMPUTED [BY]- oder GENERATED ALWAYS AS-Klausel (gemäß SQL:2003-Standard) definiert werden. Sie meinen dasselbe. Die Beschreibung des Datentyps ist für berechnete Felder nicht erforderlich (aber möglich), da das DBMS als Ergebnis der Ausdrucksanalyse den entsprechenden Typ berechnet und speichert. Entsprechende Operationen für die in einem Ausdruck enthaltenen Datentypen müssen genau angegeben werden.

Wenn der Datentyp für ein berechnetes Feld explizit angegeben wird, wird das Berechnungsergebnis in den angegebenen Typ konvertiert. Das bedeutet zum Beispiel, dass das Ergebnis eines numerischen Ausdrucks als String ausgegeben werden könnte.

In einer Abfrage, die eine COMPUTED BY-Spalte auswählt, wird der Ausdruck für jede Zeile der ausgewählten Daten ausgewertet.

Anstelle einer berechneten Spalte ist es in manchen Fällen sinnvoll, eine reguläre Spalte zu verwenden, deren Wert in Triggern zum Hinzufügen und Aktualisieren von Daten ausgewertet wird. Dies kann die Leistung beim Einfügen/Aktualisieren von Datensätzen verringern, aber die Leistung der Datenauswahl erhöhen.

Definieren einer Array-Spalte
  • Wenn die Spalte ein Array sein soll, kann der Basistyp ein beliebiger SQL-Datentyp außer BLOB und Array sein.

  • Die Abmessungen des Arrays sind in eckigen Klammern angegeben. (Im Syntax block erscheinen diese Klammern in Anführungszeichen, um sie von den eckigen Klammern zu unterscheiden, die optionale Syntaxelemente kennzeichnen.)

  • Für jede Array-Dimension definieren eine oder zwei ganze Zahlen die untere und obere Grenze ihres Indexbereichs:

    • Arrays sind standardmäßig 1-basiert. Die untere Grenze ist implizit und nur die obere Grenze muss angegeben werden. Eine einzelne Zahl kleiner als 1 definiert den Bereich num..1 und eine Zahl größer als 1 definiert den Bereich 1..num.

    • Zwei durch einen Doppelpunkt getrennte Zahlen (‘:’) und optionales Leerzeichen, das zweite größer als das erste, können verwendet werden, um den Bereich explizit zu definieren. Eine oder beide Grenzen können kleiner als Null sein, solange die obere Grenze größer als die untere ist.

  • Wenn das Array mehrere Dimensionen hat, müssen die Bereichsdefinitionen für jede Dimension durch Kommas und optionales Leerzeichen getrennt werden.

  • Indizes werden nur validiert, wenn tatsächlich ein Array existiert. Das bedeutet, dass keine Fehlermeldungen bezüglich ungültiger Indizes zurückgegeben werden, wenn die Auswahl eines bestimmten Elements nichts zurückgibt oder wenn ein Array-Feld [constant] NULL ist.

Constraints

Es können fünf Arten von Einschränkungen angegeben werden. Sie sind:

  • Primärschlüssel (PRIMARY KEY)

  • Eindeutiger Schlüssel (UNIQUE)

  • Fremdschlüssel (REFERENCES)

  • CHECK-Einschränkung (CHECK)

  • NOT NULL-Einschränkung (NOT NULL)

Einschränkungen können auf Spaltenebene (“Spaltenbeschränkungen”) oder auf Tabellenebene (“Tabellenbeschränkungen”) angegeben werden. Einschränkungen auf Tabellenebene sind erforderlich, wenn Schlüssel (eindeutige Einschränkung, Primärschlüssel, Fremdschlüssel) aus mehreren Spalten bestehen und wenn eine CHECK-Einschränkung andere Spalten in der Zeile als die definierte Spalte umfasst. Die Einschränkung NOT NULL kann nur als Spalteneinschränkung angegeben werden. Die Syntax einiger Einschränkungstypen kann geringfügig abweichen, je nachdem, ob die Einschränkung auf Spalten- oder Tabellenebene definiert ist.

  • Eine Einschränkung auf Spaltenebene wird während einer Spaltendefinition angegeben, nachdem alle Spaltenattribute außer COLLATION angegeben wurden, und kann nur die in dieser Definition angegebene Spalte betreffen

  • Einschränkungen auf Tabellenebene können nur nach den Definitionen der Spalten angegeben werden, die in der Einschränkung verwendet werden.

  • Einschränkungen auf Tabellenebene sind eine flexiblere Möglichkeit, Einschränkungen festzulegen, da sie Einschränkungen mit mehreren Spalten berücksichtigen können

  • Sie können Einschränkungen auf Spaltenebene und auf Tabellenebene in derselben CREATE TABLE-Anweisung mischen

Das System erstellt automatisch den entsprechenden Index für einen Primärschlüssel (PRIMARY KEY), einen eindeutigen Schlüssel (UNIQUE) und einen Fremdschlüssel (REFERENCES für eine Einschränkung auf Spaltenebene, FOREIGN KEY REFERENCES für eine auf der Tabellenebene).

Namen für Einschränkungen und ihre Indizes

Einschränkungen auf Spaltenebene und ihre Indizes werden automatisch benannt:

  • Der Name der Einschränkung hat die Form INTEG_n, wobei n eine oder mehrere Ziffern darstellt

  • Der Indexname hat die Form RDB$PRIMARYn (für einen Primärschlüsselindex), RDB$FOREIGNn (für einen Fremdschlüsselindex) oder RDB$n (für einen eindeutigen Schlüsselindex). Auch hier steht n für eine oder mehrere Ziffern.

Die automatische Benennung von Integritätsbedingungen auf Tabellenebene und ihrer Indizes folgt demselben Muster, es sei denn, die Namen werden explizit angegeben.

Benannte Constraints

Eine Einschränkung kann explizit benannt werden, wenn die CONSTRAINT-Klausel für ihre Definition verwendet wird. Während die Klausel CONSTRAINT zum Definieren von Einschränkungen auf Spaltenebene optional ist, ist sie für Einschränkungen auf Tabellenebene obligatorisch. Standardmäßig hat der Einschränkungsindex denselben Namen wie die Einschränkung. Wenn für den Constraint-Index ein anderer Name gewünscht wird, kann eine USING-Klausel eingefügt werden.

Die USING-Klausel

Mit der USING-Klausel können Sie einen benutzerdefinierten Namen für den automatisch erstellten Index angeben und optional die Richtung des Index festlegen – entweder aufsteigend (Standard) oder absteigend.

PRIMARY KEY

Die Einschränkung PRIMARY KEY basiert auf einer oder mehreren Schlüsselspalten, wobei für jede Spalte die Einschränkung NOT NULL angegeben ist. Die Werte in den Schlüsselspalten in jeder Zeile müssen eindeutig sein. Eine Tabelle kann nur einen Primärschlüssel haben.

  • Ein einspaltiger Primärschlüssel kann als Einschränkung auf Spaltenebene oder als Einschränkung auf Tabellenebene definiert werden

  • Als Einschränkung auf Tabellenebene muss ein mehrspaltiger Primärschlüssel angegeben werden

Die UNIQUE-Einschränkung

Die Einschränkung UNIQUE definiert die Anforderung der Eindeutigkeit des Inhalts für die Werte in einem Schlüssel in der gesamten Tabelle. Eine Tabelle kann eine beliebige Anzahl eindeutiger Schlüsseleinschränkungen enthalten.

Wie beim Primärschlüssel kann die Unique-Einschränkung mehrspaltig sein. Wenn dies der Fall ist, muss sie als Einschränkung auf Tabellenebene angegeben werden.

NULL in Unique Keys

Die SQL-99-kompatiblen Regeln von Firebird für UNIQUE-Beschränkungen erlauben eine oder mehrere NULLs in einer Spalte mit einer UNIQUE-Beschränkung. Dadurch ist es möglich, eine UNIQUE-Beschränkung für eine Spalte zu definieren, die nicht die NOT NULL-Beschränkung hat.

Bei UNIQUE-Schlüsseln, die sich über mehrere Spalten erstrecken, ist die Logik etwas kompliziert:

  • Mehrere Zeilen mit Null in allen Spalten des Schlüssels sind zulässig

  • Mehrere Zeilen mit Schlüsseln mit unterschiedlichen Kombinationen von Nullen und Nicht-Null-Werten sind zulässig

  • Mehrere Zeilen mit den gleichen Schlüsselspalten null und der Rest mit Werten ungleich null sind erlaubt, sofern sich die Werte in mindestens einer Spalte unterscheiden

  • Mehrere Zeilen mit den gleichen Schlüsselspalten null und der Rest mit Werten ungleich null gefüllt, die in jeder Spalte gleich sind, verletzen die Einschränkung

Die Regeln für die Eindeutigkeit lassen sich wie folgt zusammenfassen:

Im Prinzip werden alle Nullen als verschieden betrachtet. Wenn jedoch zwei Zeilen genau die gleichen Schlüsselspalten haben, die mit Nicht-Null-Werten gefüllt sind, werden die 'NULL'-Spalten ignoriert und die Eindeutigkeit der Nicht-Null-Spalten wird so bestimmt, als ob sie den gesamten Schlüssel bilden würden.
Illustration
RECREATE TABLE t( x int, y int, z int, unique(x,y,z));
INSERT INTO t values( NULL, 1, 1 );
INSERT INTO t values( NULL, NULL, 1 );
INSERT INTO t values( NULL, NULL, NULL );
INSERT INTO t values( NULL, NULL, NULL ); -- Permitted
INSERT INTO t values( NULL, NULL, 1 );    -- Not permitted
FOREIGN KEY

Ein Fremdschlüssel stellt sicher, dass die teilnehmende(n) Spalte(n) nur Werte enthalten können, die auch in der/den referenzierten Spalte(n) der Mastertabelle vorhanden sind. Diese referenzierten Spalten werden oft als target column bezeichnet. Sie müssen der Primärschlüssel oder ein eindeutiger Schlüssel in der Zieltabelle sein. Für sie muss keine NOT NULL-Beschränkung definiert sein, obwohl sie, wenn sie der Primärschlüssel sind, natürlich diese Einschränkung haben.

Die Fremdschlüsselspalten in der referenzierenden Tabelle selbst erfordern keine NOT NULL-Einschränkung.

Ein einspaltiger Fremdschlüssel kann in der Spaltendeklaration mit dem Schlüsselwort REFERENCES definiert werden:

... ,
  ARTIFACT_ID INTEGER REFERENCES COLLECTION (ARTIFACT_ID),

Die Spalte ARTIFACT_ID im Beispiel verweist auf eine gleichnamige Spalte in der Tabelle COLLECTIONS.

Auf der Tabellenebene können sowohl einspaltige als auch mehrspaltige Fremdschlüssel definiert werden. Bei einem mehrspaltigen Fremdschlüssel ist die Deklaration auf Tabellenebene die einzige Option. Diese Methode ermöglicht auch die Bereitstellung eines optionalen Namens für die Einschränkung:

...
  CONSTRAINT FK_ARTSOURCE FOREIGN KEY(DEALER_ID, COUNTRY)
    REFERENCES DEALER (DEALER_ID, COUNTRY),

Beachten Sie, dass sich die Spaltennamen in der referenzierten Tabelle (“master”) von denen im Fremdschlüssel unterscheiden können.

Wenn keine Zielspalten angegeben sind, verweist der Fremdschlüssel automatisch auf den Primärschlüssel der Zieltabelle.

Fremdschlüsselaktionen

Mit den Unterklauseln ON UPDATE und ON DELETE ist es möglich, eine Aktion für die betroffene(n) Fremdschlüsselspalte(n) festzulegen, wenn referenzierte Werte in der Mastertabelle geändert werden:

KEINE AKTION

(Standard) - Nichts wird getan

CASCADE

Die Änderung in der Master-Tabelle wird an die entsprechende(n) Zeile(n) in der Child-Tabelle weitergegeben. Wenn sich ein Schlüsselwert ändert, ändert sich der entsprechende Schlüssel in den untergeordneten Datensätzen auf den neuen Wert; Wenn die Masterzeile gelöscht wird, werden die untergeordneten Datensätze gelöscht.

SET DEFAULT

Die Fremdschlüsselspalten in den betroffenen Zeilen werden auf ihre Standardwerte gesetzt wie sie waren, als die Fremdschlüsseleinschränkung definiert wurde.

SET NULL

Die Fremdschlüsselspalten in den betroffenen Zeilen werden auf NULL gesetzt.

Die angegebene Aktion oder die Standardeinstellung NO ACTION kann dazu führen, dass eine Fremdschlüsselspalte ungültig wird. Sie könnte beispielsweise einen Wert erhalten, der in der Mastertabelle nicht vorhanden ist, oder er könnte NULL werden, während die Spalte eine NOT NULL-Einschränkung hat. Solche Bedingungen führen dazu, dass die Operation in der Mastertabelle mit einer Fehlermeldung fehlschlägt.

Beispiel
...
  CONSTRAINT FK_ORDERS_CUST
    FOREIGN KEY (CUSTOMER) REFERENCES CUSTOMERS (ID)
      ON UPDATE CASCADE ON DELETE SET NULL
CHECK-Einschränkung

Die Einschränkung CHECK definiert die Bedingung, die die in diese Spalte eingefügten Werte erfüllen müssen. Eine Bedingung ist ein logischer Ausdruck (auch Prädikat genannt), der die Werte TRUE, FALSE und UNKNOWN zurückgeben kann. Eine Bedingung gilt als erfüllt, wenn das Prädikat TRUE oder den Wert UNKNOWN (entspricht NULL) zurückgibt. Wenn das Prädikat FALSE zurückgibt, wird der Wert nicht akzeptiert. Diese Bedingung wird zum Einfügen einer neuen Zeile in die Tabelle (die INSERT-Anweisung) und zum Aktualisieren des vorhandenen Wertes der Tabellenspalte (die UPDATE-Anweisung) und auch für Anweisungen verwendet, bei denen eine dieser Aktionen stattfinden kann (UPDATE ODER EINFÜGEN, MERGE).

Eine CHECK-Bedingung für eine domänenbasierte Spalte ersetzt keine vorhandene CHECK-Bedingung in der Domäne, sondern wird zu einer Ergänzung dazu. Die Firebird-Engine hat während der Definition keine Möglichkeit zu überprüfen, ob das zusätzliche CHECK nicht mit dem vorhandenen kollidiert.

CHECK-Einschränkungen — ob auf Tabellen- oder Spaltenebene definiert — beziehen sich auf Tabellenspalten nach ihren Namen. Die Verwendung des Schlüsselworts VALUE als Platzhalter – wie in den CHECK-Einschränkungen der Domäne – ist im Kontext der Definition von Spalteneinschränkungen nicht gültig.

Beispiel

mit zwei Einschränkungen auf Spaltenebene und einer auf Tabellenebene:

CREATE TABLE PLACES (
  ...
  LAT DECIMAL(9, 6) CHECK (ABS(LAT) <=  90),
  LON DECIMAL(9, 6) CHECK (ABS(LON) <= 180),
  ...
  CONSTRAINT CHK_POLES CHECK (ABS(LAT) < 90 OR LON = 0)
);
NOT NULL-Einschränkung

In Firebird sind Spalten standardmäßig nullable. Die Einschränkung NOT NULL gibt an, dass die Spalte nicht NULL anstelle eines Werts annehmen kann.

Ein NOT NULL-Constraint kann nur als Spalten-Constraint definiert werden, nicht als Tabellen-Constraint.

Wer kann eine Tabelle erstellen

Die CREATE TABLE-Anweisung kann ausgeführt werden durch:

Der Benutzer, der die Anweisung CREATE TABLE ausführt, wird Eigentümer der Tabelle.

CREATE TABLE-Beispiele
  1. Erstellen der Tabelle "COUNTRY" mit dem als Spalteneinschränkung angegebenen Primärschlüssel.

    CREATE TABLE COUNTRY (
      COUNTRY COUNTRYNAME NOT NULL PRIMARY KEY,
      CURRENCY VARCHAR(10) NOT NULL
    );
  2. Erstellen der Tabelle STOCK mit dem benannten Primärschlüssel, der auf Spaltenebene angegeben ist, und dem benannten eindeutigen Schlüssel, der auf Tabellenebene angegeben ist.

    CREATE TABLE STOCK (
      MODEL     SMALLINT NOT NULL CONSTRAINT PK_STOCK PRIMARY KEY,
      MODELNAME CHAR(10) NOT NULL,
      ITEMID    INTEGER NOT NULL,
      CONSTRAINT MOD_UNIQUE UNIQUE (MODELNAME, ITEMID)
    );
  3. Erstellen der Tabelle "JOB" mit einer Primärschlüssel-Einschränkung, die sich über zwei Spalten erstreckt, einer Fremdschlüssel-Einschränkung für die Tabelle "COUNTRY" und einer "CHECK"-Einschränkung auf Tabellenebene. Die Tabelle enthält auch ein Array von 5 Elementen.

    CREATE TABLE JOB (
      JOB_CODE        JOBCODE NOT NULL,
      JOB_GRADE       JOBGRADE NOT NULL,
      JOB_COUNTRY     COUNTRYNAME,
      JOB_TITLE       VARCHAR(25) NOT NULL,
      MIN_SALARY      NUMERIC(18, 2) DEFAULT 0 NOT NULL,
      MAX_SALARY      NUMERIC(18, 2) NOT NULL,
      JOB_REQUIREMENT BLOB SUB_TYPE 1,
      LANGUAGE_REQ    VARCHAR(15) [1:5],
      PRIMARY KEY (JOB_CODE, JOB_GRADE),
      FOREIGN KEY (JOB_COUNTRY) REFERENCES COUNTRY (COUNTRY)
      ON UPDATE CASCADE
      ON DELETE SET NULL,
      CONSTRAINT CHK_SALARY CHECK (MIN_SALARY < MAX_SALARY)
    );
  4. Erstellen der Tabelle "PROJECT" mit Einschränkungen für Primär-, Fremd- und eindeutige Schlüssel mit benutzerdefinierten Indexnamen, die mit der Klausel "USING" angegeben werden.

    CREATE TABLE PROJECT (
      PROJ_ID     PROJNO NOT NULL,
      PROJ_NAME   VARCHAR(20) NOT NULL UNIQUE USING DESC INDEX IDX_PROJNAME,
      PROJ_DESC   BLOB SUB_TYPE 1,
      TEAM_LEADER EMPNO,
      PRODUCT     PRODTYPE,
      CONSTRAINT PK_PROJECT PRIMARY KEY (PROJ_ID) USING INDEX IDX_PROJ_ID,
      FOREIGN KEY (TEAM_LEADER) REFERENCES EMPLOYEE (EMP_NO)
        USING INDEX IDX_LEADER
    );
  5. Erstellen einer Tabelle mit einer Identitätsspalte

    create table objects (
      id integer generated by default as identity primary key,
      name varchar(15)
    );
    
    insert into objects (name) values ('Table');
    insert into objects (id, name) values (10, 'Computer');
    insert into objects (name) values ('Book');
    
    select * from objects order by id;
    
              ID NAME
    ============ ===============
               1 Table
               2 Book
              10 Computer
  6. Erstellen der Tabelle "SALARY_HISTORY" mit zwei berechneten Feldern. Das erste wird gemäß dem SQL:2003-Standard deklariert, während das zweite gemäß der traditionellen Deklaration von berechneten Feldern in Firebird deklariert wird.

    CREATE TABLE SALARY_HISTORY (
      EMP_NO         EMPNO NOT NULL,
      CHANGE_DATE    TIMESTAMP DEFAULT 'NOW' NOT NULL,
      UPDATER_ID     VARCHAR(20) NOT NULL,
      OLD_SALARY     SALARY NOT NULL,
      PERCENT_CHANGE DOUBLE PRECISION DEFAULT 0 NOT NULL,
      SALARY_CHANGE  GENERATED ALWAYS AS
        (OLD_SALARY * PERCENT_CHANGE / 100),
      NEW_SALARY     COMPUTED BY
        (OLD_SALARY + OLD_SALARY * PERCENT_CHANGE / 100)
    );
Global Temporary Tables (GTT)

Globale temporäre Tabellen verfügen über persistente Metadaten, ihr Inhalt ist jedoch transaktionsgebunden (Standard) oder verbindungsgebunden. Jede Transaktion oder Verbindung hat ihre eigene private Instanz einer GTT, die von allen anderen isoliert ist. Instanzen werden nur erstellt, wenn und wenn auf die GTT verwiesen wird. Sie werden zerstört, wenn die Transaktion endet oder wenn die Verbindung getrennt wird. Die Metadaten einer GTT können mit ALTER TABLE bzw. DROP TABLE geändert oder entfernt werden.

Syntax
CREATE GLOBAL TEMPORARY TABLE tablename
  (<column_def> [, {<column_def> | <table_constraint>} ...])
  [ON COMMIT {DELETE | PRESERVE} ROWS]
Syntax notes
  • ON COMMIT DELETE ROWS erstellt eine GTT auf Transaktionsebene (Standard), ON COMMIT PRESERVE ROWS eine GTT auf Verbindungsebene

  • Eine EXTERNAL [FILE]-Klausel ist in der Definition einer globalen temporären Tabelle nicht erlaubt

Seit Firebird 3.0 sind GTTs in schreibgeschützten Transaktionen beschreibbar. Die Wirkung ist wie folgt:

Schreibgeschützte Transaktion in der Datenbank mit Lese-/Schreibzugriff

Schreibbar in ON COMMIT PRESERVE ROWS und ON COMMIT DELETE ROWS

Schreibgeschützte Transaktion in schreibgeschützter Datenbank

Nur in ON COMMIT DELETE ROWS beschreibbar

Einschränkungen für GTTs

GTTs können mit allen Funktionen und Utensilien gewöhnlicher Tabellen (Schlüssel, Referenzen, Indizes, Trigger usw.) “aufgeputzt” werden, aber es gibt einige Einschränkungen:

  • GTTs und reguläre Tabellen können nicht aufeinander verweisen

  • Eine verbindungsgebundene (“PRESERVE ROWS”) GTT kann nicht auf eine transaktionsgebundene (“DELETE ROWS”) GTT verweisen

  • Domäneneinschränkungen können keine GTT referenzieren

  • Die Zerstörung einer GTT-Instanz am Ende ihres Lebenszyklus führt nicht zum Auslösen von BEFORE/AFTER Delete-Triggern

In einer bestehenden Datenbank ist es nicht immer einfach, eine reguläre Tabelle von einer GTT oder eine GTT auf Transaktionsebene von einer GTT auf Verbindungsebene zu unterscheiden. Verwenden Sie diese Abfrage, um herauszufinden, welche Art von Tabelle Sie betrachten:

select t.rdb$type_name
from rdb$relations r
join rdb$types t on r.rdb$relation_type = t.rdb$type
where t.rdb$field_name = 'RDB$RELATION_TYPE'
and r.rdb$relation_name = 'TABLENAME'

Für einen Überblick über die Typen aller Relationen in der Datenbank:

select r.rdb$relation_name, t.rdb$type_name
from rdb$relations r
join rdb$types t on r.rdb$relation_type = t.rdb$type
where t.rdb$field_name = 'RDB$RELATION_TYPE'
and coalesce (r.rdb$system_flag, 0) = 0

Das Feld RDB$TYPE_NAME zeigt PERSISTENT für eine reguläre Tabelle, VIEW für eine Ansicht, GLOBAL_TEMPORARY_PRESERVE für eine verbindungsgebundene GTT und GLOBAL_TEMPORARY_DELETE für eine transaktionsgebundene GTT.

Beispiele für globale temporäre Tabellen
  1. Erstellen einer globalen temporären Tabelle mit Verbindungsbereich.

    CREATE GLOBAL TEMPORARY TABLE MYCONNGTT (
      ID  INTEGER NOT NULL PRIMARY KEY,
      TXT VARCHAR(32),
      TS  TIMESTAMP DEFAULT CURRENT_TIMESTAMP)
    ON COMMIT PRESERVE ROWS;
  2. Erstellen einer transaktionsbezogenen globalen temporären Tabelle, die einen Fremdschlüssel verwendet, um auf eine verbindungsbezogene globale temporäre Tabelle zu verweisen. Die Unterklausel ON COMMIT ist optional, da DELETE ROWS die Vorgabe ist.

    CREATE GLOBAL TEMPORARY TABLE MYTXGTT (
      ID        INTEGER NOT NULL PRIMARY KEY,
      PARENT_ID INTEGER NOT NULL REFERENCES MYCONNGTT(ID),
      TXT       VARCHAR(32),
      TS        TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    ) ON COMMIT DELETE ROWS;
Externe Tabellen

Die optionale EXTERNAL [FILE]-Klausel gibt an, dass die Tabelle außerhalb der Datenbank in einer externen Textdatei mit Datensätzen fester Länge gespeichert wird. Die Spalten einer Tabelle, die in einer externen Datei gespeichert sind, können jeden beliebigen Typ haben, außer 'BLOB' oder 'ARRAY', obwohl für die meisten Zwecke nur Spalten des Typs 'CHAR' nützlich wären.

Mit einer in einer externen Datei gespeicherten Tabelle können Sie nur neue Zeilen einfügen (INSERT) und die Daten abfragen (SELECT). Das Aktualisieren vorhandener Daten (UPDATE) und das Löschen von Zeilen (DELETE) sind nicht möglich.

Eine Datei, die als externe Tabelle definiert ist, muss sich auf einem Speichergerät befinden, das physisch auf dem Computer vorhanden ist, auf dem der Firebird-Server läuft, und wenn der Parameter ExternalFileAccess in der Konfigurationsdatei firebird.conf den Wert Restrict hat , muss es in einem der dort aufgeführten Verzeichnisse als Argument für Restrict liegen. Wenn die Datei noch nicht existiert, erstellt Firebird sie beim ersten Zugriff.

Die Möglichkeit, externe Dateien für eine Tabelle zu verwenden, hängt vom Wert ab, der für den Parameter ExternalFileAccess in firebird.conf festgelegt wurde:

  • Wenn es auf None (Standard) gesetzt ist, wird jeder Versuch, auf eine externe Datei zuzugreifen, abgelehnt.

  • Die Einstellung Beschränken wird empfohlen, um den externen Dateizugriff auf Verzeichnisse einzuschränken, die explizit für diesen Zweck vom Serveradministrator erstellt wurden. Zum Beispiel:

    • ExternalFileAccess = Restrict externalfiles beschränkt den Zugriff auf ein Verzeichnis namens externalfiles direkt unter dem Firebird-Stammverzeichnis

    • ExternalFileAccess = d:\databases\outfiles; e:\infiles beschränkt den Zugriff auf nur diese beiden Verzeichnisse auf dem Windows-Hostserver. Beachten Sie, dass alle Pfade, die eine Netzwerkzuordnung darstellen, nicht funktionieren. Pfade, die in einfache oder doppelte Anführungszeichen eingeschlossen sind, funktionieren ebenfalls nicht.

  • Wenn dieser Parameter auf Full gesetzt ist, kann auf externe Dateien überall im Host-Dateisystem zugegriffen werden. Dies schafft eine Sicherheitslücke und wird nicht empfohlen.

Externes Dateiformat

Das “row”-Format der externen Tabelle hat eine feste Länge und ist binär. Es gibt keine Feldbegrenzer: Sowohl Feld- als auch Zeilengrenzen werden durch die maximale Größe der Felddefinitionen in Byte bestimmt. Dies ist sowohl bei der Definition der Struktur der externen Tabelle als auch beim Entwurf einer Eingabedatei für eine externe Tabelle wichtig, die Daten aus einer anderen Anwendung importieren soll. Das allgegenwärtige Format “.csv” zum Beispiel ist als Eingabedatei unbrauchbar und kann nicht direkt in eine externe Datei generiert werden.

Der nützlichste Datentyp für die Spalten externer Tabellen ist der Typ "CHAR" mit fester Länge und geeigneter Länge für die zu übertragenden Daten. Datums- und Zahlentypen lassen sich leicht in und aus Strings umwandeln, während die nativen Datentypen – Binärdaten – für externe Anwendungen als nicht zu analysierendes "Alphabetti" erscheinen, es sei denn, die Dateien sollen von einer anderen Firebird-Datenbank gelesen werden.

Natürlich gibt es Möglichkeiten, typisierte Daten zu manipulieren, um Ausgabedateien von Firebird zu erzeugen, die direkt als Eingabedateien für andere Anwendungen gelesen werden können, unter Verwendung von gespeicherten Prozeduren, mit oder ohne Verwendung externer Tabellen. Solche Techniken gehen über den Umfang einer Sprachreferenz hinaus. Hier geben wir einige Richtlinien und Tipps zum Erstellen und Arbeiten mit einfachen Textdateien, da die externe Tabellenfunktion oft als einfache Möglichkeit verwendet wird, transaktionsunabhängige Protokolle zu erstellen oder zu lesen, die offline in einem Texteditor oder Auditing untersucht werden können Anwendung.

Zeilentrennzeichen

Im Allgemeinen sind externe Dateien nützlicher, wenn Zeilen durch ein Trennzeichen in Form einer “newline”-Sequenz getrennt werden, die von Reader-Anwendungen auf der vorgesehenen Plattform erkannt wird. Für die meisten Kontexte unter Windows ist es die Zwei-Byte-'CRLF'-Sequenz, Wagenrücklauf (ASCII-Code dezimal 13) und Zeilenvorschub (ASCII-Code dezimal 10). Auf POSIX ist LF allein üblich; bei einigen MacOSX-Anwendungen kann es LFCR sein. Es gibt verschiedene Möglichkeiten, diese Trennzeichenspalte zu füllen. In unserem Beispiel unten geschieht dies mit einem BEFORE INSERT Trigger und der internen Funktion ASCII_CHAR.

Beispiel für eine externe Tabelle

In unserem Beispiel definieren wir eine externe Protokolltabelle, die von einem Ausnahmehandler in einer gespeicherten Prozedur oder einem Trigger verwendet werden könnte. Die externe Tabelle wird ausgewählt, weil die Nachrichten von allen behandelten Ausnahmen im Protokoll aufbewahrt werden, selbst wenn die Transaktion, die den Prozess gestartet hat, schließlich aufgrund einer anderen, nicht behandelten Ausnahme zurückgesetzt wird. Zu Demonstrationszwecken hat es nur zwei Datenspalten, einen Zeitstempel und eine Nachricht. Die dritte Spalte speichert das Zeilentrennzeichen:

CREATE TABLE ext_log
  EXTERNAL FILE 'd:\externals\log_me.txt' (
  stamp CHAR (24),
  message CHAR(100),
  crlf CHAR(2) -- for a Windows context
);
COMMIT;

Jetzt ein Trigger, um den Zeitstempel und das Zeilentrennzeichen jedes Mal zu schreiben, wenn eine Nachricht in die Datei geschrieben wird:

SET TERM ^;
CREATE TRIGGER bi_ext_log FOR ext_log
ACTIVE BEFORE INSERT
AS
BEGIN
  IF (new.stamp is NULL) then
    new.stamp = CAST (CURRENT_TIMESTAMP as CHAR(24));
  new.crlf = ASCII_CHAR(13) || ASCII_CHAR(10);
END ^
COMMIT ^
SET TERM ;^

Einfügen einiger Datensätze (was von einem Ausnahmehandler oder einem Shakespeare-Fan hätte erfolgen können):

insert into ext_log (message)
values('Shall I compare thee to a summer''s day?');
insert into ext_log (message)
values('Thou art more lovely and more temperate');

Die Ausgabe:

2015-10-07 15:19:03.4110Shall I compare thee to a summer's day?
2015-10-07 15:19:58.7600Thou art more lovely and more temperate

5.4.2. ALTER TABLE

Verwendet für

Ändern der Struktur einer Tabelle.

Verfügbar in

DSQL, ESQL

Syntax
ALTER TABLE tablename
  <operation> [, <operation> ...]

<operation> ::=
    ADD <col_def>
  | ADD <tconstraint>
  | DROP colname
  | DROP CONSTRAINT constr_name
  | ALTER [COLUMN] colname <col_mod>

<col_def> ::=
    <regular_col_def>
  | <computed_col_def>
  | <identity_col_def>

<regular_col_def> ::=
  colname {<datatype> | domainname}
  [DEFAULT {<literal> | NULL | <context_var>}]
  [<col_constraint> ...]
  [COLLATE collation_name]

<computed_col_def> ::=
  colname [{<datatype> | domainname}]
  {COMPUTED [BY] | GENERATED ALWAYS AS} (<expression>)

<identity_col_def> ::=
  colname {<datatype> | domainname}
  GENERATED BY DEFAULT AS IDENTITY [(START WITH startvalue)]
  [<col_constraint> ...]

<col_mod> ::=
    TO newname
  | POSITION newpos
  | <regular_col_mod>
  | <computed_col_mod>
  | <identity_col_mod>

<regular_col_mod> ::=
    TYPE {<datatype> | domainname}
  | SET DEFAULT {<literal> | NULL | <context_var>}
  | DROP DEFAULT
  | {SET | DROP} NOT NULL

<computed_col_mod> ::=
    [TYPE <datatype>] {COMPUTED [BY] | GENERATED ALWAYS AS} (<expression>)

<identity_col_mod> ::=
    RESTART [WITH startvalue]

!! Siehe auch CREATE TABLE-Syntax für weitere Regeln!!
Tabelle 27. ALTER TABLE-Anweisungsparameter
Parameter Beschreibung

tablename

Name (Bezeichner) der Tabelle

operation

Eine der verfügbaren Operationen, die die Struktur der Tabelle ändern

colname

Name (Bezeichner) für eine Spalte in der Tabelle, max. 31 Zeichen. Muss in der Tabelle eindeutig sein.

domain_name

Domainname

newname

Neuer Name (Bezeichner) für die Spalte, max. 31 Zeichen. Muss in der Tabelle eindeutig sein.

newpos

Die neue Spaltenposition (eine ganze Zahl zwischen 1 und der Anzahl der Spalten in der Tabelle)

start_value

Der erste Wert der Identitätsspalte nach dem Neustart

other_table

Der Name der Tabelle, auf die von der Fremdschlüsseleinschränkung verwiesen wird

literal

Ein Literalwert, der im angegebenen Kontext zulässig ist

context_var

Eine Kontextvariable, deren Typ im angegebenen Kontext zulässig ist

check_condition

Die Bedingung einer CHECK-Einschränkung, die erfüllt wird, wenn sie als TRUE oder UNKNOWN/NULL ausgewertet wird

collation

Name einer Kollatierungssequenz, die für charset_name gültig ist, wenn sie mit datatype versorgt wird oder ansonsten für den Standardzeichensatz der Datenbank gültig ist

Die Anweisung ALTER TABLE ändert die Struktur einer bestehenden Tabelle. Mit einer ALTER TABLE-Anweisung ist es möglich, mehrere Operationen auszuführen, Spalten und Einschränkungen hinzuzufügen/zu löschen und auch Spaltenspezifikationen zu ändern.

Mehrere Operationen in einer ALTER TABLE-Anweisung werden durch Kommas getrennt.

Versionsanzahl-Inkremente

Einige Änderungen in der Struktur einer Tabelle erhöhen den Metadaten-Änderungszähler (“version count”), der jeder Tabelle zugewiesen ist. Die Anzahl der Metadatenänderungen ist für jede Tabelle auf 255 begrenzt. Sobald der Zähler die Grenze von 255 erreicht hat, können Sie keine weiteren Änderungen an der Struktur der Tabelle vornehmen, ohne den Zähler zurückzusetzen.

So setzen Sie den Metadaten-Änderungszähler zurück
Sie müssen die Datenbank mit dem Dienstprogramm gbak sichern und wiederherstellen.
Die ADD-Klausel

Mit der ADD-Klausel können Sie eine neue Spalte oder eine neue Tabelleneinschränkung hinzufügen. Die Syntax zum Definieren der Spalte und die Syntax zum Definieren der Tabelleneinschränkung entsprechen denen, die für die CREATE TABLE-Anweisung beschrieben wurden.

Auswirkung auf die Versionsanzahl
  • Jedes Mal, wenn eine neue Spalte hinzugefügt wird, wird der Metadatenänderungszähler um eins erhöht

  • Das Hinzufügen einer neuen Tabelleneinschränkung erhöht nicht den Metadatenänderungszähler

Zu beachtende Punkte
  1. Das Hinzufügen einer Spalte mit einer NOT NULL-Einschränkung ohne einen DEFAULT-Wert wird - seit Firebird 3.0 - fehlschlagen, wenn die Tabelle bereits Zeilen enthält. Beim Hinzufügen einer Nicht-Nullable-Spalte wird empfohlen, entweder einen Standardwert dafür festzulegen oder sie als Nullable-fähig zu erstellen, die Spalte in vorhandenen Zeilen mit einem Nicht-Null-Wert zu aktualisieren und dann eine NOT NULL-Einschränkung hinzuzufügen.

  2. Wenn eine neue 'CHECK'-Beschränkung hinzugefügt wird, werden vorhandene Daten nicht auf Übereinstimmung getestet. Es wird empfohlen, vorhandene Daten vorab mit dem neuen 'CHECK'-Ausdruck zu testen.

  3. Obwohl das Hinzufügen einer Identitätsspalte unterstützt wird, ist dies nur erfolgreich, wenn die Tabelle leer ist. Das Hinzufügen einer Identitätsspalte schlägt fehl, wenn die Tabelle eine oder mehrere Zeilen enthält.

Die DROP-Klausel

Die Klausel DROP colname löscht die angegebene Spalte aus der Tabelle. Ein Versuch, eine Spalte zu löschen, schlägt fehl, wenn etwas darauf verweist. Betrachten Sie die folgenden Elemente als Quellen potenzieller Abhängigkeiten:

  • Spalten- oder Tabellenbeschränkungen

  • Indizes

  • gespeicherte Prozeduren und Trigger

  • Aufrufe

Auswirkung auf die Versionsanzahl
  • Jedes Mal, wenn eine Spalte gelöscht wird, wird der Metadaten-Änderungszähler der Tabelle um eins erhöht.

Die DROP CONSTRAINT-Klausel

Die Klausel DROP CONSTRAINT löscht die angegebene Einschränkung auf Spalten- oder Tabellenebene.

Eine PRIMARY KEY- oder UNIQUE-Schlüsseleinschränkung kann nicht gelöscht werden, wenn sie von einer FOREIGN KEY-Einschränkung in einer anderen Tabelle referenziert wird. Es wird notwendig sein, diese FOREIGN KEY-Beschränkung zu löschen, bevor versucht wird, die PRIMARY KEY- oder UNIQUE-Schlüssel-Beschränkung, auf die sie verweist, zu löschen.

Auswirkung auf die Versionsanzahl
  • Das Löschen einer Spalteneinschränkung oder einer Tabelleneinschränkung erhöht den Metadatenänderungszähler nicht.

Die ALTER [COLUMN]-Klausel

Mit der ALTER [COLUMN]-Klausel können Attribute vorhandener Spalten geändert werden, ohne dass die Spalte gelöscht und erneut hinzugefügt werden muss. Erlaubte Modifikationen sind:

  • den Namen ändern (hat keinen Einfluss auf den Metadaten-Änderungszähler)

  • den Datentyp ändern (erhöht den Metadaten-Änderungszähler um eins)

  • die Spaltenposition in der Spaltenliste der Tabelle ändern (hat keinen Einfluss auf den Metadaten-Änderungszähler)

  • den Standardspaltenwert löschen (hat keinen Einfluss auf den Metadaten-Änderungszähler)

  • einen Standardspaltenwert festlegen oder den vorhandenen Standardwert ändern (hat keinen Einfluss auf den Metadatenänderungszähler)

  • Typ und Ausdruck für eine berechnete Spalte ändern (hat keinen Einfluss auf den Metadaten-Änderungszähler)

  • Setzen Sie die Einschränkung NOT NULL (hat keinen Einfluss auf den Metadaten-Änderungszähler)

  • lösche die NOT NULL-Beschränkung (hat keinen Einfluss auf den Metadaten-Änderungszähler)

Umbenennen einer Spalte: die TO-Klausel

Das Schlüsselwort TO mit einem neuen Bezeichner benennt eine vorhandene Spalte um. Die Tabelle darf keine vorhandene Spalte mit demselben Bezeichner aufweisen.

Es ist nicht möglich, den Namen einer Spalte zu ändern, die in einer Einschränkung enthalten ist: PRIMARY KEY, UNIQUE-Schlüssel, FOREIGN KEY, Spaltenbeschränkung oder die CHECK-Beschränkung der Tabelle.

Das Umbenennen einer Spalte ist auch nicht zulässig, wenn die Spalte in einem Trigger, einer gespeicherten Prozedur oder einer Ansicht verwendet wird.

Ändern des Datentyps einer Spalte: die TYPE-Klausel

Das Schlüsselwort TYPE ändert den Datentyp einer existierenden Spalte in einen anderen zulässigen Typ. Eine Typänderung, die zu Datenverlust führen könnte, wird nicht zugelassen. Beispielsweise darf die Anzahl der Zeichen im neuen Typ für eine CHAR- oder VARCHAR-Spalte nicht kleiner sein als die dafür vorhandene Spezifikation.

Wurde die Spalte als Array deklariert, darf weder der Typ noch die Anzahl der Dimensionen geändert werden.

Der Datentyp einer Spalte, die an einem Fremdschlüssel, Primärschlüssel oder einer eindeutigen Einschränkung beteiligt ist, kann nicht geändert werden.

Ändern der Position einer Spalte: die POSITION-Klausel

Das Schlüsselwort POSITION ändert die Position einer vorhandenen Spalte im fiktiven "von links nach rechts"-Layout des Datensatzes.

Die Nummerierung der Spaltenpositionen beginnt bei 1.

  • Wenn eine Position kleiner als 1 angegeben wird, wird eine Fehlermeldung zurückgegeben

  • Wenn eine Positionsnummer größer als die Anzahl der Spalten in der Tabelle ist, wird ihre neue Position stillschweigend an die Anzahl der Spalten angepasst.

Die Klauseln DROP DEFAULT und SET DEFAULT

Die optionale DROP DEFAULT-Klausel löscht den Standardwert für die Spalte, wenn er zuvor durch eine CREATE TABLE- oder ALTER TABLE-Anweisung dort abgelegt wurde.

  • Wenn die Spalte auf einer Domäne mit einem Standardwert basiert, wird der Standardwert auf den Domänenstandard zurückgesetzt

  • Ein Ausführungsfehler wird ausgelöst, wenn versucht wird, den Standardwert einer Spalte zu löschen, die keinen Standardwert hat oder deren Standardwert domänenbasiert ist

Die optionale SET DEFAULT-Klausel setzt einen Standardwert für die Spalte. Wenn die Spalte bereits einen Standardwert hat, wird dieser durch den neuen ersetzt. Der auf eine Spalte angewendete Standardwert überschreibt immer einen von einer Domäne geerbten Wert.

Die Klauseln SET NOT NULL und DROP NOT NULL

Die SET NOT NULL-Klausel fügt einer vorhandenen Tabellenspalte eine NOT NULL-Einschränkung hinzu. Im Gegensatz zur Definition in CREATE TABLE ist die Angabe eines Constraint-Namens nicht möglich.

Das erfolgreiche Hinzufügen der NOT NULL-Einschränkung unterliegt einer vollständigen Datenvalidierung für die Tabelle. Stellen Sie daher sicher, dass die Spalte keine Nullen enthält, bevor Sie die Änderung vornehmen.

Eine explizite NOT NULL-Einschränkung für domänenbasierte Spalten überschreibt Domäneneinstellungen. In diesem Szenario erstreckt sich das Ändern der Domäne in NULL-Zulässigkeit nicht auf eine Tabellenspalte.

Das Löschen der NOT NULL-Beschränkung aus der Spalte, wenn ihr Typ eine Domäne ist, die auch eine NOT NULL-Beschränkung hat, hat keine beobachtbaren Auswirkungen, bis die NOT NULL-Beschränkung ebenfalls aus der Domäne gelöscht wird.

Die Klauseln COMPUTED [BY] oder GENERATED ALWAYS AS

Der einer berechneten Spalte zugrunde liegende Datentyp und Ausdruck können mit einer COMPUTED [BY]- oder GENERATED ALWAYS AS-Klausel in der ALTER TABLE ALTER [COLUMN]-Anweisung geändert werden. Das Konvertieren einer regulären Spalte in eine berechnete und umgekehrt ist nicht zulässig.

Identitätsspalten ändern

Für Identitätsspalten (GENERATED BY DEFAULT AS IDENTITY) ist es möglich, die zum Generieren von Identitätswerten verwendete Sequenz neu zu starten. Wenn nur die RESTART-Klausel angegeben wird, wird die Sequenz auf den bei CREATE TABLE angegebenen Anfangswert zurückgesetzt. Wenn die optionale WITH start_value-Klausel angegeben wird, wird die Sequenz mit dem angegebenen Wert neu gestartet.

Es ist nicht möglich, eine vorhandene Spalte in eine Identitätsspalte oder eine Identitätsspalte in eine normale Spalte umzuwandeln. Firebird 4 wird die Möglichkeit einführen, eine Identitätsspalte in eine normale Spalte umzuwandeln.

Der Neustart unterliegt derzeit einem Fehler: der erste nach einem Neustart generierte Wert ist 1 (eins) höher als der konfigurierte Anfangswert (oder der durch WITH angegebene Wert) .Siehe auch Identitätsspalten (autoinkrement).

Attribute, die nicht geändert werden können

Die folgenden Änderungen werden nicht unterstützt:

  • Ändern der Sortierung einer Zeichentypspalte

Wer kann eine Tabelle ändern?

Die ALTER TABLE-Anweisung kann ausgeführt werden durch:

  • Administratoren

  • Der Besitzer der Tabelle

  • Benutzer mit der Berechtigung ALTER ANY TABLE

Beispiele für die Verwendung von ALTER TABLE
  1. Hinzufügen der Spalte "CAPITAL" zur Tabelle "COUNTRY".

    ALTER TABLE COUNTRY
      ADD CAPITAL VARCHAR(25);
  2. Hinzufügen der Spalte "CAPITAL" mit den Einschränkungen "NOT NULL" und "UNIQUE" und Löschen der Spalte "CURRENCY".

    ALTER TABLE COUNTRY
      ADD CAPITAL VARCHAR(25) NOT NULL UNIQUE,
      DROP CURRENCY;
  3. Hinzufügen der Prüfbedingung CHK_SALARY und eines Fremdschlüssels zur Tabelle JOB.

    ALTER TABLE JOB
      ADD CONSTRAINT CHK_SALARY CHECK (MIN_SALARY < MAX_SALARY),
      ADD FOREIGN KEY (JOB_COUNTRY) REFERENCES COUNTRY (COUNTRY);
  4. Festlegen des Standardwerts für das Feld "MODEL", Ändern des Typs der Spalte "ITEMID" und Umbenennen der Spalte MODELNAME.

    ALTER TABLE STOCK
      ALTER COLUMN MODEL SET DEFAULT 1,
      ALTER COLUMN ITEMID TYPE BIGINT,
      ALTER COLUMN MODELNAME TO NAME;
  5. Neustart der Sequenz einer Identitätsspalte.

    ALTER TABLE objects
      ALTER ID RESTART WITH 100;
  6. Ändern der berechneten Spalten NEW_SALARY und SALARY_CHANGE.

    ALTER TABLE SALARY_HISTORY
      ALTER NEW_SALARY GENERATED ALWAYS AS
        (OLD_SALARY + OLD_SALARY * PERCENT_CHANGE / 100),
      ALTER SALARY_CHANGE COMPUTED BY
        (OLD_SALARY * PERCENT_CHANGE / 100);

5.4.3. DROP TABLE

Verwendet für

Löschen (Löschen) einer Tabelle

Verfügbar in

DSQL, ESQL

Syntax
DROP TABLE tablename
Tabelle 28. DROP TABLE-Anweisungsparameter
Parameter Beschreibung

tablename

Name (Bezeichner) der Tabelle

Die Anweisung DROP TABLE löscht (löscht) eine vorhandene Tabelle. Wenn die Tabelle Abhängigkeiten aufweist, schlägt die Anweisung DROP TABLE mit einem Ausführungsfehler fehl.

Wenn eine Tabelle gelöscht wird, werden auch alle ihre Trigger und Indizes gelöscht.

Wer kann eine Tabelle löschen?

Die DROP TABLE-Anweisung kann ausgeführt werden durch:

  • Administratoren

  • Der Besitzer der Tabelle

  • Benutzer mit dem Privileg DROP ANY TABLE

Beispiel für DROP TABLE
Löschen der 'COUNTRY'-Tabelle.
DROP TABLE COUNTRY;

5.4.4. RECREATE TABLE

Verwendet für

Erstellen einer neuen Tabelle (Relation) oder Wiederherstellen einer bestehenden Tabelle

Verfügbar in

DSQL

Syntax
RECREATE [GLOBAL TEMPORARY] TABLE tablename
  [EXTERNAL [FILE] 'filespec']
  (<col_def> [, {<col_def> | <tconstraint>} ...])
  [ON COMMIT {DELETE | PRESERVE} ROWS]

Siehe Abschnitt CREATE TABLE für die vollständige Syntax von CREATE TABLE und Beschreibungen zur Definition von Tabellen, Spalten und Einschränkungen.

RECREATE TABLE erstellt oder erstellt eine Tabelle neu. Existiert bereits eine Tabelle mit diesem Namen, versucht die Anweisung RECREATE TABLE, sie zu löschen und eine neue zu erstellen. Vorhandene Abhängigkeiten verhindern die Ausführung der Anweisung.

Beispiel für RECREATE TABLE
Erstellen oder Neuerstellen der Tabelle 'COUNTRY'.
RECREATE TABLE COUNTRY (
  COUNTRY COUNTRYNAME NOT NULL PRIMARY KEY,
  CURRENCY VARCHAR(10) NOT NULL
);
Siehe auch

CREATE TABLE, DROP TABLE

5.5. INDEX

Ein Index ist ein Datenbankobjekt, das zum schnelleren Abrufen von Daten aus einer Tabelle oder zum schnelleren Sortieren in einer Abfrage verwendet wird. Indizes werden auch verwendet, um die referenziellen Integritätsbedingungen PRIMARY KEY, FOREIGN KEY und UNIQUE zu erzwingen.

In diesem Abschnitt wird beschrieben, wie Sie Indizes erstellen, aktivieren und deaktivieren, löschen und Statistiken dazu sammeln (Selektivität neu berechnen).

5.5.1. CREATE INDEX

Verwendet für

Erstellen eines Index für eine Tabelle

Verfügbar in

DSQL, ESQL

Syntax
CREATE [UNIQUE] [ASC[ENDING] | DESC[ENDING]]
  INDEX indexname ON tablename
  {(col [, col …]) | COMPUTED BY (<expression>)}
Tabelle 29. CREATE INDEX-Anweisungsparameter
Parameter Beschreibung

indexname

Indexname. Es kann aus bis zu 31 Zeichen bestehen

tablename

Der Name der Tabelle, für die der Index erstellt werden soll

col

Name einer Spalte in der Tabelle. Spalten der Typen BLOB und ARRAY sowie berechnete Felder können nicht in einem Index verwendet werden

expression

Der Ausdruck, der die Werte für einen berechneten Index berechnet, auch als “Ausdrucksindex” bekannt

Die CREATE INDEX-Anweisung erstellt einen Index für eine Tabelle, mit dem das Suchen, Sortieren und Gruppieren beschleunigt werden kann. Indizes werden beim Definieren von Constraints wie Primärschlüssel-, Fremdschlüssel- oder Unique-Constraints automatisch erstellt.

Ein Index kann auf dem Inhalt von Spalten jedes Datentyps mit Ausnahme von 'BLOB' und Arrays erstellt werden. Der Name (Bezeichner) eines Index muss unter allen Indexnamen eindeutig sein.

Schlüsselindizes

Wenn einer Tabelle oder Spalte ein Primärschlüssel, Fremdschlüssel oder eine eindeutige Einschränkung hinzugefügt wird, wird automatisch ein Index mit demselben Namen ohne ausdrückliche Anweisung des Designers erstellt. Der Index PK_COUNTRY wird beispielsweise automatisch erstellt, wenn Sie die folgende Anweisung ausführen und festschreiben:

ALTER TABLE COUNTRY ADD CONSTRAINT PK_COUNTRY
  PRIMARY KEY (ID);
Wer kann einen Index erstellen?

Die CREATE INDEX-Anweisung kann ausgeführt werden durch:

  • Administratoren

  • Der Besitzer der Tabelle

  • Benutzer mit dem ALTER ANY TABLE-Privileg

Eindeutige Indizes

Die Angabe des Schlüsselworts UNIQUE in der Anweisung zur Indexerstellung erstellt einen Index, in dem die Eindeutigkeit in der gesamten Tabelle erzwungen wird. Der Index wird als „eindeutiger Index“ bezeichnet. Ein eindeutiger Index ist keine Einschränkung.

Eindeutige Indizes dürfen keine doppelten Schlüsselwerte (oder doppelte Schlüsselwertkombinationen im Fall von zusammengesetzten oder mehrspaltigen oder mehrsegmentigen) Indizes enthalten. Doppelte NULLs sind gemäß dem SQL:99-Standard sowohl in Einzelsegment- als auch in Mehrfachsegment-Indizes erlaubt.

Indexrichtung

Alle Indizes in Firebird sind unidirektional. Ein Index kann vom niedrigsten Wert zum höchsten (aufsteigende Reihenfolge) oder vom höchsten zum niedrigsten Wert (absteigende Reihenfolge) aufgebaut werden. Die Schlüsselwörter ASC[ENDING] und DESC[ENDING] werden verwendet, um die Richtung des Index anzugeben. Die Standardindexreihenfolge ist ASC[ENDING]. Es ist durchaus zulässig, sowohl einen aufsteigenden als auch einen absteigenden Index für dieselbe Spalte oder denselben Schlüsselsatz zu definieren.

Ein absteigender Index kann für eine Spalte nützlich sein, die nach den hohen Werten (""neuest", Maximum usw.) gesucht wird.

Firebird verwendet B-tree-Indizes, die bidirektional sind. Aufgrund technischer Einschränkungen verwendet Firebird jedoch einen Index nur in eine Richtung.

Berechnete (Ausdrucks-)Indizes

Beim Erstellen eines Index können Sie die COMPUTED BY-Klausel verwenden, um einen Ausdruck anstelle einer oder mehrerer Spalten anzugeben. Berechnete Indizes werden in Abfragen verwendet, bei denen die Bedingung in einer WHERE-, ORDER BY- oder GROUP BY-Klausel genau mit dem Ausdruck in der Indexdefinition übereinstimmt. Der Ausdruck in einem berechneten Index kann mehrere Spalten in der Tabelle umfassen.

Sie können tatsächlich einen berechneten Index für ein berechnetes Feld erstellen, aber ein solcher Index wird niemals verwendet.

Beschränkungen für Indizes

Für Indizes gelten bestimmte Grenzen.

Die maximale Länge eines Schlüssels in einem Index ist auf ¼ der Seitengröße begrenzt.

Maximale Indizes pro Tabelle

Die Anzahl der Indizes, die für jede Tabelle untergebracht werden können, ist begrenzt. Das tatsächliche Maximum für eine bestimmte Tabelle hängt von der Seitengröße und der Anzahl der Spalten in den Indizes ab.

Tabelle 30. Maximale Indizes pro Tabelle

Seitengröße (Page size)

Anzahl der Indizes abhängig von der Spaltenanzahl

Einspaltig

Zweispaltig

Dreispaltig

4096

203

145

113

8192

408

291

227

16384

818

584

454

Zeichenindexbeschränkungen

Die maximale Länge der indizierten Zeichenfolge beträgt 9 Byte weniger als die maximale Schlüssellänge. Die maximale Länge der indexierbaren Zeichenfolge hängt von der Seitengröße und dem Zeichensatz ab.

Tabelle 31. Maximale indexierbare (VAR)CHAR-Länge

Seitengröße (Page size)

Maximale Länge der indizierbaren Zeichenfolge nach Zeichensatztyp

1 Byte/Zeichen

2 Bytes/Zeichen

3 Bytes/Zeichen

4 Bytes/Zeichen

4096

1015

507

338

253

8192

2039

1019

679

509

16384

4087

2043

1362

1021

Beispiele für die Verwendung von CREATE INDEX
  1. Erstellen eines Index für die Spalte UPDATER_ID in der Tabelle SALARY_HISTORY

    CREATE INDEX IDX_UPDATER
      ON SALARY_HISTORY (UPDATER_ID);
  2. Erstellen eines Index mit in absteigender Reihenfolge sortierten Schlüsseln für die Spalte CHANGE_DATE in der Tabelle SALARY_HISTORY

    CREATE DESCENDING INDEX IDX_CHANGE
      ON SALARY_HISTORY (CHANGE_DATE);
  3. Erstellen eines Multi-Segment-Index für die Spalten ORDER_STATUS, PAID in der Tabelle SALES

    CREATE INDEX IDX_SALESTAT
      ON SALES (ORDER_STATUS, PAID);
  4. Erstellen eines Index, der keine doppelten Werte für die Spalte NAME in der Tabelle COUNTRY zulässt

    CREATE UNIQUE INDEX UNQ_COUNTRY_NAME
      ON COUNTRY (NAME);
  5. Erstellen eines berechneten Index für die Tabelle PERSONS

    CREATE INDEX IDX_NAME_UPPER ON PERSONS
      COMPUTED BY (UPPER (NAME));

    Ein Index wie dieser kann für eine Suche ohne Beachtung der Groß-/Kleinschreibung verwendet werden:

    SELECT *
    FROM PERSONS
    WHERE UPPER(NAME) STARTING WITH UPPER('Iv');
Siehe auch

ALTER INDEX, DROP INDEX

5.5.2. ALTER INDEX

Verwendet für

Aktivieren oder Deaktivieren eines Indexes; Neuerstellung eines Index

Verfügbar in

DSQL, ESQL

Syntax
ALTER INDEX indexname {ACTIVE | INACTIVE}
Tabelle 32. ALTER INDEX-Anweisungsparameter
Parameter Beschreibung

indexname

Indexname

Die ALTER INDEX-Anweisung aktiviert oder deaktiviert einen Index. Diese Anweisung bietet keine Möglichkeit, irgendwelche Attribute des Indexes zu ändern.

INAKTIV

Mit der Option INACTIVE wird der Index vom aktiven in den inaktiven Zustand geschaltet. Die Wirkung ist ähnlich wie bei der DROP INDEX-Anweisung, außer dass die Indexdefinition in der Datenbank verbleibt. Das Ändern eines Einschränkungsindex in den inaktiven Zustand ist nicht zulässig.

Ein aktiver Index kann deaktiviert werden, wenn keine Abfragen mit diesem Index vorbereitet sind; andernfalls wird ein Fehler “object in use” zurückgegeben.

Die Aktivierung eines inaktiven Index ist ebenfalls sicher. Wenn jedoch aktive Transaktionen vorhanden sind, die die Tabelle ändern, schlägt die Transaktion mit der Anweisung ALTER INDEX fehl, wenn sie das Attribut NOWAIT besitzt. Wenn sich die Transaktion im WAIT-Modus befindet, wartet sie auf den Abschluss gleichzeitiger Transaktionen.

Auf der anderen Seite der Medaille, wenn unser ALTER INDEX erfolgreich ist und beginnt, den Index bei COMMIT neu aufzubauen, werden andere Transaktionen, die diese Tabelle ändern, fehlschlagen oder warten, entsprechend ihren WAIT/NO WAIT-Attributen. Genauso verhält es sich mit CREATE INDEX.

Wofür ist es nützlich?

Es kann sinnvoll sein, einen Index in den inaktiven Zustand zu versetzen, während ein großer Satz von Datensätzen in der Tabelle, die den Index besitzt, eingefügt, aktualisiert oder gelöscht wird.

AKTIV

Mit der Option 'ACTIVE' wird der Index, wenn er sich im inaktiven Zustand befindet, in den aktiven Zustand geschaltet und das System baut den Index neu auf.

Wofür ist es nützlich?

Auch wenn der Index active ist, wenn ALTER INDEX …​ ACTIVE ausgeführt wird, wird der Index neu aufgebaut. Das Neuerstellen von Indizes kann ein nützliches Stück Haushaltsführung sein, um gelegentlich die Indizes einer großen Tabelle in einer Datenbank zu verwalten, die häufig eingefügt, aktualisiert oder gelöscht wird, aber selten wiederhergestellt wird.

Wer kann einen Index ändern?

Die ALTER INDEX-Anweisung kann ausgeführt werden durch:

  • Administratoren

  • Der Besitzer der Tabelle

  • Benutzer mit dem ALTER ANY TABLE-Privileg

Verwendung von ALTER INDEX für einen Einschränkungsindex

Das Ändern des Index eines PRIMARY KEY, FOREIGN KEY oder UNIQUE Constraints in INACTIVE ist nicht erlaubt. ALTER INDEX …​ ACTIVE funktioniert jedoch bei Constraint-Indizes genauso gut wie bei anderen, als Werkzeug zum Neuaufbau von Indizes.

ALTER INDEX-Beispiele
  1. Deaktivieren des IDX_UPDATER-Index

    ALTER INDEX IDX_UPDATER INACTIVE;
  2. Den IDX_UPDATER-Index zurück in den aktiven Zustand schalten und neu aufbauen

    ALTER INDEX IDX_UPDATER ACTIVE;

5.5.3. DROP INDEX

Verwendet für

Einen Index löschen (löschen)

Verfügbar in

DSQL, ESQL

Syntax
DROP INDEX indexname
Tabelle 33. DROP INDEX-Anweisungsparameter
Parameter Beschreibung

indexname

Indexname

Die DROP INDEX-Anweisung löscht (löscht) den benannten Index aus der Datenbank.

Ein Einschränkungsindex kann nicht mit DROP INDEX gelöscht werden. Constraint-Indizes werden während der Ausführung des Befehls ALTER TABLE …​ DROP CONSTRAINT …​ gelöscht.

Wer kann einen Index löschen?

Die DROP INDEX-Anweisung kann ausgeführt werden durch:

  • Administratoren

  • Der Besitzer der Tabelle

  • Benutzer mit dem ALTER ANY TABLE-Privileg

DROP INDEX-Beispiel
Löschen des IDX_UPDATER-Index
DROP INDEX IDX_UPDATER;

5.5.4. SET STATISTICS

Verwendet für

Neuberechnung der Selektivität eines Index

Verfügbar in

DSQL, ESQL

Syntax
SET STATISTICS INDEX indexname
Tabelle 34. SET STATISTICS-Anweisungsparameter
Parameter Beschreibung

indexname

Indexname

Die Anweisung SET STATISTICS berechnet die Selektivität des angegebenen Index neu.

Wer kann Indexstatistiken aktualisieren?

Die Anweisung SET STATISTICS kann ausgeführt werden durch:

  • Administratoren

  • Der Besitzer der Tabelle

  • Benutzer mit dem ALTER ANY TABLE-Privileg

Indexselektivität

Die Selektivität eines Index ergibt sich aus der Auswertung der Anzahl der Zeilen, die bei einer Suche nach jedem Indexwert ausgewählt werden können. Ein eindeutiger Index hat die maximale Selektivität, da es unmöglich ist, mehr als eine Zeile für jeden Wert eines Indexschlüssels auszuwählen, wenn dieser verwendet wird. Die Selektivität eines Index auf dem neuesten Stand zu halten ist wichtig für die Auswahl des Optimierers bei der Suche nach dem optimalsten Abfrageplan.

Indexstatistiken in Firebird werden als Reaktion auf große Mengen von Einfügungen, Aktualisierungen oder Löschungen nicht automatisch neu berechnet. Es kann von Vorteil sein, die Selektivität eines Index nach solchen Operationen neu zu berechnen, da die Selektivität dazu neigt, veraltet zu werden.

Die Anweisungen CREATE INDEX und ALTER INDEX ACTIVE speichern beide Indexstatistiken, die vollständig dem Inhalt des neu erstellten Index entsprechen.

Es kann unter gleichzeitiger Last ohne Beschädigungsrisiko ausgeführt werden. Beachten Sie jedoch, dass die neu berechneten Statistiken bei gleichzeitiger Belastung veraltet sein können, sobald SET STATISTICS beendet ist.

Beispiel für die Verwendung von SET STATISTICS
Neuberechnung der Selektivität des Indexes IDX_UPDATER
SET STATISTICS INDEX IDX_UPDATER;

5.6. VIEW

Eine Ansicht (View) ist eine virtuelle Tabelle, die eigentlich eine gespeicherte und benannte SELECT-Abfrage zum Abrufen von Daten beliebiger Komplexität ist. Daten können aus einer oder mehreren Tabellen, aus anderen Ansichten und auch aus auswählbaren gespeicherten Prozeduren abgerufen werden.

Im Gegensatz zu regulären Tabellen in relationalen Datenbanken ist eine Ansicht kein unabhängiger Datensatz, der in der Datenbank gespeichert ist. Das Ergebnis wird bei Auswahl der Ansicht dynamisch als Datensatz erstellt.

Die Metadaten einer View stehen dem Prozess zur Verfügung, der den Binärcode für Stored Procedures und Trigger generiert, als wären es konkrete Tabellen, die persistente Daten speichern.

5.6.1. CREATE VIEW

Verwendet für

Erstellen einer Ansicht

Verfügbar in

DSQL

Syntax
CREATE VIEW viewname [<full_column_list>]
  AS <select_statement>
  [WITH CHECK OPTION]

<full_column_list> ::= (colname [, colname ...])
Tabelle 35. CREATE VIEW-Anweisungsparameter
Parameter Beschreibung

viewname

Name der Ansicht (View), maximal 31 Zeichen

select_statement

SELECT-Anweisung

full_column_list

Die Liste der Spalten in der Ansicht

colname

Spaltennamen anzeigen. Doppelte Spaltennamen sind nicht zulässig.

Die Anweisung CREATE VIEW erstellt eine neue Ansicht. Der Bezeichner (Name) einer Ansicht muss unter den Namen aller Ansichten, Tabellen und gespeicherten Prozeduren in der Datenbank eindeutig sein.

Auf den Namen der neuen Ansicht kann die Liste der Spaltennamen folgen, die beim Aufrufen der Ansicht an den Aufrufer zurückgegeben werden sollen. Namen in der Liste müssen sich nicht auf die Namen der Spalten in den Basistabellen beziehen, von denen sie abgeleitet sind.

Wenn die Ansichtsspaltenliste weggelassen wird, verwendet das System die Spaltennamen und/oder Aliase aus der SELECT-Anweisung. Wenn doppelte Namen oder ausdrucksabgeleitete Spalten ohne Alias das Abrufen einer gültigen Liste unmöglich machen, schlägt die Erstellung der Ansicht mit einem Fehler fehl.

Die Anzahl der Spalten in der Liste der View muss genau mit der Anzahl der Spalten in der Auswahlliste der zugrunde liegenden SELECT-Anweisung in der View-Definition übereinstimmen.

Zusätzliche Punkte
  • Wenn die vollständige Liste der Spalten angegeben ist, macht es keinen Sinn, Aliase in der SELECT-Anweisung anzugeben, da die Namen in der Spaltenliste diese überschreiben

  • Die Spaltenliste ist optional, wenn alle Spalten im SELECT explizit benannt und in der Auswahlliste eindeutig sind

Aktualisierbare Ansichten

Eine Ansicht kann aktualisierbar oder schreibgeschützt sein. Wenn eine View aktualisierbar ist, können die beim Aufruf dieser View abgerufenen Daten durch die DML-Anweisungen INSERT, UPDATE, DELETE, UPDATE OR INSERT oder MERGE geändert werden. In einer aktualisierbaren Ansicht vorgenommene Änderungen werden auf die zugrunde liegende(n) Tabelle(n) angewendet.

Eine schreibgeschützte Ansicht kann mithilfe von Triggern aktualisierbar gemacht werden. Sobald Trigger für eine Ansicht definiert wurden, werden an sie gesendete Änderungen nie automatisch in die zugrunde liegende Tabelle geschrieben, selbst wenn die Ansicht von Anfang an aktualisierbar war. Es liegt in der Verantwortung des Programmierers sicherzustellen, dass die Trigger die Basistabellen nach Bedarf aktualisieren (oder aus ihnen löschen oder einfügen).

Eine Ansicht ist automatisch aktualisierbar, wenn alle folgenden Bedingungen erfüllt sind:

  • die SELECT-Anweisung fragt nur eine Tabelle oder eine aktualisierbare Ansicht ab

  • die SELECT-Anweisung ruft keine gespeicherten Prozeduren auf

  • jede nicht in der Ansichtsdefinition vorhandene Spalte der Basistabelle (oder Basisansicht) erfüllt eine der folgenden Bedingungen:

    • es ist nullable

    • es hat einen Nicht-NULL-Standardwert

    • es hat einen Trigger, der einen zulässigen Wert liefert

  • die SELECT-Anweisung enthält keine Felder, die von Unterabfragen oder anderen Ausdrücken abgeleitet sind

  • die SELECT-Anweisung enthält keine Felder, die durch Aggregatfunktionen (MIN, MAX, AVG, SUM, COUNT, LIST usw.), statistische Funktionen (CORR, COVAR_POP, COVAR_SAMP, etc.), lineare Regressionsfunktionen (REGR_AVGX, REGR_AVGY, etc.) oder jede Art von Fensterfunktion

  • die SELECT-Anweisung enthält keine ORDER BY-, GROUP BY- oder HAVING-Klausel

  • die SELECT-Anweisung enthält weder das Schlüsselwort DISTINCT noch zeilenbeschränkende Schlüsselwörter wie ROWS, FIRST, SKIP, OFFSET oder FETCH

WITH CHECK OPTION

Die optionale WITH CHECK OPTION-Klausel erfordert eine aktualisierbare Ansicht, um zu prüfen, ob neue oder aktualisierte Daten die in der WHERE-Klausel der SELECT-Anweisung angegebene Bedingung erfüllen. Bei jedem Versuch, einen neuen Datensatz einzufügen oder einen bestehenden zu aktualisieren, wird geprüft, ob der neue oder aktualisierte Datensatz die 'WHERE'-Kriterien erfüllen würde. Wenn sie die Prüfung nicht bestehen, wird die Operation nicht ausgeführt und eine entsprechende Fehlermeldung zurückgegeben.

WITH CHECK OPTION kann nur in einer CREATE VIEW-Anweisung angegeben werden, in der eine WHERE-Klausel vorhanden ist, um die Ausgabe der SELECT-Hauptanweisung einzuschränken. Andernfalls wird eine Fehlermeldung zurückgegeben.

Bitte beachten Sie:

Wenn WITH CHECK OPTION verwendet wird, prüft die Engine die Eingabe gegen die WHERE-Klausel, bevor sie irgendetwas an die Basisrelation übergibt. Wenn die Prüfung der Eingabe fehlschlägt, werden daher keine Standardklauseln oder Trigger für die Basisrelation, die möglicherweise zur Korrektur der Eingabe entworfen wurden, in Aktion treten.

Darüber hinaus werden View-Felder, die in der INSERT-Anweisung weggelassen wurden, als NULLs an die Basisrelation übergeben, unabhängig davon, ob sie in der WHERE-Klausel vorhanden oder nicht vorhanden sind. Daher werden die für solche Felder definierten Basistabellen-Standardwerte nicht angewendet. Trigger hingegen werden wie erwartet ausgelöst und funktionieren.

Bei Ansichten, die nicht über WITH CHECK OPTION verfügen, werden Felder, die in der INSERT-Anweisung weggelassen wurden, überhaupt nicht an die Basisrelation übergeben, sodass alle Standardwerte angewendet werden.

Wer kann eine Ansicht erstellen?

Die CREATE VIEW-Anweisung kann ausgeführt werden durch:

Der Ersteller einer Ansicht wird ihr Eigentümer.

Um eine Ansicht zu erstellen, benötigt ein Nicht-Administrator-Benutzer außerdem mindestens 'SELECT'-Zugriff auf die zugrunde liegende(n) Tabelle(n) und/oder Ansicht(en) und das 'EXECUTE'-Privileg für alle beteiligten auswählbaren gespeicherten Prozeduren.

Um Einfügungen, Aktualisierungen und Löschungen über die Ansicht zu ermöglichen, muss der Ersteller/Eigentümer auch die entsprechenden INSERT, UPDATE und DELETE-Rechte für das/die zugrunde liegende(n) Objekt(e) besitzen.

Anderen Benutzern Berechtigungen für die Ansicht zu erteilen ist nur möglich, wenn der Ansichtsbesitzer diese Berechtigungen für die zugrunde liegenden Objekte hat WITH GRANT OPTION. Dies ist immer dann der Fall, wenn der View-Eigentümer auch der Eigentümer der zugrunde liegenden Objekte ist.

===Beispiele für das Erstellen von Ansichten

  1. Erstellen einer Ansicht, die die Spalten JOB_CODE und JOB_TITLE nur für die Jobs zurückgibt, bei denen MAX_SALARY weniger als 15.000 USD beträgt.

    CREATE VIEW ENTRY_LEVEL_JOBS AS
    SELECT JOB_CODE, JOB_TITLE
    FROM JOB
    WHERE MAX_SALARY < 15000;
  2. Erstellen einer Ansicht, die die Spalten JOB_CODE und JOB_TITLE nur für Jobs zurückgibt, bei denen MAX_SALARY weniger als 15.000 USD beträgt. Immer wenn ein neuer Datensatz eingefügt oder ein vorhandener Datensatz aktualisiert wird, wird die Bedingung MAX_SALARY < 15000 geprüft. Wenn die Bedingung nicht wahr ist, wird die Einfüge-/Aktualisierungsoperation abgelehnt.

    CREATE VIEW ENTRY_LEVEL_JOBS AS
    SELECT JOB_CODE, JOB_TITLE
    FROM JOB
    WHERE MAX_SALARY < 15000
    WITH CHECK OPTION;
  3. Erstellen einer Ansicht mit einer expliziten Spaltenliste.

    CREATE VIEW PRICE_WITH_MARKUP (
      CODE_PRICE,
      COST,
      COST_WITH_MARKUP
    ) AS
    SELECT
      CODE_PRICE,
      COST,
      COST * 1.1
    FROM PRICE;
  4. Erstellen einer View mit Hilfe von Aliasen für Felder in der SELECT-Anweisung (das gleiche Ergebnis wie in Beispiel 3).

    CREATE VIEW PRICE_WITH_MARKUP AS
    SELECT
      CODE_PRICE,
      COST,
      COST * 1.1 AS COST_WITH_MARKUP
    FROM PRICE;
  5. Erstellen einer schreibgeschützten Ansicht basierend auf zwei Tabellen und einer gespeicherten Prozedur.

    CREATE VIEW GOODS_PRICE AS
    SELECT
      goods.name AS goodsname,
      price.cost AS cost,
      b.quantity AS quantity
    FROM
      goods
      JOIN price ON goods.code_goods = price.code_goods
      LEFT JOIN sp_get_balance(goods.code_goods) b ON 1 = 1;

5.6.2. ALTER VIEW

Verwendet für

Ändern einer vorhandenen Ansicht

Verfügbar in

DSQL

Syntax
ALTER VIEW viewname [<full_column_list>]
    AS <select_statement>
    [WITH CHECK OPTION]

<full_column_list> ::= (colname [, colname ...])
Tabelle 36. ALTER VIEW-Anweisungsparameter
Parameter Beschreibung

viewname

Name einer existierenden Ansicht

select_statement

SELECT-Anweisung

full_column_list

Die Liste der Spalten in der Ansicht

colname

Spaltennamen anzeigen. Doppelte Spaltennamen sind nicht zulässig.

Verwenden Sie die Anweisung ALTER VIEW, um die Definition einer bestehenden Ansicht zu ändern. Berechtigungen für Ansichten bleiben erhalten und Abhängigkeiten werden nicht beeinflusst.

Die Syntax der ALTER VIEW-Anweisung entspricht vollständig der von CREATE VIEW.

Seien Sie vorsichtig, wenn Sie die Anzahl der Spalten in einer Ansicht ändern. Vorhandener Anwendungscode und PSQL-Module, die auf die Ansicht zugreifen, können ungültig werden. Informationen zum Erkennen dieser Art von Problem in gespeicherten Prozeduren und Triggern finden Sie unter Das RDB$VALID_BLR-Feld im Anhang.

Wer kann eine Ansicht ändern?

Die ALTER VIEW-Anweisung kann ausgeführt werden durch:

  • Administratoren

  • Der Besitzer der Ansicht

  • Benutzer mit der Berechtigung ALTER ANY VIEW

Beispiel mit ALTER VIEW
Ändern der Ansicht PRICE_WITH_MARKUP
ALTER VIEW PRICE_WITH_MARKUP (
  CODE_PRICE,
  COST,
  COST_WITH_MARKUP
) AS
SELECT
  CODE_PRICE,
  COST,
  COST * 1.15
FROM PRICE;

5.6.3. CREATE OR ALTER VIEW

Verwendet für

Erstellen einer neuen Ansicht oder Ändern einer vorhandenen Ansicht.

Verfügbar in

DSQL

Syntax
CREATE OR ALTER VIEW viewname [<full_column_list>]
  AS <select_statement>
  [WITH CHECK OPTION]

<full_column_list> ::= (colname [, colname ...])
Tabelle 37. CREATE OR ALTER VIEW-Anweisungsparameter
Parameter Beschreibung

viewname

Name einer Ansicht, die möglicherweise nicht vorhanden ist

select_statement

SELECT-Anweisung

full_column_list

Die Liste der Spalten in der Ansicht

colname

Spaltennamen anzeigen. Doppelte Spaltennamen sind nicht zulässig.

Verwenden Sie die Anweisung CREATE OR ALTER VIEW, um die Definition einer bestehenden Ansicht zu ändern oder sie zu erstellen, falls sie nicht existiert. Berechtigungen für eine vorhandene Ansicht bleiben erhalten und Abhängigkeiten werden nicht beeinflusst.

Die Syntax der CREATE OR ALTER VIEW-Anweisung entspricht vollständig der von CREATE VIEW.

Beispiel für CREATE OR ALTER VIEW
Erstellen der neuen Ansicht PRICE_WITH_MARKUP-Ansicht oder Ändern, wenn sie bereits vorhanden ist
CREATE OR ALTER VIEW PRICE_WITH_MARKUP (
  CODE_PRICE,
  COST,
  COST_WITH_MARKUP
) AS
SELECT
  CODE_PRICE,
  COST,
  COST * 1.15
FROM PRICE;

5.6.4. DROP VIEW

Verwendet für

Löschen einer Ansicht

Verfügbar in

DSQL

Syntax
DROP VIEW viewname
Tabelle 38. DROP VIEW-Anweisungsparameter
Parameter Beschreibung

viewname

Name der Ansicht

Die DROP VIEW-Anweisung löscht (löscht) eine vorhandene Ansicht. Die Anweisung schlägt fehl, wenn die Ansicht Abhängigkeiten aufweist.

Wer kann eine Ansicht löschen?

Die DROP VIEW-Anweisung kann ausgeführt werden durch:

  • Administratoren

  • Der Besitzer der Ansicht

  • Benutzer mit dem Privileg DROP ANY VIEW

Beispiel
Löschen der Ansicht PRICE_WITH_MARKUP
DROP VIEW PRICE_WITH_MARKUP;

5.6.5. RECREATE VIEW

Verwendet für

Erstellen einer neuen Ansicht oder Neuerstellen einer vorhandenen Ansicht

Verfügbar in

DSQL

Syntax
RECREATE VIEW viewname [<full_column_list>]
  AS <select_statement>
  [WITH CHECK OPTION]

<full_column_list> ::= (colname [, colname ...])
Tabelle 39. RECREATE VIEW-Anweisungsparameter
Parameter Beschreibung

viewname

Name der Ansicht (View), maximal 31 Zeichen

select_statement

SELECT-Anweisung

full_column_list

Die Liste der Spalten in der Ansicht

colname

Spaltennamen anzeigen. Doppelte Spaltennamen sind nicht zulässig.

Erstellt eine Ansicht oder erstellt sie neu. Wenn bereits eine Ansicht mit diesem Namen vorhanden ist, versucht die Engine, sie zu löschen, bevor die neue Instanz erstellt wird. Wenn die vorhandene Ansicht nicht gelöscht werden kann, z. B. aufgrund von Abhängigkeiten oder unzureichenden Rechten, schlägt RECREATE VIEW mit einem Fehler fehl.

Beispiel für RECREATE VIEW
Neue Ansicht PRICE_WITH_MARKUP-Ansicht erstellen oder neu erstellen, falls bereits vorhanden
RECREATE VIEW PRICE_WITH_MARKUP (
  CODE_PRICE,
  COST,
  COST_WITH_MARKUP
) AS
SELECT
  CODE_PRICE,
  COST,
  COST * 1.15
FROM PRICE;

5.7. TRIGGER

Ein Trigger ist eine spezielle Art einer gespeicherten Prozedur, die nicht direkt aufgerufen wird, sondern ausgeführt wird, wenn ein angegebenes Ereignis in der zugeordneten Tabelle oder Sicht auftritt. Ein DML-Trigger ist spezifisch für eine und nur eine Beziehung (Tabelle oder Ansicht) und eine Phase im Timing des Ereignisses (BEFORE oder AFTER). Es kann für ein bestimmtes Ereignis (insert, update, delete) oder für eine Kombination von zwei oder drei dieser Ereignisse ausgeführt werden.

Es gibt zwei andere Formen von Auslösern:

  1. ein “Datenbank-Trigger” kann angegeben werden, um zu Beginn oder am Ende einer Benutzersitzung (Verbindung) oder einer Benutzertransaktion auszulösen.

  2. ein “DDL-Trigger” kann angegeben werden, um vor oder nach der Ausführung einer oder mehrerer Typen von DDL-Anweisungen auszulösen.

5.7.1. CREATE TRIGGER

Verwendet für

Einen neuen Trigger erstellen

Verfügbar in

DSQL, ESQL

Syntax
CREATE TRIGGER trigname
  { <relation_trigger_legacy>
  | <relation_trigger_sql2003>
  | <database_trigger>
  | <ddl_trigger> }
  <module-body>

<module-body> ::=
  !! Siehe auch Syntax des Modul-Bodys !!

<relation_trigger_legacy> ::=
  FOR {tablename | viewname}
  [ACTIVE | INACTIVE]
  {BEFORE | AFTER} <mutation_list>
  [POSITION number]

<relation_trigger_sql2003> ::=
  [ACTIVE | INACTIVE]
  {BEFORE | AFTER} <mutation_list>
  [POSITION number]
  ON {tablename | viewname}

<database_trigger> ::=
  [ACTIVE | INACTIVE] ON <db_event>
  [POSITION number]

<ddl_trigger> ::=
  [ACTIVE | INACTIVE]
  {BEFORE | AFTER} <ddl_event>
  [POSITION number]

<mutation_list> ::=
  <mutation> [OR <mutation> [OR <mutation>]]

<mutation> ::= INSERT | UPDATE | DELETE

<db_event> ::=
    CONNECT | DISCONNECT
  | TRANSACTION {START | COMMIT | ROLLBACK}

<ddl_event> ::=
    ANY DDL STATEMENT
  | <ddl_event_item> [{OR <ddl_event_item>} ...]

<ddl_event_item> ::=
    {CREATE | ALTER | DROP} TABLE
  | {CREATE | ALTER | DROP} PROCEDURE
  | {CREATE | ALTER | DROP} FUNCTION
  | {CREATE | ALTER | DROP} TRIGGER
  | {CREATE | ALTER | DROP} EXCEPTION
  | {CREATE | ALTER | DROP} VIEW
  | {CREATE | ALTER | DROP} DOMAIN
  | {CREATE | ALTER | DROP} ROLE
  | {CREATE | ALTER | DROP} SEQUENCE
  | {CREATE | ALTER | DROP} USER
  | {CREATE | ALTER | DROP} INDEX
  | {CREATE | DROP} COLLATION
  | ALTER CHARACTER SET
  | {CREATE | ALTER | DROP} PACKAGE
  | {CREATE | DROP} PACKAGE BODY
  | {CREATE | ALTER | DROP} MAPPING
Tabelle 40. CREATE TRIGGER-Anweisgungsparameter
Parameter Beschreibung

trigname

Triggername bestehend aus bis zu 31 Zeichen. Er muss unter allen Triggernamen in der Datenbank eindeutig sein.

relation_trigger_legacy

Legacy-Stil der Trigger-Deklaration für einen Relations-Trigger

relation_trigger_sql2003

Relations-Trigger-Deklaration gemäß SQL:2003-Standard

database_trigger

Datenbank-Trigger-Deklaration

tablename

Name der Tabelle, mit der der Relationstrigger verknüpft ist

viewname

Name der Ansicht, mit der der Relationstrigger verknüpft ist

mutation_list

Liste der Beziehungsereignisse (Tabellenansicht |)

number

Position des Abzugs in der Schussfolge. Von 0 bis 32.767

db_event

Verbindungs- oder Transaktionsereignis

ddl_event

Liste der Metadatenänderungsereignisse

ddl_event_item

Eines der Metadatenänderungsereignisse

Die Anweisung CREATE TRIGGER wird verwendet, um einen neuen Trigger zu erstellen. Ein Trigger kann entweder für ein Relation (Tabelle | Ansicht) Ereignis (oder eine Kombination von Ereignissen), für ein Datenbankereignis oder für ein DDL Ereignis erstellt werden.

CREATE TRIGGER ist zusammen mit seinen assoziierten ALTER TRIGGER, CREATE OR ALTER TRIGGER und RECREATE TRIGGER eine zusammengesetzte Anweisung, bestehend aus einem Header und einem Body. Der Header gibt den Namen des Triggers, den Namen der Beziehung (bei einem DML-Trigger), die Phase des Triggers, die Ereignisse, auf die er angewendet wird, und die Position an, um eine Reihenfolge zwischen den Triggern zu bestimmen.

Der Triggerrumpf besteht aus optionalen Deklarationen lokaler Variablen und benannten Cursors gefolgt von einer oder mehreren Anweisungen oder Anweisungsblöcken, die alle in einem äußeren Block eingeschlossen sind, der mit dem Schlüsselwort BEGIN beginnt und mit dem Schlüsselwort END endet. Deklarationen und eingebettete Anweisungen werden mit Semikolons (‘;’) abgeschlossen.

Der Name des Triggers muss unter allen Triggernamen eindeutig sein.

Statement-Terminatoren

Einige SQL-Anweisungseditoren – insbesondere das mit Firebird gelieferte Dienstprogramm isql und möglicherweise einige Editoren von Drittanbietern – verwenden eine interne Konvention, die erfordert, dass alle Anweisungen mit einem Semikolon abgeschlossen werden. Dies führt beim Codieren in diesen Umgebungen zu einem Konflikt mit der PSQL-Syntax. Wenn Sie dieses Problem und seine Lösung nicht kennen, lesen Sie bitte die Details im PSQL-Kapitel im Abschnitt Terminator in isql umschalten.

Externe UDR-Trigger

Ein Trigger kann sich auch in einem externen Modul befinden. In diesem Fall spezifiziert CREATE TRIGGER anstelle eines Trigger-Bodys die Position des Triggers im externen Modul mit der EXTERNAL-Klausel. Die optionale NAME-Klausel spezifiziert den Namen des externen Moduls, den Namen des Triggers innerhalb des Moduls und – optional – benutzerdefinierte Informationen. Die erforderliche ENGINE-Klausel gibt den Namen der UDR-Engine an, die die Kommunikation zwischen Firebird und dem externen Modul handhabt. Die optionale AS-Klausel akzeptiert ein String-Literal “body”, das von der Engine oder dem Modul für verschiedene Zwecke verwendet werden kann.

DML-Triggers (auf Tabellen oder Ansichten)

DML- oder “relation”-Trigger werden auf Zeilen-(Datensatz-)Ebene ausgeführt, jedes Mal, wenn sich das Zeilenbild ändert. Ein Trigger kann entweder 'AKTIV' oder 'INAKTIV' sein. Es werden nur aktive Trigger ausgeführt. Trigger werden standardmäßig als 'AKTIV' erstellt.

Wer kann einen DML-Trigger erstellen?

DML-Trigger können erstellt werden durch:

  • Administratoren

  • Der Besitzer der Tabelle (oder Ansicht)

  • Benutzer mit dem ALTER ANY TABLE- oder — für eine Ansicht — ALTER ANY VIEW-Privileg

Formulare der Erklärung

Firebird unterstützt zwei Deklarationsformen für Relations-Trigger:

  • Die ursprüngliche, veraltete Syntax

  • Das SQL:2003 standardkonforme Formular (empfohlen)

Das mit SQL:2003 standardkonforme Formular wird empfohlen.

Ein Relationstrigger spezifiziert — unter anderem — eine Phase und ein oder mehrere Ereignisse.

Phase

Phase betrifft das Timing des Triggers in Bezug auf das Change-of-State-Ereignis in der Datenzeile:

  • Ein BEFORE-Trigger wird ausgelöst, bevor die angegebene Datenbankoperation (insert, update oder delete) ausgeführt wird

  • Ein 'AFTER'-Trigger wird ausgelöst, nachdem die Datenbankoperation abgeschlossen ist

Zeilenereignis

Eine Relations-Trigger-Definition spezifiziert mindestens eine der DML-Operationen 'INSERT', 'UPDATE' und 'DELETE', um ein oder mehrere Ereignisse anzugeben, bei denen der Trigger ausgelöst werden soll. Werden mehrere Operationen angegeben, müssen diese durch das Schlüsselwort OR getrennt werden. Keine Operation darf mehr als einmal erfolgen.

Innerhalb des Anweisungsblocks die booleschen Kontextvariablen INSERTING, UPDATING und DELETING kann verwendet werden, um zu testen, welche Operation gerade ausgeführt wird.

Auslösereihenfolge der Auslöser

Das Schlüsselwort POSITION ermöglicht die Angabe einer optionalen Ausführungsreihenfolge (“firing order”) für eine Reihe von Triggern, die die gleiche Phase und das gleiche Ereignis wie ihr Ziel haben. Die Standardposition ist 0. Wenn keine Positionen angegeben sind oder mehrere Trigger eine einzige Positionsnummer haben, werden die Trigger in alphabetischer Reihenfolge ihrer Namen ausgeführt.

Variablendeklarationen

Der optionale Deklarationsabschnitt unter dem Schlüsselwort AS in der Kopfzeile des Triggers dient zum Definieren von Variablen und benannten Cursorn, die lokal für den Trigger sind. Weitere Informationen finden Sie unter DECLARE VARIABLE und DECLARE CURSOR im Prozedurales SQL Kapitel.

Der Trigger-Body

Die lokalen Deklarationen (sofern vorhanden) sind der letzte Teil des Header-Abschnitts eines Triggers. Es folgt der Triggerrumpf, wobei ein oder mehrere Blöcke von PSQL-Anweisungen in eine Struktur eingeschlossen werden, die mit dem Schlüsselwort BEGIN beginnt und mit dem Schlüsselwort END endet.

Nur der Eigentümer der Ansicht oder Tabelle und Administratoren haben die Berechtigung, CREATE TRIGGER zu verwenden.

Beispiele für CREATE TRIGGER für Tabellen und Ansichten
  1. Erstellen eines Triggers in “legacy”-Form, der ausgelöst wird, bevor ein neuer Datensatz in die Tabelle CUSTOMER eingefügt wird.

    CREATE TRIGGER SET_CUST_NO FOR CUSTOMER
    ACTIVE BEFORE INSERT POSITION 0
    AS
    BEGIN
      IF (NEW.CUST_NO IS NULL) THEN
        NEW.CUST_NO = GEN_ID(CUST_NO_GEN, 1);
    END
  2. Erstellen einer Triggerauslösung vor dem Einfügen eines neuen Datensatzes in die CUSTOMER-Tabelle in SQL:2003-Standard-konformer Form.

    CREATE TRIGGER set_cust_no
    ACTIVE BEFORE INSERT POSITION 0 ON customer
    AS
    BEGIN
      IF (NEW.cust_no IS NULL) THEN
        NEW.cust_no = GEN_ID(cust_no_gen, 1);
    END
  3. Erstellen eines Triggers, der nach dem Einfügen, Aktualisieren oder Löschen eines Datensatzes in der CUSTOMER-Tabelle ausgelöst wird.

    CREATE TRIGGER TR_CUST_LOG
    ACTIVE AFTER INSERT OR UPDATE OR DELETE POSITION 10
    ON CUSTOMER
    AS
    BEGIN
      INSERT INTO CHANGE_LOG (LOG_ID,
                              ID_TABLE,
                              TABLE_NAME,
                              MUTATION)
      VALUES (NEXT VALUE FOR SEQ_CHANGE_LOG,
              OLD.CUST_NO,
              'CUSTOMER',
              CASE
                WHEN INSERTING THEN 'INSERT'
                WHEN UPDATING  THEN 'UPDATE'
                WHEN DELETING  THEN 'DELETE'
              END);
    END
Datenbank-Trigger

Trigger können so definiert werden, dass sie bei “Datenbankereignissen” ausgelöst werden, was sich wirklich auf eine Mischung aus Ereignissen bezieht, die über den Umfang einer Sitzung (Verbindung) und Ereignissen, die über den Umfang einer einzelnen Transaktion hinweg wirken:

  • CONNECT

  • DISCONNECT

  • TRANSACTION START

  • TRANSACTION COMMIT

  • TRANSACTION ROLLBACK

DDL-Trigger sind eine Unterart von Datenbank-Triggern, die in einem separaten Abschnitt behandelt werden.

Wer kann einen Datenbank-Trigger erstellen?

Datenbank-Trigger können erstellt werden durch:

Ausführung von Datenbank-Triggern und Ausnahmebehandlung

Die Trigger CONNECT und DISCONNECT werden in einer eigens dafür erstellten Transaktion ausgeführt. Diese Transaktion verwendet die Standardisolationsstufe, d. h. Snapshot (Parallelität), Schreiben und Warten. Wenn alles gut geht, wird die Transaktion festgeschrieben. Nicht abgefangene Ausnahmen führen zu einem Rollback der Transaktion und

  • bei einem CONNECT-Trigger wird die Verbindung dann unterbrochen und die Ausnahme wird an den Client zurückgegeben

  • Bei einem DISCONNECT-Trigger werden Ausnahmen nicht gemeldet. Die Verbindung ist wie beabsichtigt unterbrochen

TRANSACTION-Trigger werden innerhalb der Transaktion ausgeführt, deren Start, Commit oder Rollback sie hervorruft. Die nach einer nicht abgefangenen Ausnahme ausgeführte Aktion hängt vom Ereignis ab:

  • Bei einem TRANSACTION START-Trigger wird die Ausnahme an den Client gemeldet und die Transaktion wird zurückgesetzt

  • Bei einem TRANSACTION COMMIT Trigger wird die Ausnahme gemeldet, die bisherigen Aktionen des Triggers werden rückgängig gemacht und der Commit wird abgebrochen

  • Bei einem TRANSACTION ROLLBACK-Trigger wird die Ausnahme nicht gemeldet und die Transaktion wie vorgesehen zurückgesetzt.

Fallstricke

Offensichtlich gibt es keine direkte Möglichkeit zu wissen, ob ein DISCONNECT- oder TRANSACTION ROLLBACK-Trigger eine Ausnahme verursacht hat. Daraus folgt auch, dass die Verbindung zur Datenbank nicht zustande kommen kann, wenn ein CONNECT-Trigger eine Ausnahme auslöst und eine Transaktion auch nicht starten kann, wenn ein TRANSACTION START-Trigger eine auslöst. Beide Phänomene sperren Sie effektiv aus Ihrer Datenbank, bis Sie mit unterdrückten Datenbank-Triggern dort hineinkommen und den fehlerhaften Code beheben.

Unterdrücken von Datenbank-Triggern

Einige Firebird-Befehlszeilentools wurden mit Schaltern geliefert, mit denen ein Administrator das automatische Auslösen von Datenbank-Triggern unterdrücken kann. Bisher sind das:

gbak -nodbtriggers
isql -nodbtriggers
nbackup -T
Zweiphasen-Commit

In einem zweiphasigen Commit-Szenario löst TRANSACTION COMMIT das Auslösen in der Vorbereitungsphase aus, nicht beim Commit.

Einige Vorbehalte
  1. Die Verwendung der Anweisung IN AUTONOMOUS TRANSACTION DO in den Datenbankereignis-Triggern in Bezug auf Transaktionen (TRANSACTION START, TRANSACTION ROLLBACK, TRANSACTION COMMIT) kann dazu führen, dass die autonome Transaktion in eine Endlosschleife gerät

  2. Die Ereignistrigger DISCONNECT und TRANSACTION ROLLBACK werden nicht ausgeführt, wenn Clients über Monitoring-Tabellen getrennt werden (DELETE FROM MON$ATTACHMENTS)

Nur der Datenbankbesitzer und Administratoren haben die Berechtigung, Datenbank-Trigger zu erstellen.

Beispiele für CREATE TRIGGER für „Datenbank-Trigger
  1. Erstellen eines Triggers für das Ereignis der Verbindung mit der Datenbank, der die Anmeldung von Benutzern am System protokolliert. Der Trigger wird als inaktiv angelegt.

    CREATE TRIGGER tr_log_connect
    INACTIVE ON CONNECT POSITION 0
    AS
    BEGIN
      INSERT INTO LOG_CONNECT (ID,
                               USERNAME,
                               ATIME)
      VALUES (NEXT VALUE FOR SEQ_LOG_CONNECT,
              CURRENT_USER,
              CURRENT_TIMESTAMP);
    END
  2. Erstellen eines Auslösers für das Ereignis einer Verbindung mit der Datenbank, der es keinem Benutzer außer SYSDBA erlaubt, sich außerhalb der Geschäftszeiten anzumelden.

    CREATE EXCEPTION E_INCORRECT_WORKTIME 'The working day has not started yet.';
    
    CREATE TRIGGER TR_LIMIT_WORKTIME ACTIVE
    ON CONNECT POSITION 1
    AS
    BEGIN
      IF ((CURRENT_USER <> 'SYSDBA') AND
          NOT (CURRENT_TIME BETWEEN time '9:00' AND time '17:00')) THEN
        EXCEPTION E_INCORRECT_WORKTIME;
    END
DDL-Trigger

DDL-Trigger ermöglichen Einschränkungen für Benutzer, die versuchen, ein DDL-Objekt zu erstellen, zu ändern oder zu löschen. Ihr anderer Zweck besteht darin, ein Metadatenänderungsprotokoll zu führen.

DDL-Trigger lösen bei bestimmten Metadatenänderungsereignissen in einer bestimmten Phase aus. BEFORE-Trigger werden vor Änderungen an Systemtabellen ausgeführt. AFTER-Trigger werden nach Änderungen in Systemtabellen ausgeführt.

Der Ereignistyp [BEFORE | AFTER] eines DDL-Triggers kann nicht geändert werden.

In gewisser Weise sind DDL-Trigger ein Untertyp von Datenbank-Triggern.

Wer kann einen DDL-Trigger erstellen?

DDL-Trigger können erstellt werden durch:

Unterdrücken von DDL-Triggern

Ein DDL-Trigger ist eine Art Datenbank-Trigger. Siehe auch Unterdrücken von Datenbank-Triggern wie man Datenbank- und DDL-Trigger unterdrückt.

Beispiele für DDL-Trigger
  1. So können Sie einen DDL-Trigger verwenden, um ein konsistentes Benennungsschema zu erzwingen. In diesem Fall sollten die Namen der gespeicherten Prozeduren mit dem Präfix “SP_” beginnen:

    set auto on;
    create exception e_invalid_sp_name 'Invalid SP name (should start with SP_)';
    
    set term !;
    
    create trigger trig_ddl_sp before CREATE PROCEDURE
    as
    begin
      if (rdb$get_context('DDL_TRIGGER', 'OBJECT_NAME') not starting 'SP_') then
        exception e_invalid_sp_name;
    end!

    Test

    create procedure sp_test
    as
    begin
    end!
    
    create procedure test
    as
    begin
    end!
    
    -- Der letzte Befehl löst diese Ausnahme aus und die Prozedur TEST wird nicht erstellt:
    -- Statement failed, SQLSTATE = 42000
    -- exception 1
    -- -E_INVALID_SP_NAME
    -- -Invalid SP name (should start with SP_)
    -- -At trigger 'TRIG_DDL_SP' line: 4, col: 5
    
    set term ;!
  2. Implementieren Sie benutzerdefinierte DDL-Sicherheit, indem Sie in diesem Fall die Ausführung von DDL-Befehlen auf bestimmte Benutzer beschränken:

    create exception e_access_denied 'Access denied';
    
    set term !;
    
    create trigger trig_ddl before any ddl statement
    as
    begin
      if (current_user <> 'SUPER_USER') then
        exception e_access_denied;
    end!

    Test

    create procedure sp_test
    as
    begin
    end!
    
    -- Der letzte Befehl löst diese Ausnahme aus und die Prozedur SP_TEST wird nicht erstellt
    -- Statement failed, SQLSTATE = 42000
    -- exception 1
    -- -E_ACCESS_DENIED
    -- -Access denied
    -- -At trigger 'TRIG_DDL' line: 4, col: 5
    
    set term ;!

    Firebird hat Berechtigungen zum Ausführen von DDL-Anweisungen, daher sollte das Schreiben eines DDL-Triggers dafür der letzte Ausweg sein, wenn der gleiche Effekt nicht mit Berechtigungen erzielt werden kann.

  3. Verwenden eines Triggers, um DDL-Aktionen und -Versuche zu protokollieren:

    create sequence ddl_seq;
    
    create table ddl_log (
      id bigint not null primary key,
      moment timestamp not null,
      user_name varchar(31) not null,
      event_type varchar(25) not null,
      object_type varchar(25) not null,
      ddl_event varchar(25) not null,
      object_name varchar(31) not null,
      sql_text blob sub_type text not null,
      ok char(1) not null
    );
    
    set term !;
    
    create trigger trig_ddl_log_before before any ddl statement
    as
      declare id type of column ddl_log.id;
    begin
      -- Wir nehmen die Änderungen in einer AUTONOMEN TRANSAKTION vor. Wenn also eine Ausnahme auftritt und
      -- der Befehl nicht ausgeführt wurde, bleibt das Protokoll erhalten.
      in autonomous transaction do
      begin
        insert into ddl_log (id, moment, user_name, event_type, object_type,
                             ddl_event, object_name, sql_text, ok)
          values (next value for ddl_seq, current_timestamp, current_user,
                  rdb$get_context('DDL_TRIGGER', 'EVENT_TYPE'),
                  rdb$get_context('DDL_TRIGGER', 'OBJECT_TYPE'),
                  rdb$get_context('DDL_TRIGGER', 'DDL_EVENT'),
                  rdb$get_context('DDL_TRIGGER', 'OBJECT_NAME'),
                  rdb$get_context('DDL_TRIGGER', 'SQL_TEXT'),
                  'N')
          returning id into id;
        rdb$set_context('USER_SESSION', 'trig_ddl_log_id', id);
      end
    end!

    Der obige Trigger wird für diesen DDL-Befehl ausgelöst. Es ist eine gute Idee, -nodbtriggers zu verwenden, wenn Sie mit ihnen arbeiten!

    create trigger trig_ddl_log_after after any ddl statement
    as
    begin
      -- Hier benötigen wir eine AUTONOME TRANSACTION, da die ursprüngliche Transaktion den Datensatz
      -- nicht sehen wird, der in den BEFORE-Trigger der
      -- autonomen Transaktion eingefügt wurde, wenn die Benutzertransaktion nicht READ COMMITTED ist.
      in autonomous transaction do
         update ddl_log set ok = 'Y'
         where id = rdb$get_context('USER_SESSION', 'trig_ddl_log_id');
    end!
    
    commit!
    
    set term ;!
    
    -- Löschen Sie den Datensatz über trig_ddl_log_after
    delete from ddl_log;
    commit;

    Test

    -- Dies wird einmalig protokolliert
    -- (da T1 nicht existierte, fungiert RECREATE als CREATE) mit OK = Y.
    recreate table t1 (
      n1 integer,
      n2 integer
    );
    
    -- Dies schlägt fehl, da T1 bereits existiert, also ist OK N.
    create table t1 (
      n1 integer,
      n2 integer
    );
    
    -- T2 existiert nicht. Es wird kein Protokoll geben.
    drop table t2;
    
    -- Dies wird zweimal protokolliert
    -- (da T1 existiert, fungiert RECREATE als DROP und CREATE) mit OK = Y.
    recreate table t1 (
      n integer
    );
    
    commit;
    select id, ddl_event, object_name, sql_text, ok
      from ddl_log order by id;
    
     ID DDL_EVENT                 OBJECT_NAME                      SQL_TEXT OK
    === ========================= ======================= ================= ======
      2 CREATE TABLE              T1                                   80:3 Y
    ====================================================
    SQL_TEXT:
    recreate table t1 (
        n1 integer,
        n2 integer
    )
    ====================================================
      3 CREATE TABLE              T1                                   80:2 N
    ====================================================
    SQL_TEXT:
    create table t1 (
        n1 integer,
        n2 integer
    )
    ====================================================
      4 DROP TABLE                T1                                   80:6 Y
    ====================================================
    SQL_TEXT:
    recreate table t1 (
        n integer
    )
    ====================================================
      5 CREATE TABLE              T1                                   80:9 Y
    ====================================================
    SQL_TEXT:
    recreate table t1 (
        n integer
    )
    ====================================================
Siehe auch

ALTER TRIGGER, CREATE OR ALTER TRIGGER, RECREATE TRIGGER, DROP TRIGGER, DDL-Trigger im Kapitel Procedural SQL (PSQL)-Anweisungen

5.7.2. ALTER TRIGGER

Verwendet für

Ändern und Deaktivieren eines bestehenden Triggers

Verfügbar in

DSQL, ESQL

Syntax
ALTER TRIGGER trigname
  [ACTIVE | INACTIVE]
  [{BEFORE | AFTER} <mutation_list>]
  [POSITION number]
  [<module-body>]

!! Vgl. auch die Syntax CREATE TRIGGER für weitere Regeln!!

Die ALTER TRIGGER-Anweisung erlaubt nur bestimmte Änderungen am Header und Body eines Triggers.

Zulässige Änderungen an Triggern
  • Status (ACTIVE | INACTIVE)

  • Phase (BEFORE | AFTER) (bei DML-Triggern)

  • Ereignisse (bei DML-Triggern)

  • Position in der Ausführungsfolge

  • Änderungen am Code im Trigger-Body

Wenn ein Element nicht angegeben wird, bleibt es unverändert.

Ein DML-Trigger kann nicht in einen Datenbank- (oder DDL-)Trigger geändert werden.

Es ist nicht möglich, das/die Ereignis(e) oder die Phase eines Datenbank- (oder DDL-)Triggers zu ändern.

Merken Sie sich

Das Schlüsselwort BEFORE weist an, dass der Trigger ausgeführt wird, bevor das zugehörige Ereignis eintritt; das Schlüsselwort AFTER weist an, dass es nach dem Ereignis ausgeführt wird.

Mehrere DML-Ereignisse – INSERT, UPDATE, DELETE – können in einem einzigen Trigger abgedeckt werden. Die Ereignisse sollten mit dem Schlüsselwort OR getrennt werden. Kein Ereignis sollte mehr als einmal erwähnt werden.

Das Schlüsselwort POSITION ermöglicht die Angabe einer optionalen Ausführungsreihenfolge (“firing order”) für eine Reihe von Triggern, die die gleiche Phase und das gleiche Ereignis wie ihr Ziel haben. Die Standardposition ist 0. Wenn keine Positionen angegeben sind oder mehrere Trigger eine einzige Positionsnummer haben, werden die Trigger in alphabetischer Reihenfolge ihrer Namen ausgeführt.====

Wer kann einen Trigger ändern?

DML-Trigger können geändert werden durch:

  • Administratoren

  • Der Besitzer der Tabelle (oder Ansicht)

  • Benutzer mit dem ALTER ANY TABLE- oder — für eine Ansicht — ALTER ANY VIEW-Privileg

Datenbank- und DDL-Trigger können geändert werden durch:

Beispiele mit ALTER TRIGGER
  1. Den Trigger set_cust_no deaktivieren (in den inaktiven Zustand schalten).

    ALTER TRIGGER set_cust_no INACTIVE;
  2. Ändern der Position der Zündreihenfolge des Triggers set_cust_no.

    ALTER TRIGGER set_cust_no POSITION 14;
  3. Den Trigger TR_CUST_LOG in den inaktiven Zustand schalten und die Ereignisliste ändern.

    ALTER TRIGGER TR_CUST_LOG
    INACTIVE AFTER INSERT OR UPDATE;
  4. Den tr_log_connect Trigger in den aktiven Status schalten, seine Position und seinen Körper ändern.

    ALTER TRIGGER tr_log_connect
    ACTIVE POSITION 1
    AS
    BEGIN
      INSERT INTO LOG_CONNECT (ID,
                               USERNAME,
                               ROLENAME,
                               ATIME)
      VALUES (NEXT VALUE FOR SEQ_LOG_CONNECT,
              CURRENT_USER,
              CURRENT_ROLE,
              CURRENT_TIMESTAMP);
    END

5.7.3. CREATE OR ALTER TRIGGER

Verwendet für

Erstellen eines neuen Triggers oder Ändern eines bestehenden Triggers

Verfügbar in

DSQL

Syntax
CREATE OR ALTER TRIGGER trigname
  { <relation_trigger_legacy>
  | <relation_trigger_sql2003>
  | <database_trigger>
  | <ddl_trigger> }
  <module-body>

!!Vgl. auch die Syntax CREATE TRIGGER für weitere Regeln !!

Die Anweisung CREATE OR ALTER TRIGGER erstellt einen neuen Trigger, falls dieser nicht existiert; andernfalls ändert und kompiliert es sie mit den intakten Privilegien und unberührten Abhängigkeiten.

Beispiel für CREATE OR ALTER TRIGGER
Neuen Trigger erstellen, wenn er nicht existiert, oder ihn ändern, falls vorhanden
CREATE OR ALTER TRIGGER set_cust_no
ACTIVE BEFORE INSERT POSITION 0 ON customer
AS
BEGIN
  IF (NEW.cust_no IS NULL) THEN
    NEW.cust_no = GEN_ID(cust_no_gen, 1);
END

5.7.4. DROP TRIGGER

Verwendet für

Löschen eines vorhandenen Triggers

Verfügbar in

DSQL, ESQL

Syntax
DROP TRIGGER trigname
Tabelle 41. DROP TRIGGER-Anweisgungsparameter
Parameter Beschreibung

trigname

Triggername

Die Anweisung DROP TRIGGER verwirft (löscht) einen vorhandenen Trigger.

Wer kann einen Trigger fallen lassen?

DML-Trigger können gelöscht werden durch:

  • Administratoren

  • Der Besitzer der Tabelle (oder Ansicht)

  • Benutzer mit dem ALTER ANY TABLE- oder — für eine Ansicht — ALTER ANY VIEW-Privileg

Datenbank- und DDL-Trigger können gelöscht werden durch:

Beispiel für DROP TRIGGER
Löschen des Triggers set_cust_no
DROP TRIGGER set_cust_no;

5.7.5. RECREATE TRIGGER

Verwendet für

Erstellen eines neuen Triggers oder Neuerstellen eines vorhandenen Triggers

Verfügbar in

DSQL

Syntax
RECREATE TRIGGER trigname
  { <relation_trigger_legacy>
  | <relation_trigger_sql2003>
  | <database_trigger>
  | <ddl_trigger> }
  <module-body>

!! Vgl. auch die Syntax CREATE TRIGGER für weitere Regeln !!

Die Anweisung RECREATE TRIGGER erstellt einen neuen Trigger, wenn kein Trigger mit dem angegebenen Namen existiert; andernfalls versucht die Anweisung RECREATE TRIGGER, den vorhandenen Trigger zu löschen und einen neuen zu erstellen. Die Operation schlägt bei COMMIT fehl, wenn der Trigger verwendet wird.

Beachten Sie, dass Abhängigkeitsfehler erst in der COMMIT-Phase dieser Operation erkannt werden.

Beispiel für RECREATE TRIGGER

Erstellen oder erneutes Erstellen des Triggers set_cust_no.

RECREATE TRIGGER set_cust_no
ACTIVE BEFORE INSERT POSITION 0 ON customer
AS
BEGIN
  IF (NEW.cust_no IS NULL) THEN
    NEW.cust_no = GEN_ID(cust_no_gen, 1);
END

5.8. PROCEDURE

Eine Stored Procedure ist ein Softwaremodul, das von einem Client, einer anderen Prozedur, Funktion, ausführbaren Block oder Trigger aufgerufen werden kann. Gespeicherte Prozeduren, gespeicherte Funktionen, ausführbare Blöcke und Trigger werden in prozeduralem SQL (PSQL) geschrieben. Die meisten SQL-Anweisungen sind auch in PSQL verfügbar, manchmal mit einigen Einschränkungen oder Erweiterungen, bemerkenswerte Einschränkungen sind DDL- und Transaktionssteuerungsanweisungen.

Gespeicherte Prozeduren können viele Eingabe- und Ausgabeparameter haben.

5.8.1. CREATE PROCEDURE

Verwendet für

Erstellen einer neuen gespeicherten Prozedur

Verfügbar in

DSQL, ESQL

Syntax
CREATE PROCEDURE procname [ ( [ <in_params> ] ) ]
  [RETURNS (<out_params>)]
  <module-body>

<module-body> ::=
  !! Siehe auch Syntax des Modul-Bodys !!

<in_params> ::= <inparam> [, <inparam> ...]

<inparam> ::= <param_decl> [{= | DEFAULT} <value>]

<out_params> ::= <outparam> [, <outparam> ...]

<outparam> ::= <param_decl>

<value> ::= {<literal> | NULL | <context_var>}

<param_decl> ::= paramname <domain_or_non_array_type> [NOT NULL]
  [COLLATE collation]

<type> ::=
    <datatype>
  | [TYPE OF] domain
  | TYPE OF COLUMN rel.col

<domain_or_non_array_type> ::=
  !! Siehe auch Syntax für Skalardatentypen !!
Tabelle 42. CREATE PROCEDURE-Anweisungsparameter
Parameter Beschreibung

procname

Name der gespeicherten Prozedur, bestehend aus bis zu 31 Zeichen. Muss unter allen Tabellen-, Ansichts- und Prozedurnamen in der Datenbank eindeutig sein

inparam

Beschreibung der Eingabeparameter

outparam

Beschreibung der Ausgangsparameter

literal

Ein Literalwert, der mit dem Datentyp des Parameters zuweisungskompatibel ist

context_var

Jede Kontextvariable, deren Typ mit dem Datentyp des Parameters kompatibel ist

paramname

Der Name eines Eingabe- oder Ausgabeparameters der Prozedur. Er kann aus bis zu 31 Zeichen bestehen. Der Name des Parameters muss unter den Eingabe- und Ausgabeparametern der Prozedur und ihrer lokalen Variablen eindeutig sein

collation

Sortierreihenfolge

Die Anweisung CREATE PROCEDURE erstellt eine neue gespeicherte Prozedur. Der Name der Prozedur muss unter den Namen aller gespeicherten Prozeduren, Tabellen und Ansichten in der Datenbank eindeutig sein.

CREATE PROCEDURE ist eine zusammengesetzte Anweisung, bestehend aus einem Header und einem Body. Der Header gibt den Namen der Prozedur an und deklariert Eingabeparameter und gegebenenfalls Ausgabeparameter, die von der Prozedur zurückgegeben werden sollen.

Der Prozedurrumpf besteht aus Deklarationen für alle lokalen Variablen und benannten Cursors, die von der Prozedur verwendet werden, gefolgt von einer oder mehreren Anweisungen oder Anweisungsblöcken, die alle in einem äußeren Block eingeschlossen sind, der mit dem Schlüsselwort BEGIN beginnt und mit . endet das Schlüsselwort END. Deklarationen und eingebettete Anweisungen werden mit Semikolons (‘;’) abgeschlossen.

Statement-Terminatoren

Einige Editoren für SQL-Anweisungen – insbesondere das Dienstprogramm isql, das mit Firebird geliefert wird, und möglicherweise einige Editoren von Drittanbietern – verwenden eine interne Konvention, die erfordert, dass alle Anweisungen mit einem Semikolon abgeschlossen werden. Dies führt beim Codieren in diesen Umgebungen zu einem Konflikt mit der PSQL-Syntax. Wenn Sie dieses Problem und seine Lösung nicht kennen, lesen Sie bitte die Details im PSQL-Kapitel im Abschnitt Umschalten des Terminators in isql.

Parameter

Jeder Parameter hat einen Datentyp. Der NOT NULL-Constraint kann auch für jeden Parameter angegeben werden, um zu verhindern, dass NULL übergeben oder ihm zugewiesen wird.

Eine Kollatierungssequenz kann für String-Typ-Parameter mit der COLLATE-Klausel angegeben werden.

Eingabeparameter

Eingabeparameter werden als Liste in Klammern nach dem Namen der Funktion angezeigt. Sie werden als Wert an die Prozedur übergeben, sodass Änderungen innerhalb der Prozedur keine Auswirkungen auf die Parameter im Aufrufer haben. Eingabeparameter können Standardwerte haben. Parameter mit angegebenen Standardwerten müssen am Ende der Parameterliste hinzugefügt werden.

Ausgabeparameter

Die optionale RETURNS-Klausel dient zum Angeben einer in Klammern gesetzten Liste von Ausgabeparametern für die gespeicherte Prozedur.

Variablen-, Cursor- und Sub-Routine-Deklarationen

Der optionale Deklarationsabschnitt, der sich am Anfang des Hauptteils der Prozedurdefinition befindet, definiert Variablen (einschließlich Cursors) und Unterroutinen lokal für die Prozedur. Lokale Variablendeklarationen folgen den gleichen Regeln wie Parameter bezüglich der Angabe des Datentyps. Weitere Informationen finden Sie im PSQL-Kapitel für DECLARE VARIABLE, ` DECLARE CURSOR`, DECLARE FUNCTION und DECLARE PROCEDURE.

Externe UDR-Prozeduren

Eine gespeicherte Prozedur kann sich auch in einem externen Modul befinden. In diesem Fall spezifiziert CREATE PROCEDURE anstelle eines Prozedurrumpfs die Position der Prozedur im externen Modul mit der EXTERNAL-Klausel. Die optionale NAME-Klausel spezifiziert den Namen des externen Moduls, den Namen der Prozedur innerhalb des Moduls und – optional – benutzerdefinierte Informationen. Die erforderliche ENGINE-Klausel gibt den Namen der UDR-Engine an, die die Kommunikation zwischen Firebird und dem externen Modul handhabt. Die optionale AS-Klausel akzeptiert ein String-Literal “body”, das von der Engine oder dem Modul für verschiedene Zwecke verwendet werden kann.

Wer kann ein Verfahren erstellen

Die CREATE PROCEDURE-Anweisung kann ausgeführt werden durch:

Der Benutzer, der die Anweisung CREATE PROCEDURE ausführt, wird Eigentümer der Tabelle.

Beispiele
  1. Erstellen einer gespeicherten Prozedur, die einen Datensatz in die BREED-Tabelle einfügt und den Code des eingefügten Datensatzes zurückgibt:

    CREATE PROCEDURE ADD_BREED (
      NAME D_BREEDNAME, /* Domain attributes are inherited */
      NAME_EN TYPE OF D_BREEDNAME, /* Only the domain type is inherited */
      SHORTNAME TYPE OF COLUMN BREED.SHORTNAME,
        /* The table column type is inherited */
      REMARK VARCHAR(120) CHARACTER SET WIN1251 COLLATE PXW_CYRL,
      CODE_ANIMAL INT NOT NULL DEFAULT 1
    )
    RETURNS (
      CODE_BREED INT
    )
    AS
    BEGIN
      INSERT INTO BREED (
        CODE_ANIMAL, NAME, NAME_EN, SHORTNAME, REMARK)
      VALUES (
        :CODE_ANIMAL, :NAME, :NAME_EN, :SHORTNAME, :REMARK)
      RETURNING CODE_BREED INTO CODE_BREED;
    END
  2. Erstellen einer auswählbaren gespeicherten Prozedur, die Daten für Adressetiketten generiert (aus employee.fdb):

    CREATE PROCEDURE mail_label (cust_no INTEGER)
    RETURNS (line1 CHAR(40), line2 CHAR(40), line3 CHAR(40),
             line4 CHAR(40), line5 CHAR(40), line6 CHAR(40))
    AS
      DECLARE VARIABLE customer VARCHAR(25);
      DECLARE VARIABLE first_name VARCHAR(15);
      DECLARE VARIABLE last_name VARCHAR(20);
      DECLARE VARIABLE addr1 VARCHAR(30);
      DECLARE VARIABLE addr2 VARCHAR(30);
      DECLARE VARIABLE city VARCHAR(25);
      DECLARE VARIABLE state VARCHAR(15);
      DECLARE VARIABLE country VARCHAR(15);
      DECLARE VARIABLE postcode VARCHAR(12);
      DECLARE VARIABLE cnt INTEGER;
    BEGIN
      line1 = '';
      line2 = '';
      line3 = '';
      line4 = '';
      line5 = '';
      line6 = '';
    
      SELECT customer, contact_first, contact_last, address_line1,
        address_line2, city, state_province, country, postal_code
      FROM CUSTOMER
      WHERE cust_no = :cust_no
      INTO :customer, :first_name, :last_name, :addr1, :addr2,
        :city, :state, :country, :postcode;
    
      IF (customer IS NOT NULL) THEN
        line1 = customer;
      IF (first_name IS NOT NULL) THEN
        line2 = first_name || ' ' || last_name;
      ELSE
        line2 = last_name;
      IF (addr1 IS NOT NULL) THEN
        line3 = addr1;
      IF (addr2 IS NOT NULL) THEN
        line4 = addr2;
    
      IF (country = 'USA') THEN
      BEGIN
        IF (city IS NOT NULL) THEN
      	  line5 = city || ', ' || state || '  ' || postcode;
      	ELSE
          line5 = state || '  ' || postcode;
      END
      ELSE
      BEGIN
        IF (city IS NOT NULL) THEN
      	  line5 = city || ', ' || state;
      	ELSE
          line5 = state;
        line6 = country || '    ' || postcode;
      END
    
      SUSPEND; -- die Anweisung, die eine Ausgabezeile an den Puffer sendet
               -- und die Prozedur "selektierbar" macht
    END

5.8.2. ALTER PROCEDURE

Verwendet für

Ändern einer vorhandenen gespeicherten Prozedur

Verfügbar in

DSQL, ESQL

Syntax
ALTER PROCEDURE procname [ ( [ <in_params> ] ) ]
  [RETURNS (<out_params>)]
  <module-body>

!! Vgl. auch die Syntax CREATE PROCEDURE für weitere Regeln !!

Die ALTER PROCEDURE-Anweisung ermöglicht die folgenden Änderungen an einer Stored-Procedure-Definition:

  • der Satz und die Eigenschaften der Eingabe- und Ausgabeparameter

  • lokale Variablen

  • Code im Hauptteil der gespeicherten Prozedur

Nachdem ALTER PROCEDURE ausgeführt wurde, bleiben bestehende Privilegien intakt und Abhängigkeiten werden nicht beeinflusst.

Achten Sie darauf, die Anzahl und den Typ der Eingabe- und Ausgabeparameter in gespeicherten Prozeduren zu ändern. Vorhandener Anwendungscode und Prozeduren und Trigger, die ihn aufrufen, könnten ungültig werden, da die neue Beschreibung der Parameter nicht mit dem alten Aufrufformat kompatibel ist. Informationen zur Behebung einer solchen Situation finden Sie im Artikel Das RDB$VALID_BLR-Feld im Anhang.

Wer kann ein Verfahren ändern

Die Anweisung ALTER PROCEDURE kann ausgeführt werden durch:

  • Administratoren

  • Der Besitzer der gespeicherten Prozedur

  • Benutzer mit der Berechtigung ALTER ANY PROCEDURE

ALTER PROCEDURE-Beispiel
Ändern der gespeicherten Prozedur GET_EMP_PROJ.
ALTER PROCEDURE GET_EMP_PROJ (
  EMP_NO SMALLINT)
RETURNS (
  PROJ_ID VARCHAR(20))
AS
BEGIN
  FOR SELECT
      PROJ_ID
    FROM
      EMPLOYEE_PROJECT
    WHERE
      EMP_NO = :emp_no
    INTO :proj_id
  DO
    SUSPEND;
END

5.8.3. CREATE OR ALTER PROCEDURE

Verwendet für

Erstellen einer neuen gespeicherten Prozedur oder Ändern einer vorhandenen Prozedur

Verfügbar in

DSQL

Syntax
CREATE OR ALTER PROCEDURE procname [ ( [ <in_params> ] ) ]
  [RETURNS (<out_params>)]
  <module-body>

!! Vgl. auch die Syntax CREATE PROCEDURE für weitere Regeln !!

Die Anweisung "CREATE OR ALTER PROCEDURE" erstellt eine neue gespeicherte Prozedur oder ändert eine vorhandene. Wenn die gespeicherte Prozedur nicht existiert, wird sie durch transparentes Aufrufen einer CREATE PROCEDURE-Anweisung erstellt. Wenn die Prozedur bereits existiert, wird sie geändert und kompiliert, ohne ihre bestehenden Privilegien und Abhängigkeiten zu beeinträchtigen.

CREATE OR ALTER PROCEDURE-Beispiel
Erstellen oder Ändern der Prozedur GET_EMP_PROJ.
CREATE OR ALTER PROCEDURE GET_EMP_PROJ (
    EMP_NO SMALLINT)
RETURNS (
    PROJ_ID VARCHAR(20))
AS
BEGIN
  FOR SELECT
      PROJ_ID
    FROM
      EMPLOYEE_PROJECT
    WHERE
      EMP_NO = :emp_no
    INTO :proj_id
  DO
    SUSPEND;
END

5.8.4. DROP PROCEDURE

Verwendet für

Löschen einer gespeicherten Prozedur

Verfügbar in

DSQL, ESQL

Syntax
DROP PROCEDURE procname
Tabelle 43. DROP PROCEDURE-Anweisungsparameter
Parameter Beschreibung

procname

Name einer vorhandenen gespeicherten Prozedur

Die Anweisung DROP PROCEDURE löscht eine vorhandene gespeicherte Prozedur. Wenn die gespeicherte Prozedur Abhängigkeiten aufweist, schlägt der Versuch, sie zu löschen, fehl und der entsprechende Fehler wird ausgegeben.

Wer kann ein Verfahren abbrechen

Die Anweisung ALTER PROCEDURE kann ausgeführt werden durch:

  • Administratoren

  • Der Besitzer der gespeicherten Prozedur

  • Benutzer mit dem Privileg DROP ANY PROCEDURE

DROP PROCEDURE-Beispiel
Löschen der gespeicherten Prozedur GET_EMP_PROJ.
DROP PROCEDURE GET_EMP_PROJ;

5.8.5. RECREATE PROCEDURE

Verwendet für

Erstellen einer neuen gespeicherten Prozedur oder Neuerstellen einer vorhandenen Prozedur

Verfügbar in

DSQL

Syntax
RECREATE PROCEDURE procname [ ( [ <in_params> ] ) ]
  [RETURNS (<out_params>)]
  <module-body>

!! Vgl. auch die Syntax CREATE PROCEDURE für weitere Regeln !!

Die Anweisung 'RECREATE PROCEDURE' erstellt eine neue gespeicherte Prozedur oder erstellt eine vorhandene neu. Wenn es bereits eine Prozedur mit diesem Namen gibt, versucht die Engine, diese zu löschen und eine neue zu erstellen. Das Neuerstellen einer vorhandenen Prozedur schlägt bei der COMMIT-Anforderung fehl, wenn die Prozedur Abhängigkeiten hat.

Beachten Sie, dass Abhängigkeitsfehler erst in der COMMIT-Phase dieser Operation erkannt werden.

Nachdem eine Prozedur erfolgreich neu erstellt wurde, werden die Berechtigungen zum Ausführen der gespeicherten Prozedur und die Berechtigungen der gespeicherten Prozedur selbst gelöscht.

RECREATE PROCEDURE-Beispiel
Erstellen der neuen gespeicherten Prozedur GET_EMP_PROJ oder Neuerstellen der vorhandenen gespeicherten Prozedur GET_EMP_PROJ.
RECREATE PROCEDURE GET_EMP_PROJ (
  EMP_NO SMALLINT)
RETURNS (
  PROJ_ID VARCHAR(20))
AS
BEGIN
  FOR SELECT
      PROJ_ID
    FROM
      EMPLOYEE_PROJECT
    WHERE
      EMP_NO = :emp_no
    INTO :proj_id
  DO
    SUSPEND;
END

5.9. FUNCTION

Eine gespeicherte Funktion ist eine benutzerdefinierte Funktion, die in den Metadaten einer Datenbank gespeichert ist und auf dem Server ausgeführt wird. Gespeicherte Funktionen können von gespeicherten Prozeduren, gespeicherten Funktionen (einschließlich der Funktion selbst), Triggern und Client-Programmen aufgerufen werden. Wenn sich eine gespeicherte Funktion selbst aufruft, wird eine solche gespeicherte Funktion als rekursive Funktion bezeichnet.

Im Gegensatz zu gespeicherten Prozeduren geben gespeicherte Funktionen immer einen einzelnen Skalarwert zurück. Um einen Wert aus einer gespeicherten Funktion zurückzugeben, verwenden Sie die RETURN-Anweisung, die die Funktion sofort beendet.

Siehe auch

EXTERNAL FUNCTION

5.9.1. CREATE FUNCTION

Verwendet für

Erstellen einer neuen gespeicherten Funktion

Verfügbar in

DSQL

Syntax
CREATE FUNCTION funcname [ ( [ <in_params> ] ) ]
  RETURNS <domain_or_non_array_type> [COLLATE collation]
  [DETERMINISTIC]
  <module-body>

<module-body> ::=
  !! Vgl. Syntax des Modulrumpfes !!

<in_params> ::= <inparam> [, <inparam> ... ]

<inparam> ::= <param-decl> [ { = | DEFAULT } <value> ]

<value> ::= { <literal> | NULL | <context-var> }

<param-decl> ::= paramname <domain_or_non_array_type> [NOT NULL]
  [COLLATE collation]

<domain_or_non_array_type> ::=
  !! Vgl. Skalardatentyp-Syntax !!
Tabelle 44. CREATE FUNCTION-Anweisungsparameter
Parameter Beschreibung

funcname

Gespeicherter Funktionsname bestehend aus bis zu 31 Zeichen. Muss unter allen Funktionsnamen in der Datenbank eindeutig sein.

inparam

Beschreibung der Eingabeparameter

collation

Sortierreihenfolge

literal

Ein Literalwert, der mit dem Datentyp des Parameters zuweisungskompatibel ist

context-var

Jede Kontextvariable, deren Typ mit dem Datentyp des Parameters kompatibel ist

paramname

Der Name eines Eingabeparameters der Funktion. Er kann aus bis zu 31 Zeichen bestehen. Der Name des Parameters muss unter den Eingabeparametern der Funktion und ihren lokalen Variablen eindeutig sein.

Die Anweisung CREATE FUNCTION erstellt eine neue gespeicherte Funktion. Der gespeicherte Funktionsname muss unter den Namen aller gespeicherten und externen (alten) Funktionen eindeutig sein, mit Ausnahme von Unterfunktionen oder Funktionen in Paketen. Bei Unterfunktionen oder Funktionen in Paketen muss der Name innerhalb ihres Moduls (Paket, Stored Procedure, Stored Function, Trigger) eindeutig sein.

Es ist ratsam, Funktionsnamen zwischen globalen gespeicherten Funktionen und gespeicherten Funktionen in Paketen nicht wiederzuverwenden, obwohl dies zulässig ist. Momentan ist es nicht möglich, eine Funktion oder Prozedur aus dem globalen Namensraum innerhalb eines Pakets aufzurufen, wenn dieses Paket eine Funktion oder Prozedur mit demselben Namen definiert. In dieser Situation wird die Funktion oder Prozedur des Pakets aufgerufen.

CREATE FUNCTION ist eine zusammengesetzte Anweisung mit einem Header und einem Body. Der Header definiert den Namen der gespeicherten Funktion und deklariert Eingabeparameter und Rückgabetyp.

Der Funktionsrumpf besteht aus optionalen Deklarationen von lokalen Variablen, benannten Cursorn und Unterprogrammen (Unterfunktionen und Unterprozeduren) und einer oder mehreren Anweisungen oder Anweisungsblöcken, eingeschlossen in einen äußeren Block, der mit dem Schlüsselwort BEGIN beginnt und endet mit dem Schlüsselwort END. Deklarationen und Anweisungen innerhalb des Funktionsrumpfs müssen mit einem Semikolon (‘;’) abgeschlossen werden.

Statement-Terminatoren

Einige SQL-Anweisungseditoren – insbesondere das mit Firebird gelieferte Dienstprogramm isql und möglicherweise einige Editoren von Drittanbietern – verwenden eine interne Konvention, die erfordert, dass alle Anweisungen mit einem Semikolon abgeschlossen werden. Dies führt beim Codieren in diesen Umgebungen zu einem Konflikt mit der PSQL-Syntax. Wenn Sie dieses Problem und seine Lösung nicht kennen, lesen Sie bitte die Details im PSQL-Kapitel im Abschnitt Terminator in isql umschalten.

Parameter

Jeder Parameter hat einen Datentyp.

Eine Kollatierungssequenz kann für String-Typ-Parameter mit der COLLATE-Klausel angegeben werden.

Eingabeparameter

Eingabeparameter werden als Liste in Klammern nach dem Namen der Funktion angezeigt. Sie werden als Wert an die Funktion übergeben, sodass Änderungen innerhalb der Funktion keine Auswirkungen auf die Parameter im Aufrufer haben. Die NOT NULL-Einschränkung kann auch für jeden Eingabeparameter angegeben werden, um zu verhindern, dass NULL übergeben oder zugewiesen wird. Eingabeparameter können Standardwerte haben. Parameter mit angegebenen Standardwerten müssen am Ende der Parameterliste hinzugefügt werden.

Ausgabeparameter

Die RETURNS-Klausel gibt den Rückgabetyp der gespeicherten Funktion an. Wenn eine Funktion einen String-Wert zurückgibt, ist es möglich, die Sortierung mit der COLLATE-Klausel anzugeben. Als Rückgabetyp können Sie einen Datentyp, einen Domänennamen, den Typ einer Domäne (mit TYPE OF) oder den Typ einer Spalte einer Tabelle oder View (mit TYPE OF COLUMN) angeben.

Deterministische Funktionen

Die optionale DETERMINISTIC-Klausel gibt an, dass die Funktion deterministisch ist. Deterministische Funktionen geben immer das gleiche Ergebnis für den gleichen Satz von Eingaben zurück. Nicht-deterministische Funktionen können für jeden Aufruf unterschiedliche Ergebnisse zurückgeben, sogar für denselben Satz von Eingaben. Wenn eine Funktion als deterministisch angegeben ist, wird eine solche Funktion möglicherweise nicht erneut aufgerufen, wenn sie bereits einmal mit den angegebenen Eingaben aufgerufen wurde, sondern übernimmt das Ergebnis aus einem Metadaten-Cache.

Aktuelle Versionen von Firebird speichern Ergebnisse deterministischer Funktionen nicht wirklich.

Die Angabe der DETERMINISTIC-Klausel ist eigentlich so etwas wie ein “Versprechen”, dass die Funktion für gleiche Eingaben dasselbe zurückgibt. Im Moment wird eine deterministische Funktion als Invariante betrachtet und funktioniert wie andere Invarianten. Das heißt, sie werden auf der aktuellen Ausführungsebene einer bestimmten Anweisung berechnet und zwischengespeichert.

Dies lässt sich leicht an einem Beispiel demonstrieren:

CREATE FUNCTION FN_T
RETURNS DOUBLE PRECISION DETERMINISTIC
AS
BEGIN
  RETURN rand();
END;

-- die Funktion wird zweimal ausgewertet und gibt 2 verschiedene Werte zurück
SELECT fn_t() FROM rdb$database
UNION ALL
SELECT fn_t() FROM rdb$database;

-- die Funktion wird einmal ausgewertet und gibt 2 identische Werte zurück
WITH t (n) AS (
  SELECT 1 FROM rdb$database
  UNION ALL
  SELECT 2 FROM rdb$database
)
SELECT n, fn_t() FROM t;
Variablen-, Cursor- und Sub-Routine-Deklarationen

Der optionale Deklarationsabschnitt, der sich am Anfang des Hauptteils der Funktionsdefinition befindet, definiert Variablen (einschließlich Cursors) und funktionslokale Unterroutinen. Lokale Variablendeklarationen folgen den gleichen Regeln wie Parameter bezüglich der Angabe des Datentyps. Weitere Informationen finden Sie im PSQL-Kapitel für DECLARE VARIABLE, DECLARE CURSOR, DECLARE FUNCTION und DECLARE PROCEDURE.

Funktionsrumpf

Auf den Header-Abschnitt folgt der Funktionsrumpf, der aus einer oder mehreren PSQL-Anweisungen besteht, die zwischen den äußeren Schlüsselwörtern BEGIN und END eingeschlossen sind. Mehrere BEGIN …​ END-Blöcke von beendeten Anweisungen können in den Prozedurrumpf eingebettet werden.

Externe UDR-Funktionen

Eine gespeicherte Funktion kann sich auch in einem externen Modul befinden. In diesem Fall spezifiziert CREATE FUNCTION anstelle eines Funktionsrumpfs die Position der Funktion im externen Modul mit der EXTERNAL-Klausel. Die optionale NAME-Klausel spezifiziert den Namen des externen Moduls, den Namen der Funktion innerhalb des Moduls und – optional – benutzerdefinierte Informationen. Die erforderliche ENGINE-Klausel gibt den Namen der UDR-Engine an, die die Kommunikation zwischen Firebird und dem externen Modul handhabt. Die optionale AS-Klausel akzeptiert ein String-Literal “body”, das von der Engine oder dem Modul für verschiedene Zwecke verwendet werden kann.

Externe UDR (User Defined Routine)-Funktionen, die mit CREATE FUNCTION …​ EXTERNAL …​ erstellt wurden, sollten nicht mit älteren UDFs (User Defined Functions) verwechselt werden, die mit DECLARE EXTERNAL FUNCTION deklariert wurden.

UDFs sind veraltet und ein Erbe früherer Firebird-Funktionen. Ihre Fähigkeiten sind den Fähigkeiten der neuen Art von externen UDR-Funktionen deutlich unterlegen.

Wer kann eine Funktion erstellen?

Die CREATE FUNCTION-Anweisung kann ausgeführt werden durch:

Der Benutzer, der die gespeicherte Funktion erstellt hat, wird deren Eigentümer.

CREATE FUNCTION-Beispiele
  1. Erstellen einer gespeicherten Funktion

    CREATE FUNCTION ADD_INT (A INT, B INT DEFAULT 0)
    RETURNS INT
    AS
    BEGIN
      RETURN A + B;
    END

    Aufruf einer Auswahl:

    SELECT ADD_INT(2, 3) AS R FROM RDB$DATABASE

    Aufruf innerhalb von PSQL-Code, der zweite optionale Parameter wird nicht angegeben:

    MY_VAR = ADD_INT(A);
  2. Erstellen einer deterministischen gespeicherten Funktion

    CREATE FUNCTION FN_E()
    RETURNS DOUBLE PRECISION DETERMINISTIC
    AS
    BEGIN
      RETURN EXP(1);
    END
  3. Erstellen einer gespeicherten Funktion mit Parametern vom Typ Tabellenspalte

    Gibt den Namen eines Typs nach Feldname und Wert zurück

    CREATE FUNCTION GET_MNEMONIC (
      AFIELD_NAME TYPE OF COLUMN RDB$TYPES.RDB$FIELD_NAME,
      ATYPE TYPE OF COLUMN RDB$TYPES.RDB$TYPE)
    RETURNS TYPE OF COLUMN RDB$TYPES.RDB$TYPE_NAME
    AS
    BEGIN
      RETURN (SELECT RDB$TYPE_NAME
              FROM RDB$TYPES
              WHERE RDB$FIELD_NAME = :AFIELD_NAME
              AND RDB$TYPE = :ATYPE);
    END
  4. Erstellen einer extern gespeicherten Funktion

    Erstellen Sie eine Funktion, die sich in einem externen Modul (UDR) befindet. Die Funktionsimplementierung befindet sich im externen Modul udrcpp_example. Der Name der Funktion innerhalb des Moduls ist wait_event.

    CREATE FUNCTION wait_event (
      event_name varchar (31) CHARACTER SET ascii
    ) RETURNS INTEGER
    EXTERNAL NAME 'udrcpp_example!Wait_event'
    ENGINE udr
  5. Erstellen einer gespeicherten Funktion mit einer Unterfunktion

    Erstellen einer Funktion zum Konvertieren einer Zahl in das Hexadezimalformat.

    CREATE FUNCTION INT_TO_HEX (
      ANumber BIGINT ,
      AByte_Per_Number SMALLINT = 8)
    RETURNS CHAR (66)
    AS
    DECLARE VARIABLE xMod SMALLINT ;
    DECLARE VARIABLE xResult VARCHAR (64);
    DECLARE FUNCTION TO_HEX (ANum SMALLINT ) RETURNS CHAR
      AS
      BEGIN
        RETURN CASE ANum
          WHEN 0 THEN '0'
          WHEN 1 THEN '1'
          WHEN 2 THEN '2'
          WHEN 3 THEN '3'
          WHEN 4 THEN '4'
          WHEN 5 THEN '5'
          WHEN 6 THEN '6'
          WHEN 7 THEN '7'
          WHEN 8 THEN '8'
          WHEN 9 THEN '9'
          WHEN 10 THEN 'A'
          WHEN 11 THEN 'B'
          WHEN 12 THEN 'C'
          WHEN 13 THEN 'D'
          WHEN 14 THEN 'E'
          WHEN 15 THEN 'F'
          ELSE NULL
        END;
      END
    BEGIN
      xMod = MOD (ANumber, 16);
      ANumber = ANumber / 16;
      xResult = TO_HEX (xMod);
      WHILE (ANUMBER> 0) DO
      BEGIN
        xMod = MOD (ANumber, 16);
        ANumber = ANumber / 16;
        xResult = TO_HEX (xMod) || xResult;
      END
      RETURN '0x' || LPAD (xResult, AByte_Per_Number * 2, '0' );
    END

5.9.2. ALTER FUNCTION

Verwendet für

Ändern einer vorhandenen gespeicherten Funktion

Verfügbar in

DSQL

Syntax
ALTER FUNCTION funcname
  [ ( [ <in_params> ] ) ]
  RETURNS <domain_or_non_array_type> [COLLATE collation]
  [DETERMINISTIC]
  <module-body>

!! Vgl. Syntax CREATE FUNCTION für weitere Regeln !!

Die ALTER FUNCTION-Anweisung erlaubt die folgenden Änderungen an einer gespeicherten Funktionsdefinition:

  • der Satz und die Eigenschaften des Eingangs- und Ausgangstyps

  • lokale Variablen, benannte Cursor und Unterprogramme

  • Code im Hauptteil der gespeicherten Prozedur

Für externe Funktionen (UDR) können Sie den Einstiegspunkt und den Engine-Namen ändern. Für ältere externe Funktionen, die mit DECLARE EXTERNAL FUNCTION deklariert wurden – auch als UDFs bekannt – ist es nicht möglich, in PSQL zu konvertieren und umgekehrt.

Nachdem ALTER FUNCTION ausgeführt wurde, bleiben bestehende Privilegien intakt und Abhängigkeiten werden nicht beeinflusst.

Achten Sie darauf, die Anzahl und den Typ der Eingabeparameter und den Ausgabetyp einer gespeicherten Funktion zu ändern. Vorhandener Anwendungscode und Prozeduren, Funktionen und Trigger, die ihn aufrufen, könnten ungültig werden, weil die neue Beschreibung der Parameter nicht mit dem alten Aufrufformat kompatibel ist. Informationen zur Fehlerbehebung in einer solchen Situation finden Sie im Artikel Das RDB$VALID_BLR-Feld im Anhang.

Wer kann eine Funktion ändern

Die ALTER FUNCTION-Anweisung kann ausgeführt werden durch:

  • Administratoren

  • Inhaber der gespeicherten Funktion

  • Benutzer mit der Berechtigung ALTER ANY FUNCTION

Beispiele für ALTER FUNCTION
Ändern einer gespeicherten Funktion
ALTER FUNCTION ADD_INT(A INT, B INT, C INT)
RETURNS INT
AS
BEGIN
  RETURN A + B + C;
END

5.9.3. CREATE OR ALTER FUNCTION

Verwendet für

Erstellen einer neuen oder Ändern einer vorhandenen gespeicherten Funktion

Verfügbar in

DSQL

Syntax
CREATE OR ALTER FUNCTION funcname
  [ ( [ <in_params> ] ) ]
  RETURNS <domain_or_non_array_type> [COLLATE collation]
  [DETERMINISTIC]
  <module-body>

!! Vgl. Syntax CREATE FUNCTION für weitere Regeln !!

Die Anweisung CREATE OR ALTER FUNCTION erstellt eine neue gespeicherte Funktion oder ändert eine vorhandene. Wenn die gespeicherte Funktion nicht existiert, wird sie durch transparentes Aufrufen einer CREATE FUNCTION-Anweisung erstellt. Wenn die Funktion bereits existiert, wird sie geändert und kompiliert (durch ALTER FUNCTION), ohne ihre bestehenden Privilegien und Abhängigkeiten zu beeinträchtigen.

Beispiele für CREATE OR ALTER FUNCTION
Erstellen Sie eine neue oder ändern Sie eine vorhandene gespeicherte Funktion
CREATE OR ALTER FUNCTION ADD_INT(A INT, B INT DEFAULT 0)
RETURNS INT
AS
BEGIN
  RETURN A + B;
END

5.9.4. DROP FUNCTION

Verwendet für

Löschen einer gespeicherten Funktion

Verfügbar in

DSQL

Syntax
DROP FUNCTION funcname
Tabelle 45. DROP FUNCTION-Anweisungsparameter
Parameter Beschreibung

funcname

Gespeicherter Funktionsname bestehend aus bis zu 31 Zeichen. Muss unter allen Funktionsnamen in der Datenbank eindeutig sein.

Die DROP FUNCTION-Anweisung löscht eine vorhandene gespeicherte Funktion. Wenn die gespeicherte Funktion Abhängigkeiten aufweist, schlägt der Versuch, sie zu löschen, fehl und der entsprechende Fehler wird ausgegeben.

Wer kann eine Funktion löschen?

Die DROP FUNCTION-Anweisung kann ausgeführt werden durch:

  • Administratoren

  • Inhaber der gespeicherten Funktion

  • Benutzer mit dem Privileg DROP ANY FUNCTION

Beispiele für DROP FUNCTION
DROP FUNCTION ADD_INT;

5.9.5. RECREATE FUNCTION

Verwendet für

Erstellen einer neuen gespeicherten Funktion oder Neuerstellen einer vorhandenen Funktion

Verfügbar in

DSQL

Syntax
RECREATE FUNCTION funcname
  [ ( [ <in_params> ] ) ]
  RETURNS <domain_or_non_array_type> [COLLATE collation]
  [DETERMINISTIC]
  <module-body>

!! Vgl. Syntax CREATE FUNCTION für weitere Regeln !!

Die Anweisung 'RECREATE FUNCTION' erstellt eine neue gespeicherte Funktion oder erstellt eine vorhandene neu. Wenn es bereits eine Funktion mit diesem Namen gibt, versucht die Engine, sie zu löschen und dann eine neue zu erstellen. Das Neuerstellen einer vorhandenen Funktion schlägt bei COMMIT fehl, wenn die Funktion Abhängigkeiten hat.

Beachten Sie, dass Abhängigkeitsfehler erst in der COMMIT-Phase dieser Operation erkannt werden.

Nachdem eine Prozedur erfolgreich neu erstellt wurde, werden vorhandene Berechtigungen zum Ausführen der gespeicherten Funktion und der Berechtigungen der gespeicherten Funktion selbst werden verworfen.

Beispiele für RECREATE FUNCTION
Erstellen oder Wiederherstellen einer gespeicherten Funktion
RECREATE FUNCTION ADD_INT(A INT, B INT DEFAULT 0)
RETURNS INT
AS
BEGIN
  RETURN A + B;
EN

5.10. EXTERNAL FUNCTION

Externe Funktionen, auch bekannt als “User-Defined Functions” (UDFs) sind Programme, die in einer externen Programmiersprache geschrieben und in dynamisch geladenen Bibliotheken gespeichert sind. Einmal in einer Datenbank deklariert, stehen sie in dynamischen und prozeduralen Anweisungen zur Verfügung, als wären sie in der Sprache SQL implementiert.

Externe Funktionen erweitern die Möglichkeiten der Datenverarbeitung mit SQL erheblich. Um einer Datenbank eine Funktion zur Verfügung zu stellen, wird sie mit der Anweisung DECLARE EXTERNAL FUNCTION deklariert.

Die Bibliothek, die eine Funktion enthält, wird geladen, wenn eine darin enthaltene Funktion aufgerufen wird.

Externe Funktionen, die als EXTERNAL FUNCTION deklariert wurden, sind ein Erbe früherer Versionen von Firebird. Ihre Fähigkeiten sind den Fähigkeiten des neuen Typs externer Funktionen, UDR (User-Defined Routine), unterlegen. Solche Funktionen werden als CREATE FUNCTION …​ EXTERNAL …​ deklariert. Siehe CREATE FUNCTION für Details.

Externe Funktionen können in mehr als einer Bibliothek enthalten sein — oder “Modul”, wie es in der Syntax genannt wird.

UDFs sind grundsätzlich unsicher. Wir empfehlen, ihre Verwendung nach Möglichkeit zu vermeiden und UDFs in Ihrer Datenbankkonfiguration zu deaktivieren (UdfAccess = None in firebird.conf). Wenn Sie nativen Code aus Ihrer Datenbank aufrufen müssen, verwenden Sie stattdessen eine externe UDR-Engine.

Siehe auch

FUNCTION

5.10.1. DECLARE EXTERNAL FUNCTION

Verwendet für

Deklarieren einer benutzerdefinierten Funktion (UDF) zur Datenbank

Verfügbar in

DSQL, ESQL

Syntax
DECLARE EXTERNAL FUNCTION funcname
  [{ <arg_desc_list> | ( <arg_desc_list> ) }]
  RETURNS { <return_value> | ( <return_value> ) }
  ENTRY_POINT 'entry_point' MODULE_NAME 'library_name'

<arg_desc_list> ::=
  <arg_type_decl> [, <arg_type_decl> ...]

<arg_type_decl> ::=
  <udf_data_type> [BY {DESCRIPTOR | SCALAR_ARRAY} | NULL]

<udf_data_type> ::=
    <scalar_datatype>
  | BLOB
  | CSTRING(length) [ CHARACTER SET charset ]

<scalar_datatype> ::=
  !! Vgl. Syntax für Skalardatentypen !!

<return_value> ::=
  { <udf_data_type> | PARAMETER param_num }
  [{ BY VALUE | BY DESCRIPTOR [FREE_IT] | FREE_IT }]
Tabelle 46. DECLARE EXTERNAL FUNCTION-Anweisungsparameter
Parameter Beschreibung

funcname

Funktionsname in der Datenbank. Er kann aus bis zu 31 Zeichen bestehen. Er sollte unter allen internen und externen Funktionsnamen in der Datenbank eindeutig sein und muss nicht mit dem Namen identisch sein, der über ENTRY_POINT aus der UDF-Bibliothek exportiert wird.

entry_point

Der exportierte Name der Funktion

library_name

Der Name des Moduls (MODULE_NAME), aus dem die Funktion exportiert wird. Dies ist der Name der Datei ohne die Dateierweiterung “.dll” oder “.so”.

length

Die maximale Länge einer nullterminierten Zeichenfolge, angegeben in Byte

charset

Zeichensatz des CSTRING

param_num

Die Nummer des Eingabeparameters, nummeriert von 1 in der Liste der Eingabeparameter in der Deklaration, die den Datentyp beschreibt, der von der Funktion zurückgegeben wird

Die Anweisung DECLARE EXTERNAL FUNCTION stellt eine benutzerdefinierte Funktion in der Datenbank zur Verfügung. UDF-Deklarationen müssen in jeder Datenbank vorgenommen werden, die sie verwenden wird. Es müssen keine UDFs deklariert werden, die niemals verwendet werden.

Der Name der externen Funktion muss unter allen Funktionsnamen eindeutig sein. Er kann sich vom exportierten Namen der Funktion unterscheiden, wie im Argument ENTRY_POINT angegeben.

DECLARE EXTERNAL FUNCTION-Eingabeparameter

Die Eingabeparameter der Funktion folgen dem Namen der Funktion und werden durch Kommas getrennt. Für jeden Parameter ist ein SQL-Datentyp angegeben. Arrays können nicht als Funktionsparameter verwendet werden. Zusätzlich zu den SQL-Typen steht der Typ CSTRING zur Angabe eines nullterminierten Strings mit einer maximalen Länge von LENGTH Bytes zur Verfügung. Es gibt mehrere Mechanismen, um einen Parameter von der Firebird-Engine an eine externe Funktion zu übergeben. Jeder dieser Mechanismen wird unten diskutiert.

Standardmäßig werden Eingabeparameter per Referenz übergeben. Es gibt keine separate Klausel, die explizit angibt, dass Parameter als Referenz übergeben werden.

Wenn ein NULL-Wert als Referenz übergeben wird, wird dieser in das Äquivalent von Null umgewandelt, zum Beispiel eine Zahl `0’ oder eine leere Zeichenfolge (“''”). Wenn nach einem Parameter das Schlüsselwort `NULL angegeben wird, wird bei der Übergabe von NULL-Werten der Nullzeiger an die externe Funktion übergeben.

Das Deklarieren einer Funktion mit dem Schlüsselwort NULL garantiert nicht, dass die Funktion einen NULL-Eingabeparameter korrekt behandelt. Jede Funktion muss geschrieben oder umgeschrieben werden, um NULL-Werte korrekt zu behandeln. Verwenden Sie immer die vom Entwickler bereitgestellte Funktionsdeklaration.

Wenn BY DESCRIPTOR angegeben ist, wird der Eingabeparameter vom Deskriptor übergeben. In diesem Fall erhält der UDF-Parameter einen Zeiger auf eine interne Struktur, die als Deskriptor bekannt ist. Der Deskriptor enthält Informationen über Datentyp, Untertyp, Genauigkeit, Zeichensatz und Kollation, Skalierung, einen Zeiger auf die Daten selbst und einige Flags, einschließlich des NULL-Indikators. Diese Deklaration funktioniert nur, wenn die externe Funktion mit einem Handle geschrieben wird.

Wenn ein Funktionsparameter per Deskriptor übergeben wird, wird der übergebene Wert nicht in den deklarierten Datentyp umgewandelt.

Die Klausel BY SCALAR_ARRAY wird verwendet, wenn Arrays als Eingabeparameter übergeben werden. Im Gegensatz zu anderen Typen können Sie kein Array aus einer UDF zurückgeben.

Klauseln und Schlüsselwörter
RETURNS-Klausel

(Erforderlich) gibt den von der Funktion zurückgegebenen Ausgabeparameter an. Eine Funktion ist skalar, sie gibt einen Wert (Ausgabeparameter) zurück. Der Ausgabeparameter kann einen beliebigen SQL-Typ (außer einem Array oder einem Array-Element) oder ein nullterminierter String (CSTRING) sein. Der Ausgabeparameter kann als Referenz (Standard), als Deskriptor oder als Wert übergeben werden. Wenn die Klausel BY DESCRIPTOR angegeben ist, wird der Ausgabeparameter vom Deskriptor übergeben. Wenn die Klausel BY VALUE angegeben ist, wird der Ausgabeparameter als Wert übergeben.

PARAMETER-Schlüsselwort

gibt an, dass die Funktion den Wert des Parameters unter der Nummer param_num zurückgibt. Es ist notwendig, wenn Sie einen Wert vom Datentyp BLOB zurückgeben müssen.

FREE_IT-Schlüsselwort

bedeutet, dass der zum Speichern des Rückgabewerts zugewiesene Speicher freigegeben wird, nachdem die Funktion ausgeführt wurde. Es wird nur verwendet, wenn der Speicher im UDF dynamisch allokiert wurde. In einem solchen UDF muss der Speicher mit Hilfe der Funktion ib_util_malloc aus dem Modul ib_util allokiert werden, eine Voraussetzung für die Kompatibilität mit den im Firebird-Code verwendeten Funktionen und im Code der ausgelieferten UDF-Module zum Zuweisen und Freigeben von Speicher.

ENTRY_POINT-Klausel

gibt den Namen des Einstiegspunkts (den Namen der importierten Funktion) an, wie er aus dem Modul exportiert wurde.

MODULE_NAME-Klausel

definiert den Namen des Moduls, in dem sich die exportierte Funktion befindet. Der Link zum Modul sollte nicht der vollständige Pfad und die Erweiterung der Datei sein, wenn dies vermieden werden kann. Wenn sich das Modul am Standardspeicherort (im ../UDF-Unterverzeichnis des Firebird-Server-Roots) oder an einem explizit in firebird.conf konfigurierten Speicherort befindet, erleichtert es das Verschieben der Datenbank zwischen verschiedene Plattformen. Der Parameter UDFAccess in der Datei firebird.conf ermöglicht die Konfiguration von Zugriffsbeschränkungen auf externe Funktionsmodule.

Jeder mit der Datenbank verbundene Benutzer kann eine externe Funktion (UDF) deklarieren.

Wer kann eine externe Funktion erstellen?

Die Anweisung DECLARE EXTERNAL FUNCTION kann ausgeführt werden durch:

Der Benutzer, der die Funktion erstellt hat, wird ihr Besitzer.

Beispiele für die Verwendung von DECLARE EXTERNAL FUNCTION
  1. Deklarieren der externen Funktion addDay im Modul fbudf. Die Eingabe- und Ausgabeparameter werden als Referenz übergeben.

    DECLARE EXTERNAL FUNCTION addDay
      TIMESTAMP, INT
      RETURNS TIMESTAMP
      ENTRY_POINT 'addDay' MODULE_NAME 'fbudf';
  2. Deklarieren der externen Funktion invl im Modul fbudf. Die Eingabe- und Ausgabeparameter werden per Deskriptor übergeben.

    DECLARE EXTERNAL FUNCTION invl
      INT BY DESCRIPTOR, INT BY DESCRIPTOR
      RETURNS INT BY DESCRIPTOR
      ENTRY_POINT 'idNvl' MODULE_NAME 'fbudf';
  3. Deklarieren der externen Funktion isLeapYear im Modul fbudf. Der Eingabeparameter wird als Referenz übergeben, während der Ausgabeparameter als Wert übergeben wird.

    DECLARE EXTERNAL FUNCTION isLeapYear
      TIMESTAMP
      RETURNS INT BY VALUE
      ENTRY_POINT 'isLeapYear' MODULE_NAME 'fbudf';
  4. Deklarieren der externen Funktion i64Truncate im Modul fbudf. Die Eingabe- und Ausgabeparameter werden per Deskriptor übergeben. Als Rückgabewert wird der zweite Parameter der Funktion verwendet.

    DECLARE EXTERNAL FUNCTION i64Truncate
      NUMERIC(18) BY DESCRIPTOR, NUMERIC(18) BY DESCRIPTOR
      RETURNS PARAMETER 2
      ENTRY_POINT 'fbtruncate' MODULE_NAME 'fbudf';

5.10.2. ALTER EXTERNAL FUNCTION

Verwendet für

Ändern des Einstiegspunkts und/oder des Modulnamens für eine benutzerdefinierte Funktion (UDF)

Verfügbar in

DSQL

Syntax
ALTER EXTERNAL FUNCTION funcname
  [ENTRY_POINT 'new_entry_point']
  [MODULE_NAME 'new_library_name']
Tabelle 47. ALTER EXTERNAL FUNCTION-Anweisungsparameter
Parameter Beschreibung

funcname

Funktionsname in der Datenbank

new_entry_point

Der neue exportierte Name der Funktion

new_library_name

Der neue Name des Moduls (MODULE_NAME aus dem die Funktion exportiert wird). Dies ist der Name der Datei ohne die Dateierweiterung “.dll” oder “.so”.

Die Anweisung ALTER EXTERNAL FUNCTION ändert den Einstiegspunkt und/oder den Modulnamen für eine benutzerdefinierte Funktion (UDF). Vorhandene Abhängigkeiten bleiben erhalten, nachdem die Anweisung ausgeführt wird, die die Änderung(en) enthält.

Die ENTRY_POINT-Klausel

dient zur Angabe des neuen Einstiegspunkts (der Name der Funktion, wie er aus dem Modul exportiert wurde).

Die MODULE_NAME-Klausel

dient zur Angabe des neuen Namens des Moduls, in dem sich die exportierte Funktion befindet.

Jeder mit der Datenbank verbundene Benutzer kann den Einstiegspunkt und den Modulnamen ändern.

Wer kann eine externe Funktion ändern?

Die Anweisung ALTER EXTERNAL FUNCTION kann ausgeführt werden durch:

  • Administratoren

  • Inhaber der externen Funktion

  • Benutzer mit der Berechtigung ALTER ANY FUNCTION

Beispiele zur Verwendung ALTER EXTERNAL FUNCTION
Ändern des Einstiegspunkts für eine externe Funktion
ALTER EXTERNAL FUNCTION invl ENTRY_POINT 'intNvl';
Ändern des Modulnamens für eine externe Funktion
ALTER EXTERNAL FUNCTION invl MODULE_NAME 'fbudf2';

5.10.3. DROP EXTERNAL FUNCTION

Verwendet für

Entfernen einer benutzerdefinierten Funktion (UDF) aus einer Datenbank

Verfügbar in

DSQL, ESQL

Syntax
DROP EXTERNAL FUNCTION funcname
Tabelle 48. DROP EXTERNAL FUNCTION-Anweisungsparameter
Parameter Beschreibung

funcname

Funktionsname in der Datenbank

Die Anweisung DROP EXTERNAL FUNCTION löscht die Deklaration einer benutzerdefinierten Funktion aus der Datenbank. Wenn Abhängigkeiten von der externen Funktion bestehen, schlägt die Anweisung fehl und der entsprechende Fehler wird ausgegeben.

Jeder mit der Datenbank verbundene Benutzer kann die Deklaration einer internen Funktion löschen.

Wer kann eine externe Funktion löschen?

Die Anweisung DROP EXTERNAL FUNCTION kann ausgeführt werden durch:

  • Administratoren

  • Inhaber der externen Funktion

  • Benutzer mit dem Privileg DROP ANY FUNCTION

Beispiel für DROP EXTERNAL FUNCTION
Löschen der Deklaration der Funktion addDay.
DROP EXTERNAL FUNCTION addDay;

5.11. PACKAGE

Ein Paket ist eine Gruppe von Prozeduren und Funktionen, die als eine Einheit verwaltet werden.

5.11.1. CREATE PACKAGE

Verwendet für

Paket-Header deklarieren

Verfügbar in

DSQL

Syntax
CREATE PACKAGE package_name
AS
BEGIN
  [ <package_item> ... ]
END

<package_item> ::=
    <function_decl>;
  | <procedure_decl>;

<function_decl> ::=
  FUNCTION funcname [ ( [ <in_params> ] ) ]
  RETURNS <domain_or_non_array_type> [COLLATE collation]
  [DETERMINISTIC]

<procedure_decl> ::=
  PROCEDURE procname [ ( [ <in_params> ] ) ]
  [RETURNS (<out_params>)]

<in_params> ::= <inparam> [, <inparam> ... ]

<inparam> ::= <param_decl> [ { = | DEFAULT } <value> ]

<out_params> ::= <outparam> [, <outparam> ...]

<outparam> ::= <param_decl>

<value> ::= { literal | NULL | context_var }

<param-decl> ::= paramname <domain_or_non_array_type> [NOT NULL]
  [COLLATE collation]

<domain_or_non_array_type> ::=
  !! Siehe auch Skalardatentypen-Syntax !!
Tabelle 49. CREATE PACKAGE-Anweisungsparameter
Parameter Beschreibung

package_name

Paketname bestehend aus bis zu 31 Zeichen. Der Paketname muss unter allen Paketnamen eindeutig sein.

function_decl

Funktionsdeklaration

procedure_decl

Prozedurdeklaration

func_name

Funktionsname bestehend aus bis zu 31 Zeichen. Der Funktionsname muss innerhalb des Pakets eindeutig sein.

proc_name

Prozedurname bestehend aus bis zu 31 Zeichen. Der Funktionsname muss innerhalb des Pakets eindeutig sein.

collation

Sortierreihenfolge

inparam

Deklaration der Eingabeparameter

outparam

Deklaration der Ausgabeparameter

literal

Ein Literalwert, der mit dem Datentyp des Parameters zuweisungskompatibel ist

context_var

Jede Kontextvariable, die mit dem Datentyp des Parameters zuweisungskompatibel ist

paramname

Der Name eines Eingabeparameters einer Prozedur oder Funktion oder eines Ausgabeparameters einer Prozedur. Er kann aus bis zu 31 Zeichen bestehen. Der Name des Parameters muss unter den Eingabe- und Ausgabeparametern der Prozedur oder Funktion eindeutig sein.

Die Anweisung CREATE PACKAGE erstellt einen neuen Paket-Header. Im Paketheader deklarierte Routinen (Prozeduren und Funktionen) sind außerhalb des Pakets unter Verwendung des vollständigen Bezeichners (package_name.proc_name oder package_name.func_name) verfügbar. Routinen, die nur im Paketrumpf definiert sind – aber nicht im Paketkopf – sind außerhalb des Pakets nicht sichtbar.

Paketprozedur- und Funktionsnamen können globale Routinen überschatten

Wenn ein Paketheader oder Paketrumpf eine Prozedur oder Funktion mit demselben Namen wie eine gespeicherte Prozedur oder Funktion im globalen Namespace deklariert, ist es nicht möglich, diese globale Prozedur oder Funktion aus dem Paketrumpf aufzurufen. In diesem Fall wird immer die Prozedur oder Funktion des Pakets aufgerufen.

Aus diesem Grund wird empfohlen, dass sich die Namen von gespeicherten Prozeduren und Funktionen in Paketen nicht mit Namen von gespeicherten Prozeduren und Funktionen im globalen Namespace überschneiden.

Statement-Terminatoren

Einige SQL-Anweisungseditoren – insbesondere das mit Firebird gelieferte Dienstprogramm isql und möglicherweise einige Editoren von Drittanbietern – verwenden eine interne Konvention, die erfordert, dass alle Anweisungen mit einem Semikolon abgeschlossen werden. Dies führt beim Codieren in diesen Umgebungen zu einem Konflikt mit der PSQL-Syntax. Wenn Sie dieses Problem und seine Lösung nicht kennen, lesen Sie bitte die Details im PSQL-Kapitel im Abschnitt Umschalten des Terminators in isql.

Verfahrens- und Funktionsparameter

Ausführliche Informationen zu Parametern für gespeicherte Prozeduren finden Sie unter Parameter in CREATE PROCEDURE.

Einzelheiten zu Funktionsparametern finden Sie unter Parameter in CREATE FUNCTION.

Wer kann ein Paket erstellen

Die CREATE PACKAGE-Anweisung kann ausgeführt werden durch:

Der Benutzer, der den Paketheader erstellt hat, wird sein Besitzer.

Beispiele für CREATE PACKAGE
Erstellen Sie einen Paket-Header
CREATE PACKAGE APP_VAR
AS
BEGIN
  FUNCTION GET_DATEBEGIN() RETURNS DATE DETERMINISTIC;
  FUNCTION GET_DATEEND() RETURNS DATE DETERMINISTIC;
  PROCEDURE SET_DATERANGE(ADATEBEGIN DATE,
      ADATEEND DATE DEFAULT CURRENT_DATE);
END

5.11.2. ALTER PACKAGE

Verwendet für

Ändern des Paketheaders

Verfügbar in

DSQL

Syntax
ALTER PACKAGE package_name
AS
BEGIN
  [ <package_item> ... ]
END

!! Vgl. Syntax CREATE PACKAGE für weitere Regeln!!

Die ALTER PACKAGE-Anweisung modifiziert den Paket-Header. Es kann verwendet werden, um die Anzahl und Definition von Prozeduren und Funktionen einschließlich ihrer Ein- und Ausgabeparameter zu ändern. Der Quelltext und die kompilierte Form des Paketkörpers werden jedoch beibehalten, obwohl der Körper nach der Änderung des Paketheaders möglicherweise inkompatibel ist. Die Gültigkeit eines Paketkörpers für den definierten Header wird in der Spalte RDB$PACKAGES.RDB$VALID_BODY_FLAG gespeichert.

Wer kann ein Paket ändern

Die ALTER PACKAGE-Anweisung kann ausgeführt werden durch:

  • Administratoren

  • Der Besitzer des Pakets

  • Benutzer mit der Berechtigung ALTER ANY PACKAGE

Beispiel für ALTER PACKAGE
Ändern eines Paketheaders
ALTER PACKAGE APP_VAR
AS
BEGIN
  FUNCTION GET_DATEBEGIN() RETURNS DATE DETERMINISTIC;
  FUNCTION GET_DATEEND() RETURNS DATE DETERMINISTIC;
  PROCEDURE SET_DATERANGE(ADATEBEGIN DATE,
      ADATEEND DATE DEFAULT CURRENT_DATE);
END

5.11.3. CREATE OR ALTER PACKAGE

Verwendet für

Erstellen eines neuen oder Ändern eines bestehenden Paket-Headers

Verfügbar in

DSQL

Syntax
CREATE OR ALTER PACKAGE package_name
AS
BEGIN
  [ <package_item> ... ]
END

!! Siehe auch Syntax CREATE PACKAGE für weitere Regeln!!

Die Anweisung CREATE OR ALTER PACKAGE erstellt ein neues Paket oder ändert einen vorhandenen Paket-Header. Existiert der Paket-Header nicht, wird er mit CREATE PACKAGE erstellt. Wenn es bereits existiert, wird es mit ALTER PACKAGE modifiziert, während bestehende Privilegien und Abhängigkeiten beibehalten werden.

Beispiel für CREATE OR ALTER PACKAGE
Erstellen eines neuen oder Ändern eines vorhandenen Paketheaders
CREATE OR ALTER PACKAGE APP_VAR
AS
BEGIN
  FUNCTION GET_DATEBEGIN() RETURNS DATE DETERMINISTIC;
  FUNCTION GET_DATEEND() RETURNS DATE DETERMINISTIC;
  PROCEDURE SET_DATERANGE(ADATEBEGIN DATE,
      ADATEEND DATE DEFAULT CURRENT_DATE);
END

5.11.4. DROP PACKAGE

Verwendet für

Einen Paket-Header löschen

Verfügbar in

DSQL

Syntax
DROP PACKAGE package_name
Tabelle 50. DROP PACKAGE-Anweisungsparameter
Parameter Beschreibung

package_name

Paketname

Die DROP PACKAGE-Anweisung löscht einen vorhandenen Paket-Header. Wenn ein Paketkörper vorhanden ist, wird er zusammen mit dem Paketkopf gelöscht. Wenn noch Abhängigkeiten vom Paket bestehen, wird ein Fehler ausgegeben.

Wer kann ein Paket abgeben

Die DROP PACKAGE-Anweisung kann ausgeführt werden durch:

  • Administratoren

  • Der Besitzer des Pakets

  • Benutzer mit der Berechtigung DROP ANY PACKAGE

Beispiel für DROP PACKAGE
Einen Paket-Header löschen
DROP PACKAGE APP_VAR

5.11.5. RECREATE PACKAGE

Verwendet für

Erstellen eines neuen oder erneuten Erstellens eines vorhandenen Paketheaders

Verfügbar in

DSQL

Syntax
RECREATE PACKAGE package_name
AS
BEGIN
  [ <package_item> ... ]
END

!! Siehe auch Syntax CREATE PACKAGE für weitere Regeln!!

Die Anweisung RECREATE PACKAGE erstellt ein neues Paket oder erstellt einen vorhandenen Paket-Header neu. Wenn bereits ein Paketheader mit demselben Namen vorhanden ist, wird dieser durch diese Anweisung zuerst gelöscht und dann ein neuer Paketheader erstellt. Es ist nicht möglich, den Paketheader neu zu erstellen, wenn noch Abhängigkeiten von dem vorhandenen Paket bestehen oder wenn der Hauptteil des Pakets vorhanden ist. Bestehende Privilegien des Pakets selbst werden nicht beibehalten, ebenso wenig Privilegien zum Ausführen der Prozeduren oder Funktionen des Pakets.

Beispiel für RECREATE PACKAGE
Erstellen eines neuen oder erneuten Erstellens eines vorhandenen Paketheaders
RECREATE PACKAGE APP_VAR
AS
BEGIN
  FUNCTION GET_DATEBEGIN() RETURNS DATE DETERMINISTIC;
  FUNCTION GET_DATEEND() RETURNS DATE DETERMINISTIC;
  PROCEDURE SET_DATERANGE(ADATEBEGIN DATE,
      ADATEEND DATE DEFAULT CURRENT_DATE);
END

5.12. PACKAGE BODY

5.12.1. CREATE PACKAGE BODY

Verwendet für

Erstellen des Paketrumpfes

Verfügbar in

DSQL

Syntax
CREATE PACKAGE BODY name
AS
BEGIN
  [ <package_item> ... ]
  [ <package_body_item> ... ]
END

<package_item> ::=
  !! Siehe auch CREATE PACKAGE-Syntax !!

<package_body_item> ::=
  <function_impl> |
  <procedure_impl>

<function_impl> ::=
  FUNCTION funcname [ ( [ <in_params> ] ) ]
  RETURNS <domain_or_non_array_type> [COLLATE collation]
  [DETERMINISTIC]
  <module-body>

<procedure_impl> ::=
  PROCEDURE procname [ ( [ <in_params> ] ) ]
  [RETURNS (<out_params>)]
  <module-body>

<module-body> ::=
  !! Siehe auch Syntax des Modul-Bodys !!

<in_params> ::=
  !! Siehe auch CREATE PACKAGE-Syntax !!
  !! Siehe auch die Regeln weiter unten !!

<out_params> ::=
  !! Siehe auch CREATE PACKAGE-Syntax !!

<domain_or_non_array_type> ::=
  !! Siehe auch Syntax der Skalardatentypen !!
Tabelle 51. CREATE PACKAGE BODY-Anweisungsparameter
Parameter Beschreibung

package_name

Paketname bestehend aus bis zu 31 Zeichen. Der Paketname muss unter allen Paketnamen eindeutig sein.

function_impl

Funktionsimplementierung. Im Wesentlichen eine CREATE FUNCTION-Anweisung ohne CREATE.

procedure_impl

Verfahrensimplementierung Im Wesentlichen eine CREATE PROCEDURE-Anweisung ohne CREATE.

func_name

Funktionsname bestehend aus bis zu 31 Zeichen. Der Funktionsname muss innerhalb des Pakets eindeutig sein.

collation

Sortierreihenfolge

proc_name

Prozedurname bestehend aus bis zu 31 Zeichen. Der Funktionsname muss innerhalb des Pakets eindeutig sein.

Die Anweisung CREATE PACKAGE BODY erstellt einen neuen Paketkörper. Der Paketkörper kann erst erstellt werden, nachdem der Paketkopf erstellt wurde. Wenn kein Paketheader mit dem Namen package_name vorhanden ist, wird ein entsprechender Fehler ausgegeben.

Alle im Paketkopf deklarierten Prozeduren und Funktionen müssen im Paketrumpf implementiert werden. Zusätzliche Prozeduren und Funktionen dürfen nur im Paketrumpf definiert und implementiert werden. Prozeduren und Funktionen, die im Paketrumpf definiert, aber nicht im Paketkopf definiert sind, sind außerhalb des Paketrumpfs nicht sichtbar.

Die Namen von Prozeduren und Funktionen, die im Paketrumpf definiert sind, müssen unter den Namen von Prozeduren und Funktionen, die im Paketkopf definiert und im Paketrumpf implementiert sind, eindeutig sein.

Paketprozedur- und Funktionsnamen können globale Routinen überschatten

Wenn ein Paketheader oder Paketrumpf eine Prozedur oder Funktion mit demselben Namen wie eine gespeicherte Prozedur oder Funktion im globalen Namespace deklariert, ist es nicht möglich, diese globale Prozedur oder Funktion aus dem Paketrumpf aufzurufen. In diesem Fall wird immer die Prozedur oder Funktion des Pakets aufgerufen.

Aus diesem Grund wird empfohlen, dass sich die Namen von gespeicherten Prozeduren und Funktionen in Paketen nicht mit Namen von gespeicherten Prozeduren und Funktionen im globalen Namespace überschneiden.

Regeln
  • Im Paketrumpf müssen alle Prozeduren und Funktionen mit derselben Signatur implementiert werden, die im Header und am Anfang des Paketrumpfs deklariert ist

  • Die Standardwerte für Prozedur- oder Funktionsparameter können nicht überschrieben werden (wie im Paketkopf oder in <package_item> angegeben). Dies bedeutet, dass Standardwerte nur in <package_body_item> für Prozeduren oder Funktionen definiert werden können, die nicht im Paketkopf oder früher im Paketrumpf definiert wurden.

UDF-Deklarationen (DECLARE EXTERNAL FUNCTION) werden für Pakete nicht unterstützt. Verwenden Sie stattdessen UDR.

Wer kann einen Paketkörper erstellen

Die Anweisung CREATE PACKAGE BODY kann ausgeführt werden durch:

  • Administratoren

  • Der Besitzer des Pakets

  • Benutzer mit der Berechtigung ALTER ANY PACKAGE

Beispiel für CREATE PACKAGE BODY
Erstellen des Paketkörpers
CREATE PACKAGE BODY APP_VAR
AS
BEGIN
  - Gibt das Startdatum der Periode zurück
  FUNCTION GET_DATEBEGIN() RETURNS DATE DETERMINISTIC
  AS
  BEGIN
    RETURN RDB$GET_CONTEXT('USER_SESSION', 'DATEBEGIN');
  END
  - Gibt das Enddatum des Zeitraums zurück
  FUNCTION GET_DATEEND() RETURNS DATE DETERMINISTIC
  AS
  BEGIN
    RETURN RDB$GET_CONTEXT('USER_SESSION', 'DATEEND');
  END
  - Legt den Datumsbereich des Arbeitszeitraums fest
  PROCEDURE SET_DATERANGE(ADATEBEGIN DATE, ADATEEND DATE)
  AS
  BEGIN
    RDB$SET_CONTEXT('USER_SESSION', 'DATEBEGIN', ADATEBEGIN);
    RDB$SET_CONTEXT('USER_SESSION', 'DATEEND', ADATEEND);
  END
END

5.12.2. ALTER PACKAGE BODY

Verwendet für

Ändern des Paketrumpfes

Verfügbar in

DSQL

Syntax
ALTER PACKAGE BODY name
AS
BEGIN
  [ <package_item> ... ]
  [ <package_body_item> ... ]
END

!! Siehe auch Syntax CREATE PACKAGE BODY für weitere Regeln !!

Die Anweisung ALTER PACKAGE BODY modifiziert den Paketrumpf. Es kann verwendet werden, um die Definition und Implementierung von Prozeduren und Funktionen des Paketkörpers zu ändern.

Siehe CREATE PACKAGE BODY für weitere Details.

Wer kann einen Paketkörper ändern?

Die Anweisung ALTER PACKAGE BODY kann ausgeführt werden durch:

  • Administratoren

  • Der Besitzer des Pakets

  • Benutzer mit der Berechtigung ALTER ANY PACKAGE

Beispiel für ALTER PACKAGE BODY
Ändern des Paketkörpers
ALTER PACKAGE BODY APP_VAR
AS
BEGIN
  - Gibt das Startdatum der Periode zurück
  FUNCTION GET_DATEBEGIN() RETURNS DATE DETERMINISTIC
  AS
  BEGIN
    RETURN RDB$GET_CONTEXT('USER_SESSION', 'DATEBEGIN');
  END
  - Gibt das Enddatum des Zeitraums zurück
  FUNCTION GET_DATEEND() RETURNS DATE DETERMINISTIC
  AS
  BEGIN
    RETURN RDB$GET_CONTEXT('USER_SESSION', 'DATEEND');
  END
  - Legt den Datumsbereich des Arbeitszeitraums fest
  PROCEDURE SET_DATERANGE(ADATEBEGIN DATE, ADATEEND DATE)
  AS
  BEGIN
    RDB$SET_CONTEXT('USER_SESSION', 'DATEBEGIN', ADATEBEGIN);
    RDB$SET_CONTEXT('USER_SESSION', 'DATEEND', ADATEEND);
  END
END

5.12.3. DROP PACKAGE BODY

Verwendet für

Löschen des Paketrumpfes

Verfügbar in

DSQL

Syntax
DROP PACKAGE package_name
Tabelle 52. DROP PACKAGE BODY-Anweisungsparameter
Parameter Beschreibung

package_name

Paketname

Die Anweisung DROP PACKAGE BODY löscht den Paketkörper.

Wer kann einen Paketkörper fallen lassen?

Die DROP PACKAGE BODY-Anweisung kann ausgeführt werden durch:

  • Administratoren

  • Der Besitzer des Pakets

  • Benutzer mit der Berechtigung ALTER ANY PACKAGE

Beispiel für DROP PACKAGE BODY
Löschen des Paketrumpfes
DROP PACKAGE BODY APP_VAR;

5.12.4. RECREATE PACKAGE BODY

Verwendet für

Erstellen eines neuen oder erneuten Erstellens eines vorhandenen Paketrumpfes

Verfügbar in

DSQL

Syntax
RECREATE PACKAGE BODY name
AS
BEGIN
  [ <package_item> ... ]
  [ <package_body_item> ... ]
END

!! Siehe auch Syntax CREATE PACKAGE BODY für weitere Regeln !!

Die Anweisung RECREATE PACKAGE BODY erstellt einen neuen oder erstellt einen bestehenden Paketkörper neu. Wenn bereits ein Paketkörper mit demselben Namen vorhanden ist, versucht die Anweisung, ihn zu löschen und dann einen neuen Paketkörper zu erstellen. Nach der Neuerstellung des Paketkörpers bleiben die Berechtigungen des Pakets und seiner Routinen erhalten.

Siehe CREATE PACKAGE BODY für weitere Details.

Beispiele für RECREATE PACKAGE BODY
Neuerstellen des Paketrumpfes
RECREATE PACKAGE BODY APP_VAR
AS
BEGIN
  - Gibt das Startdatum der Periode zurück
  FUNCTION GET_DATEBEGIN() RETURNS DATE DETERMINISTIC
  AS
  BEGIN
    RETURN RDB$GET_CONTEXT('USER_SESSION', 'DATEBEGIN');
  END
  - Gibt das Enddatum des Zeitraums zurück
  FUNCTION GET_DATEEND() RETURNS DATE DETERMINISTIC
  AS
  BEGIN
    RETURN RDB$GET_CONTEXT('USER_SESSION', 'DATEEND');
  END
  - Legt den Datumsbereich des Arbeitszeitraums fest
  PROCEDURE SET_DATERANGE(ADATEBEGIN DATE, ADATEEND DATE)
  AS
  BEGIN
    RDB$SET_CONTEXT('USER_SESSION', 'DATEBEGIN', ADATEBEGIN);
    RDB$SET_CONTEXT('USER_SESSION', 'DATEEND', ADATEEND);
  END
END

5.13. FILTER

Ein 'BLOB FILTER' ist ein Datenbankobjekt, das ein besonderer Typ einer externen Funktion ist, mit dem einzigen Zweck, ein 'BLOB'-Objekt in einem Format zu übernehmen und dieses in ein anderes Format umzuwandeln. Die Formate der BLOB-Objekte werden mit benutzerdefinierten BLOB-Subtypen spezifiziert.

Externe Funktionen zum Konvertieren von 'BLOB'-Typen werden in dynamischen Bibliotheken gespeichert und bei Bedarf geladen.

Weitere Informationen zu 'BLOB'-Subtypen finden Sie unter Binärdatentypen.

5.13.1. DECLARE FILTER

Verwendet für

Deklarieren eines 'BLOB'-Filters für die Datenbank

Verfügbar in

DSQL, ESQL

Syntax
DECLARE FILTER filtername
  INPUT_TYPE <sub_type> OUTPUT_TYPE <sub_type>
  ENTRY_POINT 'function_name' MODULE_NAME 'library_name'

<sub_type> ::= number | <mnemonic>

<mnemonic> ::=
    BINARY | TEXT | BLR | ACL | RANGES
  | SUMMARY | FORMAT | TRANSACTION_DESCRIPTION
  | EXTERNAL_FILE_DESCRIPTION | user_defined
Tabelle 53. DECLARE FILTER-Anweisungsparameter
Parameter Beschreibung

filtername

Filtername in der Datenbank. Er kann aus bis zu 31 Zeichen bestehen. Es muss nicht der gleiche Name sein wie der Name, der über ENTRY_POINT aus der Filterbibliothek exportiert wurde.

sub_type

BLOB-Untertyp

number

BLOB-Untertypnummer (muss negativ sein)

mnemonic

'BLOB'-Untertyp mnemonischer Name`

function_name

Der exportierte Name (Einstiegspunkt) der Funktion

library_name

Der Name des Moduls, in dem sich der Filter befindet

user_defined

Benutzerdefinierter mnemonischer Name des 'BLOB'-Untertyps

Die Anweisung DECLARE FILTER stellt der Datenbank einen BLOB-Filter zur Verfügung. Der Name des 'BLOB'-Filters muss unter den Namen der 'BLOB'-Filter eindeutig sein.

Angeben der Untertypen

Die Untertypen können als Untertypnummer oder als Untertyp-Mnemonikname angegeben werden. Benutzerdefinierte Untertypen müssen durch negative Zahlen (von -1 bis -32.768) dargestellt werden. Ein Versuch, mehr als einen 'BLOB'-Filter mit derselben Kombination der Eingabe- und Ausgabetypen zu deklarieren, schlägt mit einem Fehler fehl.

INPUT_TYPE

Klausel, die den BLOB-Subtyp des zu konvertierenden Objekts definiert

OUTPUT_TYPE

-Klausel, die den 'BLOB'-Untertyp des zu erstellenden Objekts definiert.

Mnemonische Namen können für benutzerdefinierte 'BLOB'-Subtypen definiert und manuell in die Systemtabelle 'RDB$TYPES'-Systemtabelle eingefügt werden:

INSERT INTO RDB$TYPES (RDB$FIELD_NAME, RDB$TYPE, RDB$TYPE_NAME)
VALUES ('RDB$FIELD_SUB_TYPE', -33, 'MIDI');

Nachdem die Transaktion festgeschrieben wurde, können die mnemonischen Namen in Deklarationen verwendet werden, wenn Sie neue Filter erstellen.

Der Wert der Spalte RDB$FIELD_NAME muss immer 'RDB$FIELD_SUB_TYPE' sein. Wenn mnemonische Namen in Großbuchstaben definiert wurden, können sie bei der Deklaration eines Filters ohne Beachtung der Groß-/Kleinschreibung und ohne Anführungszeichen verwendet werden, wobei die Regeln für andere Objektnamen beachtet werden.

Warnung

Ab Firebird 3.0 können die Systemtabellen von Benutzern nicht mehr geschrieben werden. Das Einfügen von benutzerdefinierten Typen in RDB$TYPES ist jedoch weiterhin möglich. Firebird 4 führt ein Systemprivileg CREATE_USER_TYPES ein, um benutzerdefinierte Untertypen zu erstellen.

Parameter
ENTRY_POINT

Klausel, die den Namen des Einstiegspunkts (den Namen der importierten Funktion) im Modul definiert.

MODULE_NAME

Die Klausel, die den Namen des Moduls definiert, in dem sich die exportierte Funktion befindet. Standardmäßig müssen sich Module im UDF-Ordner des Stammverzeichnisses auf dem Server befinden. Der Parameter UDFAccess in firebird.conf ermöglicht das Bearbeiten von Zugriffsbeschränkungen auf Filterbibliotheken.

Jeder mit der Datenbank verbundene Benutzer kann einen BLOB-Filter deklarieren.

Wer kann einen 'BLOB'-Filter erstellen?

Die DECLARE FILTER-Anweisung kann ausgeführt werden durch:

Der Benutzer, der die Anweisung DECLARE FILTER ausführt, wird Eigentümer des Filters.

Beispiele für DECLARE FILTER
  1. Erstellen eines 'BLOB'-Filters mit Untertypnummern.

    DECLARE FILTER DESC_FILTER
      INPUT_TYPE 1
      OUTPUT_TYPE -4
      ENTRY_POINT 'desc_filter'
      MODULE_NAME 'FILTERLIB';
  2. Erstellen eines 'BLOB'-Filters unter Verwendung von mnemonischen Subtypnamen.

    DECLARE FILTER FUNNEL
      INPUT_TYPE blr OUTPUT_TYPE text
      ENTRY_POINT 'blr2asc' MODULE_NAME 'myfilterlib';
Siehe auch

DROP FILTER

5.13.2. DROP FILTER

Verwendet für

Entfernen einer 'BLOB'-Filterdeklaration aus der Datenbank

Verfügbar in

DSQL, ESQL

Syntax
DROP FILTER filtername
Tabelle 54. DROP FILTER-Anweisungsparameter
Parameter Beschreibung

filtername

Filtername in der Datenbank

Die DROP FILTER-Anweisung entfernt die Deklaration eines BLOB-Filters aus der Datenbank. Das Entfernen eines 'BLOB'-Filters aus einer Datenbank macht ihn für die Verwendung in dieser Datenbank nicht verfügbar. Die dynamische Bibliothek, in der sich die Konvertierungsfunktion befindet, bleibt intakt und das Entfernen aus einer Datenbank wirkt sich nicht auf andere Datenbanken aus, in denen noch derselbe 'BLOB'-Filter deklariert ist.

Wer kann einen 'BLOB'-Filter fallen lassen?

Die DROP FILTER-Anweisung kann ausgeführt werden durch:

  • Administratoren

  • Der Besitzer des Filters

  • Benutzer mit der Berechtigung DROP ANY FILTER

DROP FILTER-Beispiel
Löschen eines 'BLOB'-Filters.
DROP FILTER DESC_FILTER;
Siehe auch

DECLARE FILTER

5.14. SEQUENCE (GENERATOR)

Eine Sequenz oder ein Generator ist ein Datenbankobjekt, das verwendet wird, um eindeutige Zahlenwerte zum Füllen einer Reihe zu erhalten. “Sequence” ist der SQL-konforme Begriff für dasselbe, was in Firebird traditionell als “Generator” bekannt war. Firebird hat Syntax für beide Begriffe.

Sequenzen (oder Generatoren) werden immer als 64-Bit-Ganzzahlen gespeichert, unabhängig vom SQL-Dialekt der Datenbank.

Wenn ein Client mit Dialekt 1 verbunden ist, sendet der Server Sequenzwerte als 32-Bit-Ganzzahlen an ihn. Die Übergabe eines Sequenzwerts an ein 32-Bit-Feld oder eine 32-Bit-Variable verursacht keine Fehler, solange der aktuelle Wert der Sequenz die Grenzen einer 32-Bit-Zahl nicht überschreitet. Sobald jedoch der Sequenzwert diese Grenze überschreitet, erzeugt eine Datenbank in Dialekt 3 einen Fehler. Eine Datenbank in Dialekt 1 schneidet die Werte ständig ab, was die Einzigartigkeit der Serie beeinträchtigt.

In diesem Abschnitt wird beschrieben, wie Sie Sequenzen erstellen, ändern, einstellen und löschen.

5.14.1. CREATE SEQUENCE

Verwendet für

Erstellen einer neuen SEQUENCE (GENERATOR)

Verfügbar in

DSQL, ESQL

Syntax
CREATE {SEQUENCE | GENERATOR} seq_name
  [START WITH start_value]
  [INCREMENT [BY] increment]
Tabelle 55. CREATE SEQUENCE-Anweisungsparameter
Parameter Beschreibung

seq_name

Name der Sequenz (Generator). Diese kann aus bis zu 31 Zeichen bestehen

start_value

Anfangswert der Sequenz

increment

Erhöhen der Sequenz (bei Verwendung von NEXT VALUE FOR seq_name); kann nicht 0 sein

Die Anweisungen CREATE SEQUENCE und CREATE GENERATOR sind synonym – beide erzeugen eine neue Sequenz. Beide können verwendet werden, aber CREATE SEQUENCE wird empfohlen, da dies die im SQL-Standard definierte Syntax ist.

Wenn eine Sequenz erstellt wird, wird ihr Wert auf den Wert gesetzt, der in der Option START WITH-Klausel angegeben ist. Wenn keine START WITH-Klausel vorhanden ist, wird die Sequenz auf 0 gesetzt.

Mit der optionalen INCREMENT [BY]-Klausel können Sie ein Inkrement für den Ausdruck NEXT VALUE FOR seq_name angeben. Standardmäßig ist das Inkrement 1 (eins). Die Schrittweite kann nicht auf 0 (Null) gesetzt werden. Stattdessen kann die Funktion GEN_ID(seq_name, <step>) aufgerufen werden, um die Serie um eine andere ganze Zahl zu “step”. Das durch INCREMENT [BY] angegebene Inkrement wird nicht für GEN_ID verwendet.

Bug mit START WITH und INCREMENT [BY]

Der SQL-Standard gibt an, dass die START WITH-Klausel den Anfangswert angeben soll, der beim ersten Aufruf von NEXT VALUE FOR seq_name generiert wird, aber Firebird verwendet sie stattdessen, um den aktuellen Wert der Sequenz zu setzen. Als Ergebnis generiert der erste Aufruf von NEXT VALUE FOR seq_name fälschlicherweise den Wert start_value + increment.

Das Erstellen einer Sequenz ohne eine START WITH-Klausel entspricht derzeit der Angabe von START WITH 0, während es START WITH 1 entsprechen sollte.

Dies wird in Firebird 4 behoben, siehe auch CORE-6084

Nicht standardmäßiges Verhalten bei negativen Inkrementen

Der SQL-Standard legt fest, dass Sequenzen mit negativem Inkrement beim Maximalwert der Sequenz (263 - 1) beginnen und herunterzählen sollen. Firebird tut dies nicht und beginnt stattdessen bei 0 + Inkrement.

Dies kann sich in einer zukünftigen Firebird-Version ändern.

Wer kann eine Sequenz erstellen?

Die Anweisung CREATE SEQUENCE (CREATE GENERATOR) kann ausgeführt werden durch:

  • Administratoren

  • Benutzer mit dem Privileg CREATE SEQUENCE (CREATE GENERATOR)

Der Benutzer, der die Anweisung CREATE SEQUENCE (CREATE GENERATOR) ausführt, wird ihr Eigentümer.

Beispiel für CREATE SEQUENCE
  1. Erstellen der Sequenz EMP_NO_GEN mit CREATE SEQUENCE.

    CREATE SEQUENCE EMP_NO_GEN;
  2. Erstellen der Sequenz EMP_NO_GEN mit CREATE GENERATOR.

    CREATE GENERATOR EMP_NO_GEN;
  3. Erstellen der Sequenz EMP_NO_GEN mit einem Anfangswert von 5 und einem Inkrement von 1. Siehe auch Bug mit START WITH und INCREMENT [BY].

    CREATE SEQUENCE EMP_NO_GEN START WITH 5;
  4. Erstellen der Sequenz EMP_NO_GEN mit einem Anfangswert von 1 und einem Inkrement von 10. Siehe auch Bug mit START WITH und INCREMENT [BY].

    CREATE SEQUENCE EMP_NO_GEN INCREMENT BY 10;
  5. Erstellen der Sequenz EMP_NO_GEN mit einem Anfangswert von 5 und einem Inkrement von 10. Siehe auch Bug mit START WITH und INCREMENT [BY].

    CREATE SEQUENCE EMP_NO_GEN START WITH 5 INCREMENT BY 10;

5.14.2. ALTER SEQUENCE

Verwendet für

Den Wert einer Sequenz oder eines Generators auf einen bestimmten Wert setzen

Verfügbar in

DSQL

Syntax
ALTER {SEQUENCE | GENERATOR} seq_name
  [RESTART [WITH newvalue]]
  [INCREMENT [BY] increment]
Tabelle 56. ALTER SEQUENCE-Anweisungsparameter
Parameter Beschreibung

seq_name

Name der Sequenz (Generator)

newvalue

Neuer Sequenz-(Generator-)Wert. Eine 64-Bit-Ganzzahl von -2-63 bis 263-1.

increment

Erhöhen der Sequenz (bei Verwendung von NEXT VALUE FOR seq_name); kann nicht 0 sein

Die ALTER SEQUENCE-Anweisung setzt den aktuellen Wert einer Sequenz oder eines Generators auf den angegebenen Wert und/oder ändert das Inkrement der Sequenz.

Mit der RESTART WITH newvalue-Klausel können Sie den Wert einer Sequenz festlegen. Die RESTART-Klausel (ohne WITH) startet die Sequenz mit dem Anfangswert neu, der mit der START WITH-Klausel konfiguriert wurde, als die Sequenz erstellt wurde.

Bugs mit RESTART

Der Anfangswert (entweder in den Metadaten gespeichert oder in der WITH-Klausel angegeben) wird verwendet, um den aktuellen Wert der Sequenz festzulegen, anstatt den nächsten Wert, der vom SQL-Standard gefordert wird, zu generieren. Siehe Hinweis Bug mit START WITH und INCREMENT [BY] für weitere Informationen.

Außerdem startet RESTART WITH newvalue nicht nur die Sequenz mit dem angegebenen Wert neu, sondern speichert auch newvalue als neuen Anfangswert der Sequenz. Dies bedeutet, dass ein nachfolgender ALTER SEQUENCE RESTART auch newvalue verwendet. Dieses Verhalten entspricht nicht dem im SQL-Standard angegebenen Verhalten.

Dieser Fehler wird in Firebird 4 behoben, siehe auch CORE-6386

Eine falsche Verwendung der ALTER SEQUENCE-Anweisung (Änderung des aktuellen Werts der Sequenz oder des Generators) kann wahrscheinlich die logische Integrität der Daten verletzen.

Mit INCREMENT [BY] können Sie das Sequenzinkrement für den NEXT VALUE FOR-Ausdruck ändern.

Das Ändern des Inkrementwerts wird für alle Abfragen wirksam, die nach dem Festschreiben der Transaktion ausgeführt werden. Prozeduren, die zum ersten Mal nach dem Ändern des Commits aufgerufen werden, verwenden den neuen Wert, wenn sie NEXT VALUE FOR verwenden. Prozeduren, die bereits verwendet (und im Metadaten-Cache zwischengespeichert wurden) verwenden weiterhin das alte Inkrement. Möglicherweise müssen Sie alle Verbindungen zur Datenbank schließen, damit der Metadatencache gelöscht und das neue Inkrement verwendet werden kann. Prozeduren, die NEXT VALUE FOR verwenden, müssen nicht neu kompiliert werden, um das neue Inkrement zu sehen. Prozeduren, die GEN_ID(gen, expression) verwenden, sind nicht betroffen, wenn das Inkrement geändert wird.

Wer kann eine Sequenz ändern?

Die Anweisung ALTER SEQUENCE (ALTER GENERATOR) kann ausgeführt werden durch:

  • Administratoren

  • Der Besitzer der Sequenz

  • Benutzer mit dem Privileg ALTER ANY SEQUENCE (ALTER ANY GENERATOR)

Beispiele für ALTER SEQUENCE
  1. Setzen des Werts der Sequenz EMP_NO_GEN auf 145.

    ALTER SEQUENCE EMP_NO_GEN RESTART WITH 145;
  2. Zurücksetzen des Basiswerts der Sequenz EMP_NO_GEN auf den in den Metadaten gespeicherten Initialwert

    ALTER SEQUENCE EMP_NO_GEN RESTART;
  3. Ändern der Schrittweite der Sequenz EMP_NO_GEN auf 10

    ALTER SEQUENCE EMP_NO_GEN INCREMENT BY 10;

5.14.3. CREATE OR ALTER SEQUENCE

Verwendet für

Erstellen einer neuen oder Ändern einer bestehenden Sequenz

Verfügbar in

DSQL, ESQL

Syntax
CREATE OR ALTER {SEQUENCE | GENERATOR} seq_name
  {RESTART | START WITH start_value}
  [INCREMENT [BY] increment]
Tabelle 57. CREATE OR ALTER SEQUENCE-Anweisungsparameter
Parameter Beschreibung

seq_name

Name der Sequenz (Generator). Diese kann aus bis zu 31 Zeichen bestehen

start_value

Anfangswert der Sequenz

increment

Erhöhen der Sequenz (bei Verwendung von NEXT VALUE FOR seq_name); kann nicht 0 sein

Wenn die Sequenz nicht existiert, wird sie erstellt. Eine bestehende Sequenz wird geändert:

  • Wenn RESTART angegeben ist, wird die Sequenz mit dem in den Metadaten gespeicherten Anfangswert neu gestartet

  • Wenn die START WITH-Klausel angegeben ist, wird start_value als Initialwert in den Metadaten gespeichert und die Sequenz wird neu gestartet

  • Wenn die INCREMENT [BY]-Klausel angegeben ist, wird increment als Inkrement in den Metadaten gespeichert und für nachfolgende Aufrufe von NEXT VALUE FOR verwendet

Beispiel für SEQUENZ ERSTELLEN ODER ÄNDERN
Erstelle eine neue oder modifiziere eine bestehende Sequenz EMP_NO_GEN
CREATE OR ALTER SEQUENCE EMP_NO_GEN
  START WITH 10
  INCREMENT BY 1

5.14.4. DROP SEQUENCE

Verwendet für

Löschen einer Sequenz SEQUENCE (GENERATOR)

Verfügbar in

DSQL, ESQL

Syntax
DROP {SEQUENCE | GENERATOR} seq_name
Tabelle 58. DROP SEQUENCE-Anweisungsparameter
Parameter Beschreibung

seq_name

Name der Sequenz (Generator). Diese kann aus bis zu 31 Zeichen bestehen

Die Anweisungen DROP SEQUENCE und DROP GENERATOR sind äquivalent: beide löschen eine existierende Sequenz (Generator). Beides ist gültig, aber DROP SEQUENCE wird empfohlen, da es im SQL-Standard definiert ist.

Die Anweisungen schlagen fehl, wenn die Sequenz (Generator) Abhängigkeiten hat.

Wer kann eine Sequenz löschen?

Die Anweisung DROP SEQUENCE (DROP GENERATOR) kann ausgeführt werden durch:

  • Administratoren

  • Der Besitzer der Sequenz

  • Benutzer mit dem Privileg DROP ANY SEQUENCE (DROP ANY GENERATOR)

Beispiel für DROP SEQUENCE
Löschen der EMP_NO_GEN-Reihe:
DROP SEQUENCE EMP_NO_GEN;

5.14.5. RECREATE SEQUENCE

Verwendet für

Sequenz erstellen oder neu erstellen (Generator)

Verfügbar in

DSQL, ESQL

Syntax
RECREATE {SEQUENCE | GENERATOR} seq_name
  [START WITH start_value]
  [INCREMENT [BY] increment]
Tabelle 59. RECREATE SEQUENCE-Anweisungsparameter
Parameter Beschreibung

seq_name

Name der Sequenz (Generator). Diese kann aus bis zu 31 Zeichen bestehen

start_value

Anfangswert der Sequenz

increment

Erhöhen der Sequenz (bei Verwendung von NEXT VALUE FOR seq_name); kann nicht 0 sein

Siehe CREATE SEQUENCE für die vollständige Syntax von CREATE SEQUENCE und Beschreibungen zur Definition einer Sequenz und ihrer Optionen.

RECREATE SEQUENCE erstellt oder erstellt eine Sequenz neu. Existiert bereits eine Sequenz mit diesem Namen, versucht die RECREATE SEQUENCE-Anweisung, sie zu löschen und eine neue zu erstellen. Vorhandene Abhängigkeiten verhindern die Ausführung der Anweisung.

Beispiel für RECREATE SEQUENCE
Neuerstellen der Sequenz EMP_NO_GEN
RECREATE SEQUENCE EMP_NO_GEN
  START WITH 10
  INCREMENT BY 2;

5.14.6. SET GENERATOR

Verwendet für

Den Wert einer Sequenz oder eines Generators auf einen bestimmten Wert setzen

Verfügbar in

DSQL, ESQL

Syntax
SET GENERATOR seq_name TO new_val
Tabelle 60. SET GENERATOR-Anweisungsparameter
Parameter Beschreibung

seq_name

Name des Generators (Sequenz)

new_val

Neuer Sequenz-(Generator-)Wert. Eine 64-Bit-Ganzzahl von -2-63 bis 263-1.

Die Anweisung SET GENERATOR setzt den aktuellen Wert einer Sequenz oder eines Generators auf den angegebenen Wert.

Obwohl SET GENERATOR als veraltet gilt, wird es aus Gründen der Abwärtskompatibilität beibehalten. Die Verwendung der standardkonformen ALTER SEQUENCE wird empfohlen.

Wer kann einen SET GENERATOR verwenden?

Die SET GENERATOR-Anweisung kann ausgeführt werden durch:

  • Administratoren

  • Der Besitzer der Sequenz (Generator)

  • Benutzer mit dem Privileg ALTER ANY SEQUENCE (ALTER ANY GENERATOR)

Beispiel für SET GENERATOR
Wert der Sequenz EMP_NO_GEN auf 145 setzen:
SET GENERATOR EMP_NO_GEN TO 145;

Das gleiche kann mit ALTER SEQUENCE gemacht werden:

ALTER SEQUENCE EMP_NO_GEN RESTART WITH 145;

5.15. EXCEPTION

In diesem Abschnitt wird beschrieben, wie Sie benutzerdefinierte Ausnahmen zur Verwendung in Fehlerhandlern in PSQL-Modulen erstellen, ändern und löschen.

5.15.1. CREATE EXCEPTION

Verwendet für

Erstellen einer neuen Ausnahme zur Verwendung in PSQL-Modulen

Verfügbar in

DSQL, ESQL

Syntax
CREATE EXCEPTION exception_name '<message>'

<message> ::= <message-part> [<message-part> ...]

<message-part> ::=
    <text>
  | @<slot>

<slot> ::= one of 1..9
Tabelle 61. CREATE EXCEPTION-Anweisungsparameter
Parameter Beschreibung

exception_name

Ausnahmename. Die maximale Länge beträgt 31 Zeichen

message

Standardfehlermeldung. Die maximale Länge beträgt 1.021 Zeichen

text

Text beliebiger Zeichen

slot

Slotnummer eines Parameters. Die Nummerierung beginnt bei 1. Die maximale Steckplatznummer ist 9.

Die Anweisung CREATE EXCEPTION erzeugt eine neue Ausnahme zur Verwendung in PSQL-Modulen. Existiert eine gleichnamige Ausnahme, schlägt die Anweisung mit einer entsprechenden Fehlermeldung fehl.

Der Ausnahmename ist ein Standardbezeichner. In einer Dialect 3-Datenbank kann es in doppelte Anführungszeichen eingeschlossen werden, um die Groß-/Kleinschreibung zu berücksichtigen und bei Bedarf Zeichen zu verwenden, die in regulären Bezeichnern nicht gültig sind. Weitere Informationen finden Sie unter Bezeichner.

Die Standardnachricht wird im Zeichensatz NONE gespeichert, d.h. in Zeichen eines beliebigen Einzelbyte-Zeichensatzes. Der Text kann im PSQL-Code überschrieben werden, wenn die Ausnahme ausgelöst wird.

Die Fehlermeldung kann “Parameter-Slots” enthalten, die beim Auslösen der Ausnahme gefüllt werden können.

Wenn die message eine Parameter-Slot-Nummer enthält, die größer als 9 ist, werden die zweite und die nachfolgenden Ziffern als Literaltext behandelt. Zum Beispiel wird @10 als Slot 1 interpretiert, gefolgt von einem Literal ‘0’.

Benutzerdefinierte Ausnahmen werden in der Systemtabelle RDB$EXCEPTIONS gespeichert.

Wer kann eine Ausnahme erstellen

Die CREATE EXCEPTION-Anweisung kann ausgeführt werden durch:

Der Benutzer, der die Anweisung CREATE EXCEPTION ausführt, wird Eigentümer der Ausnahme.

CREATE EXCEPTION-Beispiele
Erstellen einer Ausnahme namens E_LARGE_VALUE
CREATE EXCEPTION E_LARGE_VALUE
  'The value is out of range';
Erstellen einer parametrisierten Ausnahme E_INVALID_VALUE
CREATE EXCEPTION E_INVALID_VALUE
  'Invalid value @1 for field @2';
Tips

Die Gruppierung von CREATE EXCEPTION-Anweisungen in Systemaktualisierungsskripten vereinfacht die Arbeit mit ihnen und deren Dokumentation. Ein System von Präfixen zur Benennung und Kategorisierung von Ausnahmegruppen wird empfohlen.

5.15.2. ALTER EXCEPTION

Verwendet für

Ändern der von einer benutzerdefinierten Ausnahme zurückgegebenen Nachricht

Verfügbar in

DSQL, ESQL

Syntax
ALTER EXCEPTION exception_name '<message>'

!! Vgl. auch CREATE EXCEPTION für weitere Regeln !!

Die Anweisung ALTER EXCEPTION kann jederzeit verwendet werden, um den Standardtext der Nachricht zu ändern.

Wer kann eine Ausnahme ändern?

Die Anweisung ALTER EXCEPTION kann ausgeführt werden durch:

  • Administratoren

  • Der Inhaber der Ausnahme

  • Benutzer mit der Berechtigung ALTER ANY EXCEPTION

ALTER EXCEPTION-Beispiele
Ändern der Standardnachricht für die Ausnahme E_LARGE_VALUE
ALTER EXCEPTION E_LARGE_VALUE
  'The value exceeds the prescribed limit of 32,765 bytes';

5.15.3. CREATE OR ALTER EXCEPTION

Verwendet für

Ändern der von einer benutzerdefinierten Ausnahme zurückgegebenen Nachricht, falls die Ausnahme vorhanden ist; andernfalls eine neue Ausnahme erstellen

Verfügbar in

DSQL

Syntax
CREATE OR ALTER EXCEPTION exception_name '<message>'

!! Vgl. Syntax von CREATE EXCEPTION für weitere Regeln !!

Die Anweisung CREATE OR ALTER EXCEPTION wird verwendet, um die angegebene Ausnahme zu erstellen, falls sie nicht existiert, oder um den Text der von ihr zurückgegebenen Fehlermeldung zu ändern, wenn sie bereits existiert. Wenn eine bestehende Ausnahme durch diese Anweisung geändert wird, bleiben alle bestehenden Abhängigkeiten erhalten.

CREATE OR ALTER EXCEPTION-Beispiel
Ändern der Nachricht für die Ausnahme E_LARGE_VALUE
CREATE OR ALTER EXCEPTION E_LARGE_VALUE
  'The value is higher than the permitted range 0 to 32,765';

5.15.4. DROP EXCEPTION

Verwendet für

Löschen einer benutzerdefinierten Ausnahme

Verfügbar in

DSQL, ESQL

Syntax
DROP EXCEPTION exception_name
Tabelle 62. DROP EXCEPTION-Anweisungsparameter
Parameter Beschreibung

exception_name

Exception name

Die Anweisung DROP EXCEPTION dient zum Löschen einer Ausnahme. Alle Abhängigkeiten von der Ausnahme führen dazu, dass die Anweisung fehlschlägt und die Ausnahme nicht gelöscht wird.

Wer kann eine Ausnahme fallen lassen?

Die DROP EXCEPTION-Anweisung kann ausgeführt werden durch:

  • Administratoren

  • Der Inhaber der Ausnahme

  • Benutzer mit dem Privileg DROP ANY EXCEPTION

DROP EXCEPTION-Beispiele
Ausnahme E_LARGE_VALUE wird gelöscht
DROP EXCEPTION E_LARGE_VALUE;

5.15.5. RECREATE EXCEPTION

Verwendet für

Erstellen einer neuen benutzerdefinierten Ausnahme oder Neuerstellen einer vorhandenen Ausnahme

Verfügbar in

DSQL

Syntax
RECREATE EXCEPTION exception_name '<message>'

!! Vgl. Syntax CREATE EXCEPTION für weitere Regeln !!

Die Anweisung RECREATE EXCEPTION erzeugt eine neue Ausnahme zur Verwendung in PSQL-Modulen. Wenn bereits eine Ausnahme mit demselben Namen existiert, versucht die Anweisung RECREATE EXCEPTION, sie zu löschen und eine neue zu erstellen. Wenn Abhängigkeiten von der bestehenden Ausnahme bestehen, schlägt der Löschversuch fehl und RECREATE EXCEPTION wird nicht ausgeführt.

RECREATE EXCEPTION-Beispiel
Ausnahme E_LARGE_VALUE neu erstellen
RECREATE EXCEPTION E_LARGE_VALUE
  'The value exceeds its limit';

5.16. COLLATION

In SQL sind Textzeichenfolgen sortierbare Objekte. Das bedeutet, dass sie Ordnungsregeln wie die alphabetische Reihenfolge befolgen. Vergleichsoperationen können auf solche Textzeichenfolgen angewendet werden (z. B. “kleiner als” oder “größer als”), bei denen der Vergleich eine bestimmte Sortierreihenfolge oder Kollatierung anwenden muss. Der Ausdruck “'a' < 'b'” bedeutet beispielsweise, dass ‘'a'’ in der Kollatierung vor ‘'b'’ steht. Der Ausdruck “'c' > 'b'” bedeutet, dass ‘'c'’ in der Kollation auf ‘'b'’ folgt. Textstrings mit mehr als einem Zeichen werden durch sequentielle Zeichenvergleiche sortiert: Zuerst werden die ersten Zeichen der beiden Strings verglichen, dann die zweiten Zeichen usw., bis ein Unterschied zwischen den beiden Strings gefunden wird. Dieser Unterschied definiert die Sortierreihenfolge.

Eine COLLATION ist das Schemaobjekt, das eine Kollatierung (oder Sortierreihenfolge) definiert.

5.16.1. CREATE COLLATION

Verwendet für

Erstellen einer neuen Kollatierung für einen unterstützten Zeichensatz, der für die Datenbank verfügbar ist

Verfügbar in

DSQL

Syntax
CREATE COLLATION collname
    FOR charset
    [FROM {basecoll | EXTERNAL ('extname')}]
    [NO PAD | PAD SPACE]
    [CASE [IN]SENSITIVE]
    [ACCENT [IN]SENSITIVE]
    ['<specific-attributes>']

<specific-attributes> ::= <attribute> [; <attribute> ...]

<attribute> ::= attrname=attrvalue
Tabelle 63. CREATE COLLATION-Anweisungsparameter
Parameter Beschreibung

collname

Der für die neue Sortierung zu verwendende Name. Die maximale Länge beträgt 31 Zeichen

charset

Ein in der Datenbank vorhandener Zeichensatz

basecoll

Eine Kollation, die bereits in der Datenbank vorhanden ist

extname

Der in der Datei .conf verwendete Kollatierungsname

Die CREATE COLLATION-Anweisung “erzeugt” nichts, ihr Zweck besteht darin, einer Datenbank eine Kollation bekannt zu machen. Die Kollatierung muss bereits auf dem System vorhanden sein, normalerweise in einer Bibliotheksdatei, und muss ordnungsgemäß in einer .conf-Datei im intl-Unterverzeichnis der Firebird-Installation registriert sein.

Die Kollation kann alternativ auf einer basieren, die bereits in der Datenbank vorhanden ist.

Wie die Engine die Kollation erkennt

Die optionale FROM'-Klausel gibt die Basiskollation an, die verwendet wird, um eine neue Kollation abzuleiten. Diese Kollation muss bereits in der Datenbank vorhanden sein. Wenn das Schlüsselwort `EXTERNAL angegeben wird, scannt Firebird die .conf-Dateien in $fbroot/intl/, wobei extname genau mit dem Namen in der Konfigurationsdatei übereinstimmen muss (Groß-/Kleinschreibung beachten).

Wenn keine FROM-Klausel vorhanden ist, durchsucht Firebird die .conf-Datei(en) im intl-Unterverzeichnis nach einer Kollatierung mit dem in CREATE COLLATION angegebenen Kollatierungsnamen. Mit anderen Worten, das Weglassen der FROM basecoll-Klausel entspricht der Angabe von FROM EXTERNAL ('collname').

Beim — in einfachen Anführungszeichen angegebenen — extname muss die Groß-/Kleinschreibung beachtet werden und genau mit dem Kollatierungsnamen in der Datei .conf übereinstimmen. Bei den Parametern collname, charset und basecoll wird die Groß-/Kleinschreibung nicht beachtet, es sei denn, sie stehen in doppelten Anführungszeichen.

Beim Erstellen einer Sortierung können Sie angeben, ob nachgestellte Leerzeichen in den Vergleich einbezogen werden. Bei Angabe der NO PAD-Klausel werden abschließende Leerzeichen beim Vergleich berücksichtigt. Wenn die PAD SPACE-Klausel angegeben ist, werden nachgestellte Leerzeichen beim Vergleich ignoriert.

Mit der optionalen CASE-Klausel können Sie angeben, ob beim Vergleich die Groß-/Kleinschreibung beachtet wird oder nicht.

Mit der optionalen ACCENT-Klausel können Sie angeben, ob der Vergleich akzentsensitiv oder akzentunabhängig ist (zB wenn ‘'e'’ und ‘'é'’ als gleich betrachtet werden oder ungleich).

Spezifische Attribute

Die CREATE COLLATION-Anweisung kann auch spezielle Attribute enthalten, um die Kollatierung zu konfigurieren. Die verfügbaren spezifischen Attribute sind in der folgenden Tabelle aufgeführt. Nicht alle spezifischen Attribute gelten für jede Sortierung. Wenn das Attribut nicht auf die Sortierung anwendbar ist, aber beim Erstellen angegeben wird, wird kein Fehler verursacht.

Bei spezifische Attributnamen muss die Groß-/Kleinschreibung beachtet werden.

In der Tabelle gibt “1 bpc” an, dass ein Attribut für Kollationen von Zeichensätzen mit 1 Byte pro Zeichen gültig ist (sogenannte schmale Zeichensätze), und “UNI” für “Unicode Collations”.

Tabelle 64. Spezifische Sortierattribute
Atrribute Werte Gültig für Hinweis

DISABLE-COMPRESSIONS

0, 1

1 bpc

Deaktiviert Kompressionen (auch bekannt als Kontraktionen). Kompressionen bewirken, dass bestimmte Zeichenfolgen als atomare Einheiten sortiert werden, z.B. Spanisch c+h als einzelnes Zeichen ch

DISABLE-EXPANSIONS

0, 1

1 bpc

Deaktiviert Erweiterungen. Erweiterungen bewirken, dass bestimmte Zeichen (z.B. Ligaturen oder umlaute Vokale) als Zeichenfolgen behandelt und entsprechend sortiert werden

ICU-VERSION

Standard oder M.m

UNI

Gibt die zu verwendende ICU-Bibliotheksversion an. Gültige Werte sind diejenigen, die im entsprechenden <intl_module>-Element in intl/fbintl.conf definiert sind. Format: entweder das Stringliteral “default” oder eine Major+Minor Versionsnummer wie “3.0” (beide ohne Anführungszeichen).

LOCALE

xx_YY

UNI

Gibt das Kollatierungsgebietsschema an. Erfordert eine vollständige Version der ICU-Bibliotheken. Format: ein Gebietsschema-String wie “du_NL” (ohne Anführungszeichen)

MULTI-LEVEL

0, 1

1 bpc

Verwendet mehr als eine Sortierebene

NUMERIC-SORT

0, 1

UNI

Behandelt zusammenhängende Gruppen von Dezimalziffern in der Zeichenfolge als atomare Einheiten und sortiert sie numerisch. (Dies wird auch als natürliche Sortierung bezeichnet)

SPECIALS-FIRST

0, 1

1 bpc

Ordnet Sonderzeichen (Leerzeichen, Symbole usw.) vor alphanumerischen Zeichen an

Wenn Sie Ihrer Datenbank einen neuen Zeichensatz mit seiner Standardsortierung hinzufügen möchten, deklarieren Sie die gespeicherte Prozedur sp_register_character_set(name, max_bytes_per_character), die sich in misc/intl.sql im Firebird-Installationsverzeichnis befindet, und führen Sie sie aus.

Damit dies funktioniert, muss der Zeichensatz auf dem System vorhanden und in einer .conf-Datei im intl-Unterverzeichnis registriert sein.

Wer kann eine Kollation erstellen

Die CREATE COLLATION-Anweisung kann ausgeführt werden durch:

Der Benutzer, der die CREATE COLLATION-Anweisung ausführt, wird Eigentümer der Kollation.

Beispiele zur Nutzung von CREATE COLLATION
  1. Erstellen einer Kollatierung mit dem Namen aus der Datei fbintl.conf (Groß-/Kleinschreibung beachten)

    CREATE COLLATION ISO8859_1_UNICODE FOR ISO8859_1;
  2. Erstellen einer Kollatierung unter Verwendung eines speziellen (benutzerdefinierten) Namens (der “externe” Name muss vollständig mit dem Namen in der Datei fbintl.conf übereinstimmen)

    CREATE COLLATION LAT_UNI
      FOR ISO8859_1
      FROM EXTERNAL ('ISO8859_1_UNICODE');
  3. Erstellen einer Sortierung ohne Beachtung der Groß-/Kleinschreibung basierend auf einer bereits in der Datenbank vorhandenen.

    CREATE COLLATION ES_ES_NOPAD_CI
      FOR ISO8859_1
      FROM ES_ES
      NO PAD
      CASE INSENSITIVE;
  4. Erstellen einer Sortierung ohne Beachtung der Groß-/Kleinschreibung basierend auf einer bereits in der Datenbank vorhandenen Sortierung mit bestimmten Attributen

    CREATE COLLATION ES_ES_CI_COMPR
      FOR ISO8859_1
      FROM ES_ES
      CASE INSENSITIVE
      'DISABLE-COMPRESSIONS=0';
  5. Erstellen einer Sortierung ohne Beachtung der Groß-/Kleinschreibung nach dem Wert von Zahlen (die sogenannte natürliche Sortierung)

    CREATE COLLATION nums_coll FOR UTF8
      FROM UNICODE
      CASE INSENSITIVE 'NUMERIC-SORT=1';
    
    CREATE DOMAIN dm_nums AS varchar(20)
      CHARACTER SET UTF8 COLLATE nums_coll; -- Original-(Hersteller-)Nummern
    
    CREATE TABLE wares(id int primary key, articul dm_nums ...);
Siehe auch

DROP COLLATION

5.16.2. DROP COLLATION

Verwendet für

Eine Kollation aus der Datenbank entfernen

Verfügbar in

DSQL

Syntax
DROP COLLATION collname
Tabelle 65. DROP COLLATION-Anweisungsparameter
Parameter Beschreibung

collname

Der Name der Kollation

Die Anweisung DROP COLLATION entfernt die angegebene Kollatierung aus der Datenbank, falls vorhanden. Wenn die angegebene Sortierung nicht vorhanden ist, wird ein Fehler ausgegeben.

Wenn Sie einen ganzen Zeichensatz mit all seinen Kollatierungen aus der Datenbank entfernen möchten, deklarieren Sie die gespeicherte Prozedur sp_unregister_character_set(name) aus dem misc/intl.sql-Unterverzeichnis der Firebird-Installation und führen Sie sie aus.

Wer kann eine Kollation abgeben

Die Drop COLLATION-Anweisung kann ausgeführt werden durch:

  • Administratoren

  • Der Besitzer der Kollation

  • Benutzer mit dem Privileg DROP ANY COLLATION

Beispiele für DROP COLLATION
Löschen der Kollatierung ES_ES_NOPAD_CI.
DROP COLLATION ES_ES_NOPAD_CI;
Siehe auch

CREATE COLLATION

5.17. CHARACTER SET

5.17.1. ALTER CHARACTER SET

Verwendet für

Festlegen der Standardsortierung für einen Zeichensatz

Verfügbar in

DSQL

Syntax
ALTER CHARACTER SET charset
  SET DEFAULT COLLATION collation
Tabelle 66. ALTER CHARACTER SET-Anweisungsparameter
Parameter Beschreibung

charset

Zeichensatzkennung

collation

Der Name der Kollation

Die Anweisung ALTER CHARACTER SET ändert die Standardsortierung für den angegebenen Zeichensatz. Sie wirkt sich auf die zukünftige Verwendung des Zeichensatzes aus, außer in Fällen, in denen die COLLATE-Klausel explizit überschrieben wird. In diesem Fall bleibt die Kollatierungsreihenfolge bestehender Domänen, Spalten und PSQL-Variablen nach der Änderung der Standardkollation des zugrunde liegenden Zeichensatzes erhalten.

Wenn Sie die Standardsortierung für den Datenbankzeichensatz ändern (der beim Erstellen der Datenbank definierte), wird die Standardsortierung für die Datenbank geändert.

Wenn Sie die Standardsortierung für den während der Verbindung angegebenen Zeichensatz ändern, werden Zeichenfolgenkonstanten gemäß dem neuen Sortierungswert interpretiert, außer in den Fällen, in denen der Zeichensatz und/oder die Sortierung überschrieben wurden.

Wer kann einen Zeichensatz ändern?

Die Anweisung ALTER CHARACTER SET kann ausgeführt werden durch:

ALTER CHARACTER SET-Beispiel
Festlegen der Standardsortierung UNICODE_CI_AI für die UTF8-Codierung
ALTER CHARACTER SET UTF8
  SET DEFAULT COLLATION UNICODE_CI_AI;

5.18. COMMENT

Datenbankobjekte und eine Datenbank selbst können mit Kommentaren versehen werden. Es ist ein bequemer Mechanismus, um die Entwicklung und Wartung einer Datenbank zu dokumentieren. Kommentare, die mit COMMENT ON erstellt wurden, überleben eine gbak-Sicherung und -Wiederherstellung.

5.18.1. COMMENT ON

Verwendet für

Metadaten dokumentieren

Verfügbar in

DSQL

Syntax
COMMENT ON <object> IS {'sometext' | NULL}

<object> ::=
    {DATABASE | SCHEMA}
  | <basic-type> objectname
  | COLUMN relationname.fieldname
  | [{PROCEDURE | FUNCTION}] PARAMETER
      [packagename.]routinename.paramname
  | {PROCEDURE | [EXTERNAL] FUNCTION}
      [package_name.]routinename

<basic-type> ::=
    CHARACTER SET | COLLATION | DOMAIN
  | EXCEPTION     | FILTER    | GENERATOR
  | INDEX         | PACKAGE   | ROLE
  | SEQUENCE      | TABLE     | TRIGGER
  | USER          | VIEW
Tabelle 67. COMMENT ON-Anweisungsparameter
Parameter Beschreibung

sometext

Kommentartext

basic-type

Metadaten-Objekttyp

objectname

Name des Metadatenobjekts

relationname

Name der Tabelle oder Ansicht

fieldname

Name der Spalte

package_name

Name des Pakets

routinename

Name der gespeicherten Prozedur oder Funktion

paramname

Name einer gespeicherten Prozedur oder eines Funktionsparameters

Die Anweisung COMMENT ON fügt Kommentare für Datenbankobjekte (Metadaten) hinzu. Kommentare werden in der Spalte RDB$DESCRIPTION der entsprechenden Systemtabellen gespeichert. Clientanwendungen können Kommentare aus diesen Feldern anzeigen.

  1. Wenn Sie einen leeren Kommentar hinzufügen (“''”), wird dieser als NULL in der Datenbank gespeichert.

  2. Die COMMENT ON USER-Anweisung erstellt nur Kommentare zu Benutzern, die vom Standardbenutzermanager verwaltet werden (das erste Plugin, das in der Konfigurationsoption UserManager aufgeführt ist). Siehe auch CORE-6479.

  3. Kommentare zu Benutzern werden in der Sicherheitsdatenbank gespeichert.

  4. SCHEMA ist derzeit ein Synonym für DATABASE; dies kann sich in einer zukünftigen Version ändern, daher empfehlen wir immer DATABASE zu verwenden

Kommentare zu Benutzern sind für diesen Benutzer über die virtuelle Tabelle SEC$USERS sichtbar.

Wer kann einen Kommentar hinzufügen

Die COMMENT ON-Anweisung kann ausgeführt werden durch:

  • Administratoren

  • Der Besitzer des Objekts, das kommentiert wird

  • Benutzer mit der Berechtigung ALTER ANY object_type, wobei object_type der Typ des kommentierten Objekts ist (z. B. PROCEDURE)

Beispiele mit COMMENT ON
  1. Kommentar zur aktuellen Datenbank hinzufügen

    COMMENT ON DATABASE IS 'It is a test (''my.fdb'') database';
  2. Kommentar für die Tabelle METALS hinzufügen

    COMMENT ON TABLE METALS IS 'Metal directory';
  3. Hinzufügen eines Kommentars für das Feld ISALLOY in der Tabelle METALS

    COMMENT ON COLUMN METALS.ISALLOY IS '0 = fine metal, 1 = alloy';
  4. Kommentar zu einem Parameter hinzufügen

    COMMENT ON PARAMETER ADD_EMP_PROJ.EMP_NO IS 'Employee ID';
  5. Hinzufügen eines Kommentars für ein Paket, seine Prozeduren und Funktionen sowie deren Parameter

    COMMENT ON PACKAGE APP_VAR IS 'Application Variables';
    
    COMMENT ON FUNCTION APP_VAR.GET_DATEBEGIN
    IS 'Returns the start date of the period';
    
    COMMENT ON PROCEDURE APP_VAR.SET_DATERANGE
    IS 'Set date range';
    
    COMMENT ON
    PROCEDURE PARAMETER APP_VAR.SET_DATERANGE.ADATEBEGIN
    IS 'Start Date';

6. Data Manipulation-Statements (DML)

DML – Data Manipulation Language – ist die Teilmenge von SQL, die von Anwendungen und prozeduralen Modulen verwendet wird, um Daten zu extrahieren und zu ändern. Die Extraktion zum Lesen von Daten, sowohl roh als auch manipuliert, wird mit der SELECT-Anweisung erreicht. INSERT dient zum Hinzufügen neuer Daten und DELETE zum Löschen nicht mehr benötigter Daten. UPDATE, MERGE und UPDATE OR INSERT ändern alle Daten auf verschiedene Weise.

6.1. SELECT

Verwendet für

Abfrage von Daten

Verfügbar in

DSQL, ESQL, PSQL

Global syntax
[WITH [RECURSIVE] <cte> [, <cte> ...]]
SELECT
  [FIRST m] [SKIP n]
  [{DISTINCT | ALL}] <columns>
FROM
  <source> [[AS] alias]
  [<joins>]
[WHERE <condition>]
[GROUP BY <grouping-list>
[HAVING <aggregate-condition>]]
[PLAN <plan-expr>]
[UNION [{DISTINCT | ALL}] <other-select>]
[ORDER BY <ordering-list>]
[{ ROWS <m> [TO <n>]
 | [OFFSET n {ROW | ROWS}]
   [FETCH {FIRST | NEXT} [m] {ROW | ROWS} ONLY]
}]
[FOR UPDATE [OF <columns>]]
[WITH LOCK]
[INTO <variables>]

<variables> ::= [:]varname [, [:]varname ...]

Die SELECT-Anweisung ruft Daten aus der Datenbank ab und übergibt sie an die Anwendung oder die einschließende SQL-Anweisung. Daten werden in null oder mehr rows zurückgegeben, die jeweils eine oder mehrere columns oder fields enthalten. Die Summe der zurückgegebenen Zeilen ist der result set der Anweisung.

Die einzigen obligatorischen Teile der SELECT-Anweisung sind:

  • Das Schlüsselwort SELECT, gefolgt von einer Spaltenliste. Dieser Teil gibt an, was Sie abrufen möchten.

  • Das Schlüsselwort FROM, gefolgt von einem auswählbaren Objekt. Dies teilt der Engine mit, wo Sie sie von beziehen möchten.

In seiner einfachsten Form ruft SELECT eine Reihe von Spalten aus einer einzelnen Tabelle oder Ansicht ab, wie folgt:

select id, name, address
  from contacts

Oder, um alle Spalten abzurufen:

select * from sales

In der Praxis wird eine SELECT-Anweisung normalerweise mit einer WHERE-Klausel ausgeführt, die die zurückgegebenen Zeilen begrenzt. Die Ergebnismenge kann durch eine ORDER BY-Klausel sortiert werden, und FIRST …​ SKIP, OFFSET …​ FETCH oder ROWS können die Anzahl der zurückgegebenen Zeilen weiter begrenzen und können - zum Beispiel - sein für die Paginierung verwendet.

Die Spaltenliste kann statt nur Spaltennamen alle Arten von Ausdrücken enthalten, und die Quelle muss keine Tabelle oder Sicht sein: Sie kann auch eine abgeleitete Tabelle, ein allgemeiner Tabellenausdruck (CTE) oder eine auswählbare gespeicherte Prozedur (SP) sein. Mehrere Quellen können in einem JOIN kombiniert werden und mehrere Ergebnismengen können in einer UNION kombiniert werden.

Die folgenden Abschnitte behandeln die verfügbaren SELECT-Unterklauseln und ihre Verwendung im Detail.

6.1.1. FIRST, SKIP

Verwendet für

Abrufen eines Zeilenabschnitts aus einer geordneten Menge

Verfügbar in

DSQL, PSQL

Syntax
SELECT
  [FIRST <m>] [SKIP <n>]
  FROM ...
  ...

<m>, <n>  ::=
    <integer-literal>
  | <query-parameter>
  | (<integer-expression>)
Tabelle 68. Argumente für die Klauseln FIRST und SKIP
Argument Beschreibung

integer-literal

Ganzzahlliteral

query-parameter

Platzhalter für Abfrageparameter. ? in DSQL und :paramname in PSQL

integer-expression

Ausdruck, der einen ganzzahligen Wert zurückgibt

FIRST und SKIP sind Nicht-Standard-Syntax

FIRST und SKIP sind Firebird-spezifische Klauseln. Verwenden Sie nach Möglichkeit die SQL-Standardsyntax OFFSET, FETCH.

FIRST begrenzt die Ausgabe einer Abfrage auf die ersten m Zeilen. SKIP unterdrückt die angegebenen n Zeilen, bevor die Ausgabe gestartet wird.

FIRST und SKIP sind beide optional. Bei gemeinsamer Verwendung wie in “FIRST m SKIP n” werden die n obersten Zeilen der Ausgabemenge verworfen und die ersten m Zeilen der restlichen Menge zurückgegeben.

Eigenschaften von FIRST und SKIP
  • Jedes Argument für FIRST und SKIP, das kein Integer-Literal oder ein SQL-Parameter ist, muss in Klammern eingeschlossen werden. Dies impliziert, dass ein Unterabfrageausdruck in zwei Klammerpaare eingeschlossen werden muss.

  • SKIP 0 ist erlaubt, aber völlig sinnlos.

  • FIRST 0 ist ebenfalls erlaubt und gibt eine leere Menge zurück.

  • Negative SKIP und/oder FIRST Werte führen zu einem Fehler.

  • Wenn ein SKIP nach dem Ende des Datensatzes landet, wird ein leerer Satz zurückgegeben.

  • Wenn die Anzahl der Zeilen im Datensatz (oder der Rest nach einem SKIP) kleiner ist als der Wert des m-Arguments, das für FIRST bereitgestellt wurde, wird diese kleinere Anzahl von Zeilen zurückgegeben. Dies sind gültige Ergebnisse, keine Fehlerbedingungen.

Beispiele für FIRST/SKIP
  1. Die folgende Abfrage gibt die ersten 10 Namen aus der Tabelle "People" zurück:

    select first 10 id, name from People
      order by name asc
  2. Die folgende Abfrage gibt alles zurück, aber die ersten 10 Namen:

    select skip 10 id, name from People
      order by name asc
  3. Und dieser gibt die letzten 10 Zeilen zurück. Beachten Sie die doppelten Klammern:

    select skip ((select count(*) - 10 from People))
      id, name from People
      order by name asc
  4. Diese Abfrage gibt die Zeilen 81 bis 100 der People-Tabelle zurück:

    select first 20 skip 80 id, name from People
      order by name asc
Siehe auch

OFFSET, FETCH, ROWS

6.1.2. Die SELECT-Spaltenliste

Die Spaltenliste enthält einen oder mehrere durch Kommas getrennte Wertausdrücke. Jeder Ausdruck stellt einen Wert für eine Ausgabespalte bereit. Alternativ kann * (“Hole Sternchen” oder “Hole alle”) verwendet werden, um für alle Spalten in einer Relation (d.h. einer Tabelle, View oder auswählbaren Stored Procedure) zu stehen. .Syntax

SELECT
  [...]
  [{DISTINCT | ALL}] <output-column> [, <output-column> ...]
  [...]
  FROM ...

<output-column> ::=
  { [<qualifier>.]*
  | <value-expression> [COLLATE collation] [[AS] alias] }

<value-expression> ::=
  { [<qualifier>.]table-column
  | [<qualifier>.]view-column
  | [<qualifier>.]selectable-SP-outparm
  | <literal>
  | <context-variable>
  | <function-call>
  | <single-value-subselect>
  | <CASE-construct>
  | any other expression returning a single
    value of a Firebird data type or NULL }

<qualifier> ::= a relation name or alias
Tabelle 69. Argumente für die SELECT-Spaltenliste
Argument Beschreibung

qualifier

Name der Relation (View, Stored Procedure, abgeleitete Tabelle); oder ein Alias dafür

collation

Nur für zeichenartige Spalten: ein vorhandener und für den Zeichensatz der Daten gültiger Kollatierungsname

alias

Spalten- oder Feldalias

table-column

Name einer Tabellenspalte

view-column

Name einer Ansichtsspalte

selectable-SP-outparm

Deklarierter Name eines Ausgabeparameters einer auswählbaren gespeicherten Prozedur

constant

Eine Konstante

context-variable

Kontextvariable

function-call

Skalar-, Aggregat- oder Fensterfunktionsausdruck

single-value-subselect

Eine Unterabfrage, die einen Skalarwert zurückgibt (Singleton)

CASE-construct

CASE-Konstrukt, das Bedingungen für einen Rückgabewert setzt

other-single-value-expr

Jeder andere Ausdruck, der einen einzelnen Wert eines Firebird-Datentyps zurückgibt; oder NULL

Es ist immer gültig, einen Spaltennamen (oder “*”) mit dem Namen oder Alias der Tabelle, Ansicht oder auswählbaren SP, zu der er gehört, zu qualifizieren, gefolgt von einem Punkt (‘.’). Beispiel: Beziehungsname.Spaltenname, Beziehungsname.*, Alias.Spaltenname, Alias.*. Qualifizierend ist erforderlich, wenn der Spaltenname in mehr als einer Relation vorkommt, die an einem Join teilnimmt. Das Qualifizieren von “*” ist immer obligatorisch, wenn es nicht das einzige Element in der Spaltenliste ist.

Aliase verbergen den ursprünglichen Beziehungsnamen: Sobald eine Tabelle, Ansicht oder Prozedur mit einem Alias versehen wurde, kann nur der Alias als Qualifizierer während der gesamten Abfrage verwendet werden. Der Beziehungsname selbst wird nicht mehr verfügbar.

Der Spaltenliste kann optional eines der Schlüsselwörter DISTINCT oder ALL vorangestellt werden:

  • DISTINCT filtert alle doppelten Zeilen heraus. Das heißt, wenn zwei oder mehr Zeilen in jeder entsprechenden Spalte die gleichen Werte haben, wird nur eine davon in die Ergebnismenge aufgenommen

  • ALL ist die Vorgabe: es gibt alle Zeilen zurück, einschließlich der Duplikate. ALL wird selten verwendet; es wird zur Einhaltung des SQL-Standards unterstützt.

Eine COLLATE-Klausel ändert das Aussehen der Spalte als solche nicht. Wenn die angegebene Sortierung jedoch die Groß-/Kleinschreibung oder die Akzentempfindlichkeit der Spalte ändert, kann dies Folgendes beeinflussen:

  • Die Reihenfolge, wenn auch eine ORDER BY-Klausel vorhanden ist und diese Spalte betrifft

  • Gruppierung, wenn die Spalte Teil einer GROUP BY-Klausel ist

  • Die abgerufenen Zeilen (und damit die Gesamtzahl der Zeilen in der Ergebnismenge), wenn DISTINCT verwendet wird

Beispiele für SELECT-Abfragen mit verschiedenen Arten von Spaltenlisten

Ein einfaches SELECT, das nur Spaltennamen verwendet:

select cust_id, cust_name, phone
  from customers
  where city = 'London'

Eine Abfrage mit einem Verkettungsausdruck und einem Funktionsaufruf in der Spaltenliste:

select 'Mr./Mrs. ' || lastname, street, zip, upper(city)
  from contacts
  where date_last_purchase(id) = current_date

Eine Abfrage mit zwei Unterauswahlen:

select p.fullname,
  (select name from classes c where c.id = p.class) as class,
  (select name from mentors m where m.id = p.mentor) as mentor
from pupils p

Die folgende Abfrage bewirkt dasselbe wie die vorherige, indem Joins anstelle von Subselects verwendet werden:

select p.fullname,
  c.name as class,
  m.name as mentor
  join classes c on c.id = p.class
from pupils p
  join mentors m on m.id = p.mentor

Diese Abfrage verwendet ein CASE-Konstrukt, um den richtigen Titel zu ermitteln, z.B. beim Senden von E-Mails an eine Person:

select case upper(sex)
    when 'F' then 'Mrs.'
    when 'M' then 'Mr.'
    else ''
  end as title,
  lastname,
  address
from employees

Abfrage über eine Fensterfunktion. Sortiert Mitarbeiter nach Gehalt.

SELECT
  id,
  salary,
  name ,
  DENSE_RANK() OVER (ORDER BY salary) AS EMP_RANK
FROM employees
ORDER BY salary;

Abfrage einer auswählbaren gespeicherten Prozedur:

select * from interesting_transactions(2010, 3, 'S')
  order by amount

Auswählen aus Spalten einer abgeleiteten Tabelle. Eine abgeleitete Tabelle ist eine SELECT-Anweisung in Klammern, deren Ergebnismenge in einer einschließenden Abfrage verwendet wird, als wäre es eine reguläre Tabelle oder Ansicht. Die abgeleitete Tabelle ist hier fett gedruckt:

select fieldcount,
  count(relation) as num_tables
from (select r.rdb$relation_name as relation,
        count(*) as fieldcount
      from rdb$relations r
        join rdb$relation_fields rf
          on rf.rdb$relation_name = r.rdb$relation_name
      group by relation)
group by fieldcount

Abfrage der Uhrzeit über eine Kontextvariable (CURRENT_TIME):

select current_time from rdb$database

Für diejenigen, die mit RDB$DATABASE nicht vertraut sind: Dies ist eine Systemtabelle, die in allen Firebird-Datenbanken vorhanden ist und garantiert genau eine Zeile enthält. Obwohl es nicht für diesen Zweck erstellt wurde, ist es unter Firebird-Programmierern zur Standardpraxis geworden, aus dieser Tabelle auszuwählen, wenn Sie “from Nothing” auswählen möchten, dh wenn Sie Daten benötigen, die nicht an eine Tabelle oder Ansicht gebunden sind, kann aber allein aus den Ausdrücken in den Ausgabespalten abgeleitet werden. Ein anderes Beispiel ist:

select power(12, 2) as twelve_squared, power(12, 3) as twelve_cubed
  from rdb$database

Schließlich ein Beispiel, in dem Sie einige aussagekräftige Informationen aus RDB$DATABASE selbst auswählen:

select rdb$character_set_name from rdb$database

Wie Sie vielleicht erraten haben, erhalten Sie dadurch den Standardzeichensatz der Datenbank.

6.1.3. Die FROM-Klausel

Die FROM-Klausel gibt die Quelle(n) an, aus der die Daten abgerufen werden sollen. In seiner einfachsten Form ist dies nur eine einzelne Tabelle oder Ansicht. Die Quelle kann jedoch auch eine auswählbare gespeicherte Prozedur, eine abgeleitete Tabelle oder ein allgemeiner Tabellenausdruck sein. Mehrere Quellen können mit verschiedenen Arten von Joins kombiniert werden.

Dieser Abschnitt konzentriert sich auf Single-Source-Selects. Joins werden in einem der folgenden Abschnitte behandelt.

Syntax
SELECT
  ...
  FROM <source>
  [<joins>]
  [...]

<source> ::=
  { table
  | view
  | selectable-stored-procedure [(<args>)]
  | <derived-table>
  | <common-table-expression>
  } [[AS] alias]

<derived-table> ::=
  (<select-statement>) [[AS] alias] [(<column-aliases>)]

<common-table-expression> ::=
  WITH [RECURSIVE] <cte-def> [, <cte-def> ...]
  <select-statement>

<cte-def> ::= name [(<column-aliases>)] AS (<select-statement>)

<column-aliases> ::= column-alias [, column-alias ...]
Tabelle 70. Argumente für die FROM-Klausel
Argument Beschreibung

table

Name einer Tabelle

view

Name einer Ansicht

selectable-stored-procedure

Name einer auswählbaren Stored Procedure

args

Selektierbare Argumente für gespeicherte Prozeduren

derived-table

Abgeleiteter Tabellenabfrageausdruck

cte-def

Common Table Expression (CTE)-Definition, einschließlich eines “ad hoc”-Namens

select-statement

Beliebige SELECT-Anweisung

column-aliases

Alias für eine Spalte in einer Beziehung, CTE oder abgeleiteten Tabelle

name

Der “ad hoc”-Name für einen CTE

alias

Der Alias einer Datenquelle (Tabelle, Sicht, Prozedur, CTE, abgeleitete Tabelle)

Auswählen mit FROM in einer Tabelle oder Ansicht

Bei der Auswahl aus einer einzelnen Tabelle oder Ansicht erfordert die FROM-Klausel nur den Namen. Ein Alias kann nützlich oder sogar notwendig sein, wenn es Unterabfragen gibt, die sich auf die Haupt-select-Anweisung beziehen (wie sie es oft tun — Unterabfragen wie diese werden als korrelierte Unterabfragen bezeichnet).

Beispiele
select id, name, sex, age from actors
where state = 'Ohio'
select * from birds
where type = 'flightless'
order by family, genus, species
select firstname,
  middlename,
  lastname,
  date_of_birth,
  (select name from schools s where p.school = s.id) schoolname
from pupils p
where year_started = '2012'
order by schoolname, date_of_birth
Mischen Sie niemals Spaltennamen mit Spaltenaliasen!

Wenn Sie einen Alias für eine Tabelle oder einen View angeben, müssen Sie diesen Alias immer anstelle des Tabellennamens verwenden, wenn Sie die Spalten der Relation abfragen (und überall dort, wo Sie sonst auf Spalten verweisen, z. GROUP BY- und WHERE-Klauseln).

Richtige Verwendung:

SELECT PEARS
FROM FRUIT;

SELECT FRUIT.PEARS
FROM FRUIT;

SELECT PEARS
FROM FRUIT F;

SELECT F.PEARS
FROM FRUIT F;

Falsche Verwendung:

SELECT FRUIT.PEARS
FROM FRUIT F;
Auswählen von FROM einer gespeicherten Prozedur

Eine auswählbare gespeicherte Prozedur ist eine Prozedur, die:

  • enthält mindestens einen Ausgabeparameter und

  • verwendet das Schlüsselwort SUSPEND, damit der Aufrufer die Ausgabezeilen einzeln abrufen kann, genau wie bei der Auswahl aus einer Tabelle oder Ansicht.

Die Ausgabeparameter einer auswählbaren gespeicherten Prozedur entsprechen den Spalten einer regulären Tabelle.

Die Auswahl aus einer gespeicherten Prozedur ohne Eingabeparameter entspricht der Auswahl aus einer Tabelle oder Ansicht:

select * from suspicious_transactions
  where assignee = 'John'

Alle erforderlichen Eingabeparameter müssen nach dem Prozedurnamen in Klammern angegeben werden:

select name, az, alt from visible_stars('Brugge', current_date, '22:30')
  where alt >= 20
  order by az, alt

Werte für optionale Parameter (dh Parameter, für die Standardwerte definiert wurden) können weggelassen oder bereitgestellt werden. Wenn Sie sie jedoch nur teilweise bereitstellen, müssen sich die weggelassenen Parameter alle am Ende befinden.

Angenommen, die Prozedur visible_stars aus dem vorherigen Beispiel hat zwei optionale Parameter: min_magn (numeric(3,1)) und spectral_class (varchar(12)), sind die folgenden Abfragen gültig:

select name, az, alt
from visible_stars('Brugge', current_date, '22:30');

select name, az, alt
from visible_stars('Brugge', current_date, '22:30', 4.0);

select name, az, alt
from visible_stars('Brugge', current_date, '22:30', 4.0, 'G');

Dies ist jedoch nicht der Fall, da die Parameterliste ein “hole” enthält:

select name, az, alt
from visible_stars('Brugge', current_date, '22:30', 'G');

Ein Alias für eine auswählbare gespeicherte Prozedur wird nach der Parameterliste angegeben:

select
  number,
  (select name from contestants c where c.number = gw.number)
from get_winners('#34517', 'AMS') gw

Wenn Sie auf einen Ausgabeparameter (“column”) verweisen, indem Sie ihn mit dem vollständigen Prozedurnamen qualifizieren, sollte der Prozeduralias weggelassen werden:

select
  number,
  (select name from contestants c where c.number = get_winners.number)
from get_winners('#34517', 'AMS')
Abfragen einer abgeleiteten Tabelle mittels FROM

Eine abgeleitete Tabelle ist eine gültige SELECT-Anweisung in Klammern, optional gefolgt von einem Tabellenalias und/oder Spaltenaliasen. Die Ergebnismenge der Anweisung fungiert als virtuelle Tabelle, die die einschließende Anweisung abfragen kann.

Syntax
(<select-query>)
  [[AS] derived-table-alias]
  [(<derived-column-aliases>)]

<derived-column-aliases> := column-alias [, column-alias ...]

Der von diesem “SELECT FROM (SELECT FROM..)”-Stil der Anweisung zurückgegebene Datensatz ist eine virtuelle Tabelle, die innerhalb der einschließenden Anweisung abgefragt werden kann, als wäre es eine reguläre Tabelle oder Ansicht.

Beispiel using a derived table

Die abgeleitete Tabelle in der folgenden Abfrage gibt die Liste der Tabellennamen in der Datenbank und die Anzahl der Spalten in jeder Tabelle zurück. Eine “Drill-Down”-Abfrage für die abgeleitete Tabelle gibt die Anzahl der Felder und die Anzahl der Tabellen mit jeder Feldanzahl zurück:

SELECT
  FIELDCOUNT,
  COUNT(RELATION) AS NUM_TABLES
FROM (SELECT
        R.RDB$RELATION_NAME RELATION,
        COUNT(*) AS FIELDCOUNT
      FROM RDB$RELATIONS R
        JOIN RDB$RELATION_FIELDS RF
        ON RF.RDB$RELATION_NAME = R.RDB$RELATION_NAME
        GROUP BY RELATION)
GROUP BY FIELDCOUNT

Ein triviales Beispiel, das zeigt, wie der Alias einer abgeleiteten Tabelle und die Liste der Spaltenaliase (beide optional) verwendet werden können:

SELECT
  DBINFO.DESCR, DBINFO.DEF_CHARSET
FROM (SELECT *
      FROM RDB$DATABASE) DBINFO
        (DESCR, REL_ID, SEC_CLASS, DEF_CHARSET)
Mehr über abgeleitete Tabellen

Abgeleitete Tabellen können

  • verschachtelt sein

  • Gewerkschaften sein und in Gewerkschaften verwendet werden können

  • enthalten Aggregatfunktionen, Unterabfragen und Joins

  • in Aggregatfunktionen, Unterabfragen und Joins verwendet werden

  • Aufrufe an auswählbare gespeicherte Prozeduren oder Abfragen an diese sein

  • haben WHERE, ORDER BY und GROUP BY Klauseln, FIRST/SKIP oder ROWS Direktiven, et al.

Außerdem,

  • Jede Spalte in einer abgeleiteten Tabelle muss einen Namen haben. Wenn es keinen Namen hat, z. B. wenn es sich um eine Konstante oder einen Laufzeitausdruck handelt, sollte ihm ein Alias zugewiesen werden, entweder auf reguläre Weise oder durch Einfügen in die Liste der Spaltenaliase in der Spezifikation der abgeleiteten Tabelle.

    • Die Liste der Spaltenaliase ist optional, aber falls vorhanden, muss sie für jede Spalte in der abgeleiteten Tabelle einen Alias enthalten

  • Der Optimierer kann abgeleitete Tabellen sehr effektiv verarbeiten. Wenn jedoch eine abgeleitete Tabelle in einen Inner Join eingeschlossen ist und eine Unterabfrage enthält, kann der Optimierer keine Join-Reihenfolge verwenden.

Ein nützlicheres Beispiel

Angenommen, wir haben eine Tabelle COEFFS, die die Koeffizienten einer Reihe von quadratischen Gleichungen enthält, die wir lösen müssen. Es wurde wie folgt definiert:

create table coeffs (
  a double precision not null,
  b double precision not null,
  c double precision not null,
  constraint chk_a_not_zero check (a <> 0)
)

Abhängig von den Werten von 'a', 'b' und 'c' kann jede Gleichung null, eine oder zwei Lösungen haben. Es ist möglich, diese Lösungen mit einer einstufigen Abfrage der Tabelle COEFFS zu finden, aber der Code sieht ziemlich unordentlich aus und mehrere Werte (wie die Diskriminante) müssen mehrmals pro Zeile berechnet werden. Eine abgeleitete Tabelle kann hier helfen, die Dinge sauber zu halten:

select
  iif (D >= 0, (-b - sqrt(D)) / denom, null) sol_1,
  iif (D >  0, (-b + sqrt(D)) / denom, null) sol_2
  from
    (select b, b*b - 4*a*c, 2*a from coeffs) (b, D, denom)

Wenn wir die Koeffizienten neben den Lösungen anzeigen möchten (was möglicherweise keine schlechte Idee ist), können wir die Abfrage wie folgt ändern:

select
  a, b, c,
  iif (D >= 0, (-b - sqrt(D)) / denom, null) sol_1,
  iif (D >  0, (-b + sqrt(D)) / denom, null) sol_2
  from
    (select a, b, c, b*b - 4*a*c as D, 2*a as denom
     from coeffs)

Beachten Sie, dass, während die erste Abfrage eine Spaltenaliasliste für die abgeleitete Tabelle verwendet, die zweite bei Bedarf intern Aliase hinzufügt. Beide Methoden funktionieren, solange jede Spalte garantiert einen Namen hat.

Alle Spalten in der abgeleiteten Tabelle werden so oft ausgewertet, wie sie in der Hauptabfrage angegeben sind. Dies ist wichtig, da es bei der Verwendung nichtdeterministischer Funktionen zu unerwarteten Ergebnissen führen kann. Das Folgende zeigt ein Beispiel dafür.

SELECT
  UUID_TO_CHAR(X) AS C1,
  UUID_TO_CHAR(X) AS C2,
  UUID_TO_CHAR(X) AS C3
FROM (SELECT GEN_UUID() AS X
      FROM RDB$DATABASE) T;

Das Ergebnis, wenn diese Abfrage drei verschiedene Werte erzeugt:

C1  80AAECED-65CD-4C2F-90AB-5D548C3C7279
C2  C1214CD3-423C-406D-B5BD-95BF432ED3E3
C3  EB176C10-F754-4689-8B84-64B666381154

Um ein einzelnes Ergebnis der Funktion GEN_UUID sicherzustellen, können Sie die folgende Methode verwenden:

SELECT
  UUID_TO_CHAR(X) AS C1,
  UUID_TO_CHAR(X) AS C2,
  UUID_TO_CHAR(X) AS C3
FROM (SELECT GEN_UUID() AS X
      FROM RDB$DATABASE
      UNION ALL
      SELECT NULL FROM RDB$DATABASE WHERE 1 = 0) T;

Diese Abfrage erzeugt ein einzelnes Ergebnis für alle drei Spalten:

C1  80AAECED-65CD-4C2F-90AB-5D548C3C7279
C2  80AAECED-65CD-4C2F-90AB-5D548C3C7279
C3  80AAECED-65CD-4C2F-90AB-5D548C3C7279

Eine alternative Lösung besteht darin, die Abfrage 'GEN_UUID' in eine Unterabfrage einzuschließen:

SELECT
  UUID_TO_CHAR(X) AS C1,
  UUID_TO_CHAR(X) AS C2,
  UUID_TO_CHAR(X) AS C3
FROM (SELECT
        (SELECT GEN_UUID() FROM RDB$DATABASE) AS X
      FROM RDB$DATABASE) T;

Dies ist ein Artefakt der aktuellen Implementierung. Dieses Verhalten kann sich in einer zukünftigen Firebird-Version ändern.

Abfragen einer Common Table Expression (CTE) mittels FROM

Ein allgemeiner Tabellenausdruck – oder CTE – ist eine komplexere Variante der abgeleiteten Tabelle, aber auch leistungsfähiger. Eine Präambel, die mit dem Schlüsselwort WITH beginnt, definiert eine oder mehrere benannte CTEs, jede mit einer optionalen Spalten-Alias-Liste. Die Hauptabfrage, die der Präambel folgt, kann dann auf diese CTEs zugreifen, als wären es reguläre Tabellen oder Ansichten. Die CTEs verlassen den Gültigkeitsbereich, sobald die Hauptabfrage vollständig ausgeführt wurde.

Eine vollständige Diskussion der CTEs finden Sie im Abschnitt Common Table Expressions (“WITH …​ AS …​ SELECT”).

Das Folgende ist eine Umschreibung unseres abgeleiteten Tabellenbeispiels als CTE:

with vars (b, D, denom) as (
  select b, b*b - 4*a*c, 2*a from coeffs
)
select
  iif (D >= 0, (-b - sqrt(D)) / denom, null) sol_1,
  iif (D >  0, (-b + sqrt(D)) / denom, null) sol_2
from vars

Abgesehen davon, dass die Berechnungen, die zuerst durchgeführt werden müssen, jetzt am Anfang stehen, ist dies keine große Verbesserung gegenüber der abgeleiteten Tabellenversion. Allerdings können wir jetzt auch die doppelte Berechnung von sqrt(D) für jede Zeile eliminieren:

with vars (b, D, denom) as (
  select b, b*b - 4*a*c, 2*a from coeffs
),
vars2 (b, D, denom, sqrtD) as (
  select b, D, denom, iif (D >= 0, sqrt(D), null) from vars
)
select
  iif (D >= 0, (-b - sqrtD) / denom, null) sol_1,
  iif (D >  0, (-b + sqrtD) / denom, null) sol_2
from vars2

Der Code ist jetzt etwas komplizierter, kann aber effizienter ausgeführt werden (je nachdem, was länger dauert: Ausführen der SQRT-Funktion oder Übergabe der Werte von b, D und denom durch einen zusätzlichen CTE) . Übrigens hätten wir das auch mit abgeleiteten Tabellen machen können, aber das würde eine Verschachtelung erfordern.

Alle Spalten im CTE werden so oft ausgewertet, wie sie in der Hauptabfrage angegeben sind. Dies ist wichtig, da es bei der Verwendung nichtdeterministischer Funktionen zu unerwarteten Ergebnissen führen kann. Das Folgende zeigt ein Beispiel dafür.

WITH T (X) AS (
  SELECT GEN_UUID()
  FROM RDB$DATABASE)
SELECT
  UUID_TO_CHAR(X) as c1,
  UUID_TO_CHAR(X) as c2,
  UUID_TO_CHAR(X) as c3
FROM T

Das Ergebnis, wenn diese Abfrage drei verschiedene Werte erzeugt:

C1  80AAECED-65CD-4C2F-90AB-5D548C3C7279
C2  C1214CD3-423C-406D-B5BD-95BF432ED3E3
C3  EB176C10-F754-4689-8B84-64B666381154

Um ein einzelnes Ergebnis der Funktion GEN_UUID sicherzustellen, können Sie die folgende Methode verwenden:

WITH T (X) AS (
  SELECT GEN_UUID()
  FROM RDB$DATABASE
  UNION ALL
  SELECT NULL FROM RDB$DATABASE WHERE 1 = 0)
SELECT
  UUID_TO_CHAR(X) as c1,
  UUID_TO_CHAR(X) as c2,
  UUID_TO_CHAR(X) as c3
FROM T;

Diese Abfrage erzeugt ein einzelnes Ergebnis für alle drei Spalten:

C1  80AAECED-65CD-4C2F-90AB-5D548C3C7279
C2  80AAECED-65CD-4C2F-90AB-5D548C3C7279
C3  80AAECED-65CD-4C2F-90AB-5D548C3C7279

Eine alternative Lösung besteht darin, die Abfrage 'GEN_UUID' in eine Unterabfrage einzuschließen:

WITH T (X) AS (
  SELECT (SELECT GEN_UUID() FROM RDB$DATABASE)
  FROM RDB$DATABASE)
SELECT
  UUID_TO_CHAR(X) as c1,
  UUID_TO_CHAR(X) as c2,
  UUID_TO_CHAR(X) as c3
FROM T;

Dies ist ein Artefakt der aktuellen Implementierung. Dieses Verhalten kann sich in einer zukünftigen Firebird-Version ändern.

6.1.4. Joins

Joins kombinieren Daten aus zwei Quellen zu einem einzigen Satz. Dies erfolgt zeilenweise und beinhaltet normalerweise die Überprüfung einer Join-Bedingung, um zu bestimmen, welche Zeilen zusammengeführt und im resultierenden Dataset erscheinen sollen. Es gibt verschiedene Typen (INNER, OUTER) und Klassen (qualifiziert, natürlich usw.) von Joins, jede mit ihrer eigenen Syntax und eigenen Regeln.

Da Joins verkettet werden können, können die an einem Join beteiligten Datasets selbst verbundene Sets sein.

Syntax
SELECT
   ...
   FROM <source>
   [<joins>]
   [...]

<source> ::=
  { table
  | view
  | selectable-stored-procedure [(<args>)]
  | <derived-table>
  | <common-table-expression>
  } [[AS] alias]

<joins> ::= <join> [<join> ...]

<join> ::=
    [<join-type>] JOIN <source> <join-condition>
  | NATURAL [<join-type>] JOIN <source>
  | {CROSS JOIN | ,} <source>

<join-type> ::= INNER | {LEFT | RIGHT | FULL} [OUTER]

<join-condition> ::= ON <condition> | USING (<column-list>)
Tabelle 71. Argumente für die JOIN-Klausel
Argument Beschreibung

table

Name einer Tabelle

view

Name einer Ansicht

selectable-stored-procedure

Name einer auswählbaren Stored Procedure

args

Wählbare Eingabeparameter für gespeicherte Prozeduren

derived-table

Verweis, namentlich, auf eine abgeleitete Tabelle

common-table-expression

Verweis nach Name auf einen allgemeinen Tabellenausdruck (CTE)

alias

Ein Alias für eine Datenquelle (Tabelle, Sicht, Prozedur, CTE, abgeleitete Tabelle)

condition

Join-Bedingung (Kriterium)

column-list

Die Liste der Spalten, die für einen Equi-Join verwendet werden

Inner vs. Outer Joins

Ein Join kombiniert immer Datenzeilen aus zwei Sätzen (normalerweise als linker Satz und rechter Satz bezeichnet). Standardmäßig gelangen nur Zeilen in die Ergebnismenge, die die Join-Bedingung erfüllen (d. h. die mindestens einer Zeile in der anderen Menge entsprechen, wenn die Join-Bedingung angewendet wird). Dieser Standard-Join-Typ wird als inner join bezeichnet. Angenommen, wir haben die folgenden zwei Tabellen:

Tabelle A
ID S

87

Just some text

235

Silence

Tabelle B
CODE X

-23

56.7735

87

416.0

Wenn wir diese Tabellen wie folgt verbinden:

select *
  from A
  join B on A.id = B.code;

dann ist die Ergebnismenge:

ID S CODE X

87

Just some text

87

416.0

Die erste Reihe von A wurde mit der zweiten Reihe von B verbunden, weil sie zusammen die Bedingung “A.id = B.code” erfüllten. Die anderen Zeilen aus den Quelltabellen haben keine Übereinstimmung in der entgegengesetzten Menge und werden daher nicht in den Join aufgenommen. Denken Sie daran, dies ist ein INNER-Join. Wir können diese Tatsache explizit machen, indem wir schreiben:

select *
  from A
  inner join B on A.id = B.code;

Da jedoch INNER die Vorgabe ist, wird es normalerweise weggelassen.

Es ist durchaus möglich, dass eine Reihe im linken Satz mit mehreren Reihen im rechten Satz übereinstimmt oder umgekehrt. In diesem Fall sind alle diese Kombinationen enthalten, und wir können Ergebnisse erhalten wie:

ID S CODE X

87

Just some text

87

416.0

87

Just some text

87

-1.0

-23

Don’t know

-23

56.7735

-23

Still don’t know

-23

56.7735

-23

I give up

-23

56.7735

Manchmal möchten (oder müssen) wir alle Zeilen einer oder beider Quellen in der verbundenen Menge erscheinen, unabhängig davon, ob sie mit einem Datensatz in der anderen Quelle übereinstimmen. Hier kommen Outer Joins ins Spiel. Ein 'LEFT' Outer Join enthält alle Datensätze aus dem linken Satz, aber nur übereinstimmende Datensätze aus dem rechten Satz. Bei einem RIGHT Outer Join ist es umgekehrt. FULL Outer Joins beinhalten alle Datensätze aus beiden Sets. In allen Outer Joins werden die "Löcher" (die Stellen, an denen ein eingeschlossener Quelldatensatz keine Übereinstimmung im anderen Satz hat) mit NULL aufgefüllt.

Um einen Outer Join zu erstellen, müssen Sie LEFT, RIGHT oder FULL angeben, optional gefolgt vom Schlüsselwort OUTER.

Unten sind die Ergebnisse der verschiedenen Outer Joins, wenn sie auf unsere ursprünglichen Tabellen A und B angewendet werden:

select *
  from A
  left [outer] join B on A.id = B.code;
ID S CODE X

87

Just some text

87

416.0

235

Silence

<null>

<null>

select *
  from A
  right [outer] join B on A.id = B.code
ID S CODE X

<null>

<null>

-23

56.7735

87

Just some text

87

416.0

select *
  from A
  full [outer] join B on A.id = B.code
ID S CODE X

<null>

<null>

-23

56.7735

87

Just some text

87

416.0

235

Silence

<null>

<null>

Qualifizierte joins

Qualifizierte Joins geben Bedingungen für das Kombinieren von Zeilen an. Dies geschieht entweder explizit in einer ON-Klausel oder implizit in einer USING-Klausel.

Syntax
<qualified-join> ::= [<join-type>] JOIN <source> <join-condition>

<join-type> ::= INNER | {LEFT | RIGHT | FULL} [OUTER]

<join-condition> ::= ON <condition> | USING (<column-list>)
Joins mit expliziter Bedingung

Die meisten qualifizierten Joins haben eine ON-Klausel mit einer expliziten Bedingung, die jeder gültige boolesche Ausdruck sein kann, aber normalerweise einen Vergleich zwischen den beiden beteiligten Quellen beinhaltet.

Sehr oft ist die Bedingung ein Gleichheitstest (oder eine Reihe von AND-verknüpften Gleichheitstests) mit dem Operator “=”. Joins wie diese heißen equi-joins. (Die Beispiele im Abschnitt über innere und äußere Verknüpfungen waren alle Gleichverknüpfungen.)

Beispiele für Joins mit einer expliziten Bedingung:

/* Wählen Sie alle Detroit-Kunden aus, die einen Kauf getätigt haben
    2013, zusammen mit den Kaufdetails: */
select * from customers c
  join sales s on s.cust_id = c.id
  where c.city = 'Detroit' and s.year = 2013;
/* Wie oben, aber auch nicht kaufende Kunden: */
select * from customers c
  left join sales s on s.cust_id = c.id
  where c.city = 'Detroit' and s.year = 2013;
/* Wählen Sie für jeden Mann die Frauen aus, die größer sind als er.
    Männer, für die es keine solche Frau gibt, werden nicht berücksichtigt. */
select m.fullname as man, f.fullname as woman
  from males m
  join females f on f.height > m.height;
/* Wählen Sie alle Schüler mit ihrer Klasse und ihrem Mentor aus.
    Auch Schüler ohne Mentor werden einbezogen.
    Schüler ohne Klasse werden nicht berücksichtigt. */
select p.firstname, p.middlename, p.lastname,
       c.name, m.name
  from pupils p
  join classes c on c.id = p.class
  left join mentors m on m.id = p.mentor;
Joins mit benannten Spalten

Equi-Joins vergleichen häufig Spalten mit dem gleichen Namen in beiden Tabellen. Wenn dies der Fall ist, können wir auch den zweiten Typ eines qualifizierten Joins verwenden: den benannten Spalten join.

Benannte Spalten-Joins werden in Dialekt-1-Datenbanken nicht unterstützt.

Benannte Spalten-Joins haben eine USING-Klausel, die nur die Spaltennamen angibt. Also stattdessen:

select * from flotsam f
  join jetsam j
  on f.sea = j.sea
  and f.ship = j.ship;

wir können auch schreiben:

select * from flotsam
  join jetsam using (sea, ship)

was deutlich kürzer ist. Die Ergebnismenge ist jedoch etwas anders — zumindest bei Verwendung von “SELECT *”:

  • Der Join mit expliziter Bedingung — mit der ON-Klausel — enthält jede der Spalten SEA und SHIP zweimal: einmal aus der Tabelle FLOTSAM und einmal aus der Tabelle JETSAM. Offensichtlich haben sie die gleichen Werte.

  • Der Join mit benannten Spalten – mit der USING-Klausel – enthält diese Spalten nur einmal.

Wenn Sie alle Spalten in der Ergebnismenge der benannten Spalten verknüpfen möchten, richten Sie Ihre Abfrage wie folgt ein:

select f.*, j.*
  from flotsam f
  join jetsam j using (sea, ship);

Dadurch erhalten Sie genau die gleiche Ergebnismenge wie beim Join mit expliziter Bedingung.

Für einen OUTER benannten Spalten-Join gibt es eine zusätzliche Wendung, wenn “SELECT *” oder ein nicht qualifizierter Spaltenname aus der USING-Liste verwendet wird:

Wenn eine Zeile aus einem Quellsatz keine Übereinstimmung im anderen hat, aber aufgrund der Direktiven LEFT, RIGHT oder FULL trotzdem eingeschlossen werden muss, erhält die zusammengeführte Spalte in der verbundenen Menge das Nicht- NULL-Wert. Das ist fair genug, aber jetzt können Sie nicht sagen, ob dieser Wert aus dem linken Satz, dem rechten Satz oder beiden stammt. Dies kann besonders täuschen, wenn der Wert aus dem rechten Satz stammt, da “*” immer kombinierte Spalten im linken Teil anzeigt — auch bei einem RIGHT-Join.

Ob dies ein Problem ist oder nicht, hängt von der Situation ab. Wenn dies der Fall ist, verwenden Sie den oben gezeigten Ansatz “a.*, b.*”, wobei a und b die Namen oder Aliase der beiden Quellen sind. Oder noch besser, vermeiden Sie “*” in Ihren ernsthaften Abfragen und qualifizieren Sie alle Spaltennamen in verbundenen Mengen. Dies hat den zusätzlichen Vorteil, dass Sie sich überlegen müssen, welche Daten Sie woher abrufen möchten.

Es liegt in Ihrer Verantwortung, sicherzustellen, dass die Spaltennamen in der USING-Liste von kompatiblen Typen zwischen den beiden Quellen sind. Wenn die Typen kompatibel, aber nicht gleich sind, konvertiert die Engine sie in den Typ mit dem breitesten Wertebereich, bevor die Werte verglichen werden. Dies ist auch der Datentyp der zusammengeführten Spalte, der in der Ergebnismenge angezeigt wird, wenn “SELECT *” oder der nicht qualifizierte Spaltenname verwendet wird. Qualifizierte Spalten hingegen behalten immer ihren ursprünglichen Datentyp.

Wenn Sie beim Zusammenführen nach benannten Spalten eine Join-Spalte in der WHERE-Klausel verwenden, verwenden Sie immer den qualifizierten Spaltennamen, andernfalls wird kein Index für diese Spalte verwendet.

SELECT 1 FROM t1 a JOIN t2 b USING (x) WHERE x = 0;

-- PLAN JOIN (A NATURAL , B INDEX (RDB$2))

Jedoch:

SELECT 1 FROM t1 a JOIN t2 b USING (x) WHERE a.x = 0;
-- PLAN JOIN (A INDEX (RDB$1), B INDEX (RDB$2))

SELECT 1 FROM t1 a JOIN t2 b USING (x) WHERE b.x = 0;
-- PLAN JOIN (A INDEX (RDB$1), B INDEX (RDB$2))

Tatsache ist, dass die nicht spezifizierte Spalte in diesem Fall implizit durch `COALESCE(a.x, b.x) ersetzt wird. Dieser clevere Trick wird verwendet, um Spaltennamen eindeutig zu machen, stört aber auch die Verwendung des Indexes.

Natural Joins

Um die Idee des benannten Spalten-Joins noch einen Schritt weiter zu gehen, führt ein natural join einen automatischen Equi-Join für alle Spalten mit dem gleichen Namen in der linken und rechten Tabelle durch. Die Datentypen dieser Spalten müssen kompatibel sein.

Natural-Joins werden in Dialekt-1-Datenbanken nicht unterstützt.

Syntax
<natural-join> ::= NATURAL [<join-type>] JOIN <source>

<join-type> ::= INNER | {LEFT | RIGHT | FULL} [OUTER]

Gegeben seien diese beiden Tabellen:

create table TA (
  a bigint,
  s varchar(12),
  ins_date date
);
create table TB (
  a bigint,
  descr varchar(12),
  x float,
  ins_date date
);

Ein natürlicher Join von TA und TB würde die Spalten a und ins_date beinhalten, und die folgenden beiden Anweisungen hätten den gleichen Effekt:

select * from TA
  natural join TB;
select * from TA
  join TB using (a, ins_date);

Wie alle Joins sind natürliche Joins standardmäßig innere Joins, aber Sie können sie in äußere Joins umwandeln, indem Sie LEFT, RIGHT oder FULL vor dem JOIN-Schlüsselwort angeben.

Gibt es in den beiden Quellbeziehungen keine gleichnamigen Spalten, wird ein CROSS JOIN ausgeführt. Wir kommen in einer Minute zu dieser Art von Join.

Cross Joins

Ein Cross-Join erzeugt das Full-Set-Produkt der beiden Datenquellen. Dies bedeutet, dass jede Zeile in der linken Quelle erfolgreich mit jeder Zeile in der rechten Quelle abgeglichen wird.

Syntax
<cross-join> ::= {CROSS JOIN | ,} <source>

Bitte beachten Sie, dass die Kommasyntax veraltet ist! Es wird nur unterstützt, um die Funktionsfähigkeit des Legacy-Codes aufrechtzuerhalten, und kann in einer zukünftigen Version verschwinden.

Das Kreuzverknüpfen zweier Mengen ist äquivalent dazu, sie auf einer Tautologie zu verbinden (eine Bedingung, die immer wahr ist). Die folgenden beiden Aussagen haben die gleiche Wirkung:

select * from TA
  cross join TB;
select * from TA
  join TB on 1 = 1;

Cross-Joins sind Inner-Joins, da sie nur übereinstimmende Datensätze enthalten – es kommt einfach vor, dass jeder Datensatz übereinstimmt! Ein Outer-Cross-Join, falls vorhanden, würde dem Ergebnis nichts hinzufügen, da die hinzugefügten Outer-Joins nicht übereinstimmende Datensätze sind und diese in Cross-Joins nicht vorhanden sind.

Cross-Joins sind selten sinnvoll, außer wenn Sie alle möglichen Kombinationen von zwei oder mehr Variablen auflisten möchten. Angenommen, Sie verkaufen ein Produkt in verschiedenen Größen, Farben und Materialien. Wenn diese Variablen jeweils in einer eigenen Tabelle aufgeführt sind, würde diese Abfrage alle Kombinationen zurückgeben:

select m.name, s.size, c.name
  from materials m
  cross join sizes s
  cross join colors c;
Implizite Joins

Im SQL:89-Standard wurden die an einem Join beteiligten Tabellen als durch Kommas getrennte Liste in der FROM-Klausel angegeben (mit anderen Worten, ein Cross Join ). Die Join-Bedingungen wurden dann neben anderen Suchbegriffen in der WHERE-Klausel angegeben. Diese Art von Join wird als impliziter Join bezeichnet.

Ein Beispiel für einen impliziten Join:

/*
 * Eine Auswahl aller Detroit-Kunden, die
 * einen Einkauf getätigt haben
 */
SELECT *
FROM customers c, sales s
WHERE s.cust_id = c.id AND c.city = 'Detroit'

Die implizite Join-Syntax ist veraltet und wird möglicherweise in einer zukünftigen Version entfernt. Wir empfehlen, die zuvor gezeigte explizite Join-Syntax zu verwenden.

Explizite und implizite Verknüpfungen mischen

Das Mischen von expliziten und impliziten Joins wird nicht empfohlen, ist jedoch zulässig. Einige Arten des Mischens werden jedoch von Firebird nicht unterstützt.

Die folgende Abfrage gibt beispielsweise den Fehler “Spalte gehört nicht zur referenzierten Tabelle” aus.

SELECT *
FROM TA, TB
JOIN TC ON TA.COL1 = TC.COL1
WHERE TA.COL2 = TB.COL2

Das liegt daran, dass der explizite Join die Tabelle TA nicht sehen kann. Die nächste Abfrage wird jedoch ohne Fehler abgeschlossen, da die Einschränkung nicht verletzt wird.

SELECT *
FROM TA, TB
JOIN TC ON TB.COL1 = TC.COL1
WHERE TA.COL2 = TB.COL2
Ein Hinweis zu Gleichheit

Dieser Hinweis zu Gleichheits- und Ungleichheitsoperatoren gilt überall in Firebirds SQL-Sprache, nicht nur in JOIN-Bedingungen.

Der Operator “=”, der explizit in vielen bedingten Joins und implizit in benannten Spalten-Joins und natürlichen Joins verwendet wird, gleicht nur Werte mit Werten ab. Nach dem SQL-Standard ist NULL kein Wert und daher sind zwei NULL weder gleich noch ungleich. Wenn NULLs in einem Join miteinander übereinstimmen müssen, verwenden Sie den IS NOT DISTINCT FROM-Operator. Dieser Operator gibt true zurück, wenn die Operanden den gleichen Wert oder haben, wenn beide NULL sind.

select *
  from A join B
  on A.id is not distinct from B.code;

Ebenso in den — extrem seltenen — Fällen, in denen Sie bei inequality beitreten möchten, verwenden Sie IS DISTINCT FROM, nicht “<>”, wenn NULL als anders betrachtet werden soll Wert und zwei NULLs als gleich betrachtet:

select *
  from A join B
  on A.id is distinct from B.code;
Mehrdeutige Feldnamen in Joins

Firebird weist nicht qualifizierte Feldnamen in einer Abfrage zurück, wenn diese Feldnamen in mehr als einem an einem Join beteiligten Dataset vorhanden sind. Dies gilt sogar für innere Equi-Joins, bei denen der Feldname in der ON-Klausel wie folgt vorkommt:

select a, b, c
  from TA
  join TB on TA.a = TB.a;

Von dieser Regel gibt es eine Ausnahme: Bei Named-Column-Joins und Natural-Joins darf der unqualifizierte Feldname einer am Matching-Prozess beteiligten Spalte legal verwendet werden und bezieht sich auf die gleichnamige zusammengeführte Spalte. Bei Joins mit benannten Spalten sind dies die Spalten, die in der USING-Klausel aufgelistet sind. Bei natürlichen Verknüpfungen sind dies die Spalten, die in beiden Beziehungen denselben Namen haben. Beachten Sie aber bitte noch einmal, dass, insbesondere bei Outer-Joins, ein einfacher colname nicht immer gleich links.colname oder right.colname ist. Typen können unterschiedlich sein und eine der qualifizierten Spalten kann NULL sein, während die andere nicht ist. In diesem Fall kann der Wert in der zusammengeführten, nicht qualifizierten Spalte die Tatsache maskieren, dass einer der Quellwerte fehlt.

Joins mit gespeicherten Prozeduren

Wenn ein Join mit einer Stored Procedure durchgeführt wird, die nicht über Eingabeparameter mit anderen Datenströmen korreliert ist, gibt es keine Merkwürdigkeiten. Wenn Korrelation im Spiel ist, offenbart sich eine unangenehme Eigenart. Das Problem ist, dass sich der Optimierer jede Möglichkeit verweigert, die Zusammenhänge der Eingabeparameter der Prozedur aus den Feldern in den anderen Streams zu ermitteln:

SELECT *
FROM MY_TAB
JOIN MY_PROC(MY_TAB.F) ON 1 = 1;

Hier wird die Prozedur ausgeführt, bevor ein einzelner Datensatz aus der Tabelle MY_TAB abgerufen wurde. Der Fehler isc_no_cur_rec error (no current record for fetch operation) wird ausgelöst und unterbricht die Ausführung.

Die Lösung besteht darin, eine Syntax zu verwenden, die die Join-Reihenfolge explizit angibt:

SELECT *
FROM MY_TAB
LEFT JOIN MY_PROC(MY_TAB.F) ON 1 = 1;

Dies erzwingt, dass die Tabelle vor dem Vorgang gelesen wird und alles funktioniert ordnungsgemäß.

Diese Eigenart wurde im Optimierer als Fehler erkannt und wird in der nächsten Version von Firebird behoben.

6.1.5. Die WHERE-Klausel

Die WHERE-Klausel dient dazu, die zurückgegebenen Zeilen auf diejenigen zu beschränken, die den Aufrufer interessieren. Die Bedingung, die dem Schlüsselwort WHERE folgt, kann eine einfache Prüfung wie “AMOUNT = 3” sein oder ein vielschichtiger, verschachtelter Ausdruck mit Unterauswahlen, Prädikaten, Funktionsaufrufen, mathematischen und logischen Operatoren, Kontexvariablen und mehr.

Die Bedingung in der WHERE-Klausel wird oft als Suchbedingung, als Suchausdruck oder einfach als Suche bezeichnet.

In DSQL und ESQL kann der Suchausdruck Parameter enthalten. Dies ist sinnvoll, wenn eine Abfrage mit unterschiedlichen Eingabewerten mehrmals wiederholt werden muss. In der SQL-Zeichenfolge, die an den Server übergeben wird, werden Fragezeichen als Platzhalter für die Parameter verwendet. Sie werden positionale Parameter genannt, weil sie nur durch ihre Position im String unterschieden werden können. Konnektivitätsbibliotheken unterstützen oft named parameters der Form :id, :amount, :a usw. Diese sind benutzerfreundlicher; die Bibliothek kümmert sich um die Übersetzung der benannten Parameter in Positionsparameter, bevor die Anweisung an den Server übergeben wird.

Die Suchbedingung kann auch lokale (PSQL) oder Host- (ESQL) Variablennamen enthalten, denen ein Doppelpunkt vorangestellt ist.

Syntax
SELECT ...
  FROM ...
  [...]
  WHERE <search-condition>
  [...]
Tabelle 72. WHERE-Argumente
Parameter Beschreibung

search-condition

Ein boolescher Ausdruck, der TRUE, FALSE oder möglicherweise UNKNOWN (NULL) zurückgibt.

Nur die Zeilen, für die die Suchbedingung 'TRUE' ergibt, werden in die Ergebnismenge aufgenommen. Seien Sie vorsichtig mit möglichen NULL-Ergebnissen: Wenn Sie einen NULL-Ausdruck mit NOT negieren, ist das Ergebnis immer noch NULL und die Zeile wird nicht passieren. Dies wird in einem der folgenden Beispiele demonstriert.

Beispiele
select genus, species from mammals
  where family = 'Felidae'
  order by genus;
select * from persons
  where birthyear in (1880, 1881)
     or birthyear between 1891 and 1898;
select name, street, borough, phone
  from schools s
  where exists (select * from pupils p where p.school = s.id)
  order by borough, street;
select * from employees
  where salary >= 10000 and position <> 'Manager';
select name from wrestlers
  where region = 'Europe'
    and weight > all (select weight from shot_putters
                      where region = 'Africa');
select id, name from players
  where team_id = (select id from teams where name = 'Buffaloes');
select sum (population) from towns
  where name like '%dam'
  and province containing 'land';
select password from usertable
  where username = current_user;

Das folgende Beispiel zeigt, was passieren kann, wenn die Suchbedingung NULL ergibt.

Angenommen, Sie haben eine Tabelle mit den Namen einiger Kinder und der Anzahl der Murmeln (engl. marbles), die sie besitzen. Zu einem bestimmten Zeitpunkt enthält die Tabelle diese Daten:

CHILD MARBLES

Anita

23

Bob E.

12

Chris

<null>

Deirdre

1

Eve

17

Fritz

0

Gerry

21

Hadassah

<null>

Isaac

6

Beachten Sie zunächst den Unterschied zwischen NULL und 0: Fritz hat bekannt überhaupt keine Murmeln, Chris' und Hadassah’s Murmeln sind unbekannt.

Wenn Sie nun diese SQL-Anweisung ausgeben:

select list(child) from marbletable where marbles > 10;

Sie erhalten die Namen Anita, Bob E., Eve und Gerry. Diese Kinder haben alle mehr als 10 Murmeln.

Wenn Sie den Ausdruck negieren:

select list(child) from marbletable where not marbles > 10

Deirdre, Fritz und Isaac sind an der Reihe, die Liste zu füllen. Chris und Hadassah sind nicht enthalten, da sie nicht bekannt haben, dass sie zehn Murmeln oder weniger haben. Sollten Sie diese letzte Abfrage ändern in:

select list(child) from marbletable where marbles <= 10;

das Ergebnis bleibt gleich, da der Ausdruck NULL <= 10 UNKNOWN ergibt. Dies ist nicht dasselbe wie TRUE, daher werden Chris und Hadassah nicht aufgeführt. Wenn Sie möchten, dass sie mit den “armen”-Kindern aufgelistet werden, ändern Sie die Abfrage in:

select list(child) from marbletable
where marbles <= 10 or marbles is null;

Jetzt wird die Suchbedingung für Chris und Hadassah wahr, da “marbles is null” in ihrem Fall offensichtlich TRUE zurückgibt. Tatsächlich kann die Suchbedingung jetzt für niemanden NULL sein.

Zuletzt zwei Beispiele für SELECT-Abfragen mit Parametern in der Suche. Es hängt von der Anwendung ab, wie Sie Abfrageparameter definieren sollten und ob dies überhaupt möglich ist. Beachten Sie, dass Abfragen wie diese nicht sofort ausgeführt werden können: Sie müssen zuerst vorbereitet werden. Nachdem eine parametrisierte Abfrage erstellt wurde, kann der Benutzer (oder der aufrufende Code) Werte für die Parameter bereitstellen und mehrmals ausführen lassen, wobei vor jedem Aufruf neue Werte eingegeben werden. Wie die Werte eingegeben und die Ausführung gestartet wird, bleibt der Anwendung überlassen. In einer GUI-Umgebung gibt der Benutzer typischerweise die Parameterwerte in ein oder mehrere Textfelder ein und klickt dann auf eine Schaltfläche "Ausführen", "Ausführen" oder "Aktualisieren".

select name, address, phone frome stores
  where city = ? and class = ?;
select * from pants
  where model = :model and size = :size and color = :col;

Die letzte Abfrage kann nicht direkt an die Engine übergeben werden; die Anwendung muss es zuerst in das andere Format konvertieren und benannte Parameter Positionsparametern zuordnen.

6.1.6. Die GROUP BY-Klausel

GROUP BY führt Ausgabezeilen, die dieselbe Kombination von Werten in ihrer Elementliste haben, zu einer einzigen Zeile zusammen. Aggregatfunktionen in der Auswahlliste werden auf jede Gruppe einzeln und nicht auf den gesamten Datensatz angewendet.

Wenn die Auswahlliste nur Aggregatspalten enthält oder allgemeiner Spalten, deren Werte nicht von einzelnen Zeilen in der zugrunde liegenden Menge abhängen, ist GROUP BY optional. Wenn es weggelassen wird, besteht die endgültige Ergebnismenge von aus einer einzelnen Zeile (vorausgesetzt, dass mindestens eine aggregierte Spalte vorhanden ist).

Wenn die Auswahlliste sowohl Aggregatspalten als auch Spalten enthält, deren Werte pro Zeile variieren können, wird die GROUP BY-Klausel obligatorisch.

Syntax
SELECT ... FROM ...
  GROUP BY <grouping-item> [, <grouping-item> ...]
  [HAVING <grouped-row-condition>]
  ...

<grouping-item> ::=
    <non-aggr-select-item>
  | <non-aggr-expression>

<non-aggr-select-item> ::=
    column-copy
  | column-alias
  | column-position
Tabelle 73. Argumente für die GROUP BY-Klausel
Argument Beschreibung

non-aggr-expression

Jeder nicht aggregierende Ausdruck, der nicht in der SELECT-Liste enthalten ist, d. h. nicht ausgewählte Spalten aus dem Quellsatz oder Ausdrücke, die überhaupt nicht von den Daten im Satz abhängen

column-copy

Eine wörtliche Kopie aus der SELECT-Liste eines Ausdrucks, der keine Aggregatfunktion enthält

column-alias

Der Alias aus der SELECT-Liste eines Ausdrucks (Spalte), der keine Aggregatfunktion enthält

column-position

Die Positionsnummer in der SELECT-Liste eines Ausdrucks (Spalte), der keine Aggregatfunktion enthält

Als allgemeine Faustregel gilt, dass jedes nicht aggregierte Element in der SELECT-Liste auch in der GROUP BY-Liste enthalten sein muss. Sie können dies auf drei Arten tun:

  1. Durch wörtliches Kopieren des Artikels aus der Auswahlliste, z.B. “class” oder “'D:' || upper(doccode)”.

  2. Durch Angabe des Spaltenalias, falls vorhanden.

  3. Durch Angabe der Spaltenposition als Ganzzahl literal zwischen 1 und der Anzahl der Spalten. Ganzzahlwerte, die aus Ausdrücken oder Parameterersetzungen resultieren, sind einfach unveränderlich und werden als solche in der Gruppierung verwendet. Sie haben jedoch keine Auswirkung, da ihr Wert für jede Zeile gleich ist.

Wenn Sie nach einer Spaltenposition gruppieren, wird der Ausdruck an dieser Position intern aus der Auswahlliste kopiert. Wenn es sich um eine Unterabfrage handelt, wird diese Unterabfrage in der Gruppierungsphase erneut ausgeführt. Das heißt, das Gruppieren nach der Spaltenposition, anstatt den Unterabfrageausdruck in der Gruppierungsklausel zu duplizieren, spart Tastenanschläge und Bytes, aber es ist keine Möglichkeit, Verarbeitungszyklen zu sparen!

Zusätzlich zu den erforderlichen Elementen kann die Gruppierungsliste auch Folgendes enthalten:

  • Spalten aus der Quelltabelle, die nicht in der Auswahlliste enthalten sind, oder nicht aggregierte Ausdrücke, die auf solchen Spalten basieren. Das Hinzufügen solcher Spalten kann die Gruppen weiter unterteilen. Da sich diese Spalten jedoch nicht in der Auswahlliste befinden, können Sie nicht erkennen, welche aggregierte Zeile welchem ​​Wert in der Spalte entspricht. Wenn Sie also an diesen Informationen interessiert sind, nehmen Sie im Allgemeinen auch die Spalte oder den Ausdruck in die Auswahlliste auf — was Sie zu der Regel zurückbringt: “Jede nicht aggregierte Spalte in der Auswahlliste muss auch in der Gruppierungsliste”.

  • Ausdrücke, die nicht von den Daten in der zugrunde liegenden Menge abhängig sind, z. Konstanten, Kontextvariablen, einwertige nicht korrelierte Unterauswahlen usw. Dies wird nur der Vollständigkeit halber erwähnt, da das Hinzufügen solcher Elemente völlig sinnlos ist: Sie beeinflussen die Gruppierung überhaupt nicht. “Harmlose aber nutzlose” Elemente wie diese können auch in der Auswahlliste vorkommen, ohne in die Gruppierungsliste kopiert zu werden.

Beispiele

Wenn die Auswahlliste nur aggregierte Spalten enthält, ist GROUP BY nicht obligatorisch:

select count(*), avg(age) from students
  where sex = 'M';

Dadurch wird eine einzelne Zeile zurückgegeben, die die Anzahl der männlichen Studenten und ihr Durchschnittsalter auflistet. Das Hinzufügen von Ausdrücken, die nicht von Werten in einzelnen Zeilen der Tabelle STUDENTS abhängen, ändert daran nichts:

select count(*), avg(age), current_date from students
  where sex = 'M';

Die Zeile enthält jetzt eine zusätzliche Spalte mit dem aktuellen Datum, aber ansonsten hat sich nichts Wesentliches geändert. Eine GROUP BY-Klausel ist weiterhin nicht erforderlich.

In beiden obigen Beispielen ist es jedoch erlaubt. Das ist vollkommen gültig:

select count(*), avg(age) from students
  where sex = 'M'
  group by class;

Dadurch wird für jede Klasse mit Jungen eine Zeile zurückgegeben, in der die Anzahl der Jungen und ihr Durchschnittsalter in dieser bestimmten Klasse aufgeführt sind. (Wenn Sie auch das Feld current_date belassen, wird dieser Wert in jeder Zeile wiederholt, was nicht sehr aufregend ist.)

Die obige Abfrage hat jedoch einen großen Nachteil: Sie gibt Ihnen Informationen über die verschiedenen Klassen, aber sie sagt Ihnen nicht, welche Zeile für welche Klasse gilt. Um diese zusätzlichen Informationen zu erhalten, muss die nicht aggregierte Spalte "CLASS" zur Auswahlliste hinzugefügt werden:

select class, count(*), avg(age) from students
  where sex = 'M'
  group by class;

Jetzt haben wir eine nützliche Abfrage. Beachten Sie, dass das Hinzufügen der Spalte CLASS auch die GROUP BY-Klausel obligatorisch macht. Wir können diese Klausel nicht mehr löschen, es sei denn, wir entfernen auch CLASS aus der Spaltenliste.

Die Ausgabe unserer letzten Abfrage kann etwa so aussehen:

CLASS COUNT AVG

2A

12

13.5

2B

9

13.9

3A

11

14.6

3B

12

14.4

…​

…​

…​

Die Überschriften “COUNT” und “AVG” sind wenig aussagekräftig. In einem einfachen Fall wie diesem kommen Sie vielleicht damit durch, aber im Allgemeinen sollten Sie Aggregatspalten einen aussagekräftigen Namen geben, indem Sie sie mit einem Alias versehen:

select class,
       count(*) as num_boys,
       avg(age) as boys_avg_age
  from students
  where sex = 'M'
  group by class;

Wie Sie sich vielleicht an der formalen Syntax der Spaltenliste erinnern, ist das Schlüsselwort AS optional.

Das Hinzufügen weiterer nicht-aggregierter (oder besser: zeilenabhängiger) Spalten erfordert auch das Hinzufügen dieser zur GROUP BY-Klausel. Zum Beispiel möchten Sie vielleicht die oben genannten Informationen auch für Mädchen sehen; und vielleicht möchten Sie auch zwischen Internats- und Tagesschülern unterscheiden:

select class,
       sex,
       boarding_type,
       count(*) as number,
       avg(age) as avg_age
  from students
  group by class, sex, boarding_type;

Dies kann zu folgendem Ergebnis führen:

CLASS SEX BOARDING_TYPE NUMBER AVG_AGE

2A

F

BOARDING

9

13.3

2A

F

DAY

6

13.5

2A

M

BOARDING

7

13.6

2A

M

DAY

5

13.4

2B

F

BOARDING

11

13.7

2B

F

DAY

5

13.7

2B

M

BOARDING

6

13.8

…​

…​

…​

…​

…​

Jede Zeile in der Ergebnismenge entspricht einer bestimmten Kombination der Spalten CLASS, SEX und BOARDING_TYPE. Die aggregierten Ergebnisse – Anzahl und Durchschnittsalter – werden für jede dieser eher spezifischen Gruppen einzeln angegeben. In einer Abfrage wie dieser sehen Sie keine Gesamtsumme für Jungen als Ganzes oder Tagesschüler als Ganzes. Das ist der Kompromiss: Je mehr nicht aggregierte Spalten Sie hinzufügen, desto mehr können Sie sehr spezifische Gruppen lokalisieren, aber desto mehr verlieren Sie auch den Überblick. Natürlich können Sie die “gröberen” Aggregate weiterhin durch separate Abfragen erhalten.

HAVING

So wie eine 'WHERE'-Klausel die Zeilen in einem Datensatz auf diejenigen beschränkt, die die Suchbedingung erfüllen, so erlegt die 'HAVING'-Unterklausel Beschränkungen für die aggregierten Zeilen in einer gruppierten Menge auf. HAVING ist optional und kann nur in Verbindung mit GROUP BY verwendet werden.

Die Bedingung(en) in der HAVING-Klausel können sich beziehen auf:

  • Jede aggregierte Spalte in der Auswahlliste. Dies ist der am häufigsten verwendete Fall.

  • Jeder aggregierte Ausdruck, der nicht in der Auswahlliste enthalten ist, aber im Kontext der Abfrage zulässig ist. Dies ist manchmal auch nützlich.

  • Jede Spalte in der GROUP BY-Liste. Obwohl es legal ist, ist es effizienter, diese nicht aggregierten Daten zu einem früheren Zeitpunkt zu filtern: in der WHERE-Klausel.

  • Jeder Ausdruck, dessen Wert nicht vom Inhalt des Datasets abhängt (wie eine Konstante oder eine Kontextvariable). Dies ist gültig, aber völlig sinnlos, da es entweder die gesamte Menge unterdrückt oder unberührt lässt, basierend auf Bedingungen, die nichts mit der Menge selbst zu tun haben.

Eine HAVING-Klausel kann nicht enthalten:

  • Nicht aggregierte Spaltenausdrücke, die nicht in der GROUP BY-Liste enthalten sind.

  • Spaltenpositionen. Eine ganze Zahl in der HAVING-Klausel ist nur eine ganze Zahl.

  • Spaltenaliase – nicht einmal, wenn sie in der GROUP BY-Klausel vorkommen!

Beispiele

Aufbauend auf unseren früheren Beispielen könnte dies verwendet werden, um kleine Schülergruppen zu überspringen:

select class,
       count(*) as num_boys,
       avg(age) as boys_avg_age
  from students
  where sex = 'M'
  group by class
  having count(*) >= 5;

So wählen Sie nur Gruppen mit einer Mindestaltersspanne aus:

select class,
       count(*) as num_boys,
       avg(age) as boys_avg_age
  from students
  where sex = 'M'
  group by class
  having max(age) - min(age) > 1.2;

Beachten Sie, dass Sie, wenn Sie wirklich an diesen Informationen interessiert sind, normalerweise min(age) und max(age) einschließen würden – oder den Ausdruck „`max(age) - min(age) `" – auch in der Auswahlliste!

Um nur 3. Klassen einzubeziehen:

select class,
       count(*) as num_boys,
       avg(age) as boys_avg_age
  from students
  where sex = 'M'
  group by class
  having class starting with '3';

Besser wäre es, diese Bedingung in die WHERE-Klausel zu verschieben:

select class,
       count(*) as num_boys,
       avg(age) as boys_avg_age
  from students
  where sex = 'M' and class starting with '3'
  group by class;

6.1.7. Die PLAN-Klausel

Die PLAN-Klausel ermöglicht es dem Benutzer, einen Datenabrufplan zu übermitteln und damit den Plan zu überschreiben, den der Optimierer automatisch generiert hätte.

Syntax
PLAN <plan-expr>

<plan-expr> ::=
    (<plan-item> [, <plan-item> ...])
  | <sorted-item>
  | <joined-item>
  | <merged-item>
  | <hash-item>

<sorted-item> ::= SORT (<plan-item>)

<joined-item> ::=
  JOIN (<plan-item>, <plan-item> [, <plan-item> ...])

<merged-item> ::=
  [SORT] MERGE (<sorted-item>, <sorted-item> [, <sorted-item> ...])

<hash-item> ::=
  HASH (<plan-item>, <plan-item> [, <plan-item> ...])

<plan-item> ::= <basic-item> | <plan-expr>

<basic-item> ::=
  <relation> { NATURAL
             | INDEX (<indexlist>)
             | ORDER index [INDEX (<indexlist>)] }

<relation> ::= table | view [table]

<indexlist> ::= index [, index ...]
Tabelle 74. Argumente für die PLAN-Klausel
Argument Beschreibung

table

Tabellenname oder sein Alias

view

Ansichtsname

index

Indexname

Jedes Mal, wenn ein Benutzer eine Abfrage an die Firebird-Engine sendet, berechnet der Optimierer eine Datenabrufstrategie. Die meisten Firebird-Clients können diesen Abrufplan für den Benutzer sichtbar machen. In Firebirds eigenem Dienstprogramm isql geschieht dies mit dem Befehl SET PLAN ON. Wenn Sie Abfragepläne untersuchen, anstatt Abfragen auszuführen, zeigt SET PLANONLY ON den Plan an, ohne die Abfrage auszuführen. Verwenden Sie SET PLANONLY OFF, um die Abfrage auszuführen und den Plan anzuzeigen.

Einen detaillierteren Plan erhalten Sie, wenn Sie einen erweiterten Plan aktivieren. In isql kann dies mit SET EXPLAIN ON erfolgen. Der erweiterte Plan zeigt detailliertere Informationen über die vom Optimierer verwendeten Zugriffsmethoden an, kann jedoch nicht in die PLAN-Klausel einer Anweisung aufgenommen werden. Die Beschreibung des erweiterten Plans geht über den Rahmen dieser Sprachreferenz hinaus.

In den meisten Situationen können Sie darauf vertrauen, dass Firebird den optimalen Abfrageplan für Sie auswählt. Wenn Sie jedoch komplizierte Abfragen haben, deren Leistung nicht ausreicht, kann es sich durchaus lohnen, den Plan zu prüfen und zu prüfen, ob Sie ihn verbessern können.

Einfache Pläne

Die einfachsten Pläne bestehen nur aus einem Relationsnamen gefolgt von einer Abrufmethode. Zum Beispiel für eine unsortierte Einzeltabellenauswahl ohne WHERE-Klausel:

select * from students
  plan (students natural);

Erweiterter Plan:

Select Expression
  -> Table "STUDENTS" Full Scan

Wenn es eine WHERE- oder eine HAVING-Klausel gibt, können Sie den Index angeben, der für die Suche nach Übereinstimmungen verwendet werden soll:

select * from students
  where class = '3C'
  plan (students index (ix_stud_class));

Erweiterter Plan:

Select Expression
  -> Filter
    -> Table "STUDENTS" Access By ID
      -> Bitmap
        -> Index "IX_STUD_CLASS" Range Scan (full match)

Die Direktive INDEX wird auch für Join-Bedingungen verwendet (wird etwas später besprochen). Es kann eine durch Kommas getrennte Liste von Indizes enthalten.

ORDER gibt den Index zum Sortieren der Menge an, wenn eine ORDER BY- oder GROUP BY-Klausel vorhanden ist:

select * from students
  plan (students order pk_students)
  order by id;

Erweiterter plan:

Select Expression
  -> Table "STUDENTS" Access By ID
    -> Index "PK_STUDENTS" Full Scan

ORDER und INDEX können kombiniert werden:

select * from students
  where class >= '3'
  plan (students order pk_students index (ix_stud_class))
  order by id;

Erweiterter Plan:

Select Expression
  -> Filter
    -> Table "STUDENTS" Access By ID
      -> Index "PK_STUDENTS" Full Scan
        -> Bitmap
          -> Index "IX_STUD_CLASS" Range Scan (lower bound: 1/1)

Es ist vollkommen in Ordnung, wenn ORDER und INDEX denselben Index angeben:

select * from students
  where class >= '3'
  plan (students order ix_stud_class index (ix_stud_class))
  order by class;

Erweiterter Plan:

Select Expression
  -> Filter
    -> Table "STUDENTS" Access By ID
      -> Index "IX_STUD_CLASS" Range Scan (lower bound: 1/1)
        -> Bitmap
          -> Index "IX_STUD_CLASS" Range Scan (lower bound: 1/1)

Um Sets zu sortieren, wenn kein verwendbarer Index verfügbar ist (oder wenn Sie seine Verwendung unterdrücken möchten), lassen Sie ORDER weg und stellen Sie dem Planausdruck SORT voran:

select * from students
  plan sort (students natural)
  order by name;

Erweiterter Plan:

Select Expression
  -> Sort (record length: 128, key length: 56)
    -> Table "STUDENTS" Full Scan

Oder wenn ein Index für die Suche verwendet wird:

select * from students
  where class >= '3'
  plan sort (students index (ix_stud_class))
  order by name;

Erweiterter Plan:

elect Expression
  -> Sort (record length: 136, key length: 56)
    -> Filter
      -> Table "STUDENTS" Access By ID
        -> Bitmap
          -> Index "IX_STUD_CLASS" Range Scan (lower bound: 1/1)

Beachten Sie, dass SORT im Gegensatz zu ORDER außerhalb der Klammern steht. Dies spiegelt die Tatsache wider, dass die Datenzeilen ungeordnet abgerufen und anschließend von der Engine sortiert werden.

Geben Sie bei der Auswahl aus einer Ansicht die Ansicht und die betreffende Tabelle an. Wenn Sie beispielsweise eine Ansicht FRESHMEN haben, die nur die Erstsemester auswählt:

select * from freshmen
  plan (freshmen students natural);

Erweiterter Plan:

Select Expression
  -> Table "STUDENTS" as "FRESHMEN" Full Scan

Oder zum Beispiel:

select * from freshmen
  where id > 10
  plan sort (freshmen students index (pk_students))
  order by name desc;

Erweiterter Plan:

Select Expression
  -> Sort (record length: 144, key length: 24)
    -> Filter
      -> Table "STUDENTS" as "FRESHMEN" Access By ID
        -> Bitmap
          -> Index "PK_STUDENTS" Range Scan (lower bound: 1/1)

Wenn eine Tabelle oder Ansicht mit einem Alias versehen wurde, muss der Alias, nicht der ursprüngliche Name, in der PLAN-Klausel verwendet werden.

Zusammengesetzte Pläne

Bei einem Join können Sie den Index angeben, der für den Abgleich verwendet werden soll. Sie müssen auch die JOIN-Direktive für die beiden Streams im Plan verwenden:

select s.id, s.name, s.class, c.mentor
  from students s
  join classes c on c.name = s.class
  plan join (s natural, c index (pk_classes));

Erweiterter Plan:

Select Expression
  -> Nested Loop Join (inner)
    -> Table "STUDENTS" as "S" Full Scan
    -> Filter
      -> Table "CLASSES" as "C" Access By ID
        -> Bitmap
          -> Index "PK_CLASSES" Unique Scan

Dieselbe Verknüpfung, sortiert nach einer indizierten Spalte:

select s.id, s.name, s.class, c.mentor
  from students s
  join classes c on c.name = s.class
  plan join (s order pk_students, c index (pk_classes))
  order by s.id;

Erweiterter Plan:

Select Expression
  -> Nested Loop Join (inner)
    -> Table "STUDENTS" as "S" Access By ID
      -> Index "PK_STUDENTS" Full Scan
    -> Filter
      -> Table "CLASSES" as "C" Access By ID
        -> Bitmap
          -> Index "PK_CLASSES" Unique Scan

Und für eine nicht indizierte Spalte:

select s.id, s.name, s.class, c.mentor
  from students s
  join classes c on c.name = s.class
  plan sort (join (s natural, c index (pk_classes)))
  order by s.name;

Erweiterter Plan:

Select Expression
  -> Sort (record length: 152, key length: 12)
    -> Nested Loop Join (inner)
      -> Table "STUDENTS" as "S" Full Scan
      -> Filter
        -> Table "CLASSES" as "C" Access By ID
          -> Bitmap
            -> Index "PK_CLASSES" Unique Scan

Mit einer hinzugefügten Suchbedingung:

select s.id, s.name, s.class, c.mentor
  from students s
  join classes c on c.name = s.class
  where s.class <= '2'
  plan sort (join (s index (fk_student_class), c index (pk_classes)))
  order by s.name;

Erweiterter Plan:

Select Expression
  -> Sort (record length: 152, key length: 12)
    -> Nested Loop Join (inner)
      -> Filter
        -> Table "STUDENTS" as "S" Access By ID
          -> Bitmap
            -> Index "FK_STUDENT_CLASS" Range Scan (lower bound: 1/1)
      -> Filter
        -> Table "CLASSES" as "C" Access By ID
          -> Bitmap
            -> Index "PK_CLASSES" Unique Scan

Als Left Outer Join:

select s.id, s.name, s.class, c.mentor
  from classes c
  left join students s on c.name = s.class
  where s.class <= '2'
  plan sort (join (c natural, s index (fk_student_class)))
  order by s.name;

Erweiterter Plan:

Select Expression
  -> Sort (record length: 192, key length: 56)
    -> Filter
      -> Nested Loop Join (outer)
        -> Table "CLASSES" as "C" Full Scan
        -> Filter
          -> Table "STUDENTS" as "S" Access By ID
            -> Bitmap
              -> Index "FK_STUDENT_CLASS" Range Scan (full match)

Wenn keine Indizes verfügbar sind, die der Join-Bedingung entsprechen (oder wenn Sie sie nicht verwenden möchten), können Sie die Streams mit der Methode HASH oder MERGE verbinden.

Um eine Verbindung mit der HASH-Methode im Plan herzustellen, wird die HASH-Direktive anstelle der JOIN-Direktive verwendet. In diesem Fall wird der kleinere (sekundäre) Strom vollständig in einem internen Puffer materialisiert. Beim Lesen dieses sekundären Streams wird eine Hash-Funktion angewendet und ein Paar {Hash, Zeiger auf Puffer} in eine Hash-Tabelle geschrieben. Dann wird der primäre Stream gelesen und sein Hash-Schlüssel wird gegen die Hash-Tabelle getestet.

select *
  from students s
  join classes c on c.cookie = s.cookie
  plan hash (c natural, s natural)

Erweiterter Plan:

Select Expression
  -> Filter
    -> Hash Join (inner)
      -> Table "STUDENTS" as "S" Full Scan
      -> Record Buffer (record length: 145)
        -> Table "CLASSES" as "C" Full Scan

Für einen 'MERGE'-Join muss der Plan zuerst beide Streams in deren Join-Spalte(n) sortieren und dann zusammenführen. Dies wird mit der SORT-Direktive (die wir bereits gesehen haben) und MERGE statt JOIN erreicht:

select * from students s
  join classes c on c.cookie = s.cookie
  plan merge (sort (c natural), sort (s natural));

Das Hinzufügen einer ORDER BY-Klausel bedeutet, dass das Ergebnis der Zusammenführung ebenfalls sortiert werden muss:

select * from students s
  join classes c on c.cookie = s.cookie
  plan sort (merge (sort (c natural), sort (s natural)))
  order by c.name, s.id;

Schließlich fügen wir eine Suchbedingung für zwei indizierbare Spalten der Tabelle STUDENTS hinzu:

select * from students s
  join classes c on c.cookie = s.cookie
  where s.id < 10 and s.class <= '2'
  plan sort (merge (sort (c natural),
                    sort (s index (pk_students, fk_student_class))))
  order by c.name, s.id;

Wie aus der formalen Syntaxdefinition hervorgeht, können JOINs und MERGEs im Plan mehr als zwei Streams kombinieren. Außerdem kann jeder Planausdruck als Planelement in einem umfassenden Plan verwendet werden. Dies bedeutet, dass Pläne bestimmter komplizierter Abfragen verschiedene Verschachtelungsebenen haben können.

Schließlich können Sie statt MERGE auch SORT MERGE schreiben. Da dies absolut keinen Unterschied macht und zu Verwirrung mit “real” SORT-Direktiven führen kann (die einen Unterschied machen), ist es wahrscheinlich am besten, beim einfachen MERGE zu bleiben.

Neben dem Plan für die Hauptabfrage können Sie für jede Unterabfrage einen Plan angeben. Die folgende Abfrage mit mehreren Plänen funktioniert beispielsweise:

select *
from color
where exists (
  select *
  from hors
  where horse.code_color = color.code_color
  plan (horse index (fk_horse_color)))
plan (color natural)

Gelegentlich akzeptiert der Optimierer einen Plan und folgt ihm dann nicht, obwohl er ihn nicht als ungültig zurückweist. Ein solches Beispiel war

MERGE (unsorted stream, unsorted stream)

Es ist ratsam, einen solchen Plan als “veraltet” zu behandeln.

6.1.8. UNION

Die UNION-Klausel verkettet zwei oder mehr Datensätze und erhöht somit die Anzahl der Zeilen, aber nicht die Anzahl der Spalten. Datensätze, die an einer UNION teilnehmen, müssen die gleiche Anzahl von Spalten haben und die Spalten an den entsprechenden Positionen müssen vom gleichen Typ sein. Abgesehen davon können sie völlig unabhängig sein.

Standardmäßig unterdrückt eine Vereinigung doppelte Zeilen. UNION ALL zeigt alle Zeilen, einschließlich aller Duplikate. Das optionale Schlüsselwort DISTINCT macht das Standardverhalten explizit.

Syntax
<union> ::=
  <individual-select>
  UNION [{DISTINCT | ALL}]
  <individual-select>
  [
    [UNION [{DISTINCT | ALL}]
    <individual-select>
    ...
  ]
  [<union-wide-clauses>]

<individual-select> ::=
  SELECT
  [TRANSACTION name]
  [FIRST m] [SKIP n]
  [{DISTINCT | ALL}] <columns>
  [INTO <host-varlist>]
  FROM <source> [[AS] alias]
  [<joins>]
  [WHERE <condition>]
  [GROUP BY <grouping-list>
  [HAVING <aggregate-condition>]]
  [PLAN <plan-expr>]

<union-wide-clauses> ::=
  [ORDER BY <ordering-list>]
  [{ ROWS <m> [TO <n>]
   | [OFFSET n {ROW | ROWS}]
     [FETCH {FIRST | NEXT} [m] {ROW | ROWS} ONLY]
  }]
  [FOR UPDATE [OF <columns>]]
  [WITH LOCK]
  [INTO <PSQL-varlist>]

Unions beziehen ihre Spaltennamen aus der ersten Auswahlabfrage. Wenn Sie Union-Spalten mit einem Alias versehen möchten, tun Sie dies in der Spaltenliste des obersten SELECT. Aliase in anderen teilnehmenden Selects sind erlaubt und können sogar nützlich sein, werden aber nicht auf Gewerkschaftsebene verbreitet.

Wenn eine Union eine ORDER BY-Klausel hat, sind die einzigen zulässigen Sortierelemente Integer-Literale, die 1-basierte Spaltenpositionen angeben, optional gefolgt von einem ASC/DESC und/oder einem NULLS {FIRST | LAST}-Anweisung. Dies impliziert auch, dass Sie eine Union nicht nach etwas sortieren können, das keine Spalte in der Union ist. (Sie können es jedoch in eine abgeleitete Tabelle einschließen, die Ihnen alle üblichen Sortieroptionen zurückgibt.)

Unions sind in Unterabfragen jeglicher Art erlaubt und können selbst Unterabfragen enthalten. Sie können auch Joins enthalten und an einem Join teilnehmen, wenn sie in eine abgeleitete Tabelle eingeschlossen sind.

Beispiele

Diese Abfrage präsentiert Informationen aus verschiedenen Musiksammlungen in einem Datensatz unter Verwendung von Unions:

select id, title, artist, length, 'CD' as medium
  from cds
union
select id, title, artist, length, 'LP'
  from records
union
select id, title, artist, length, 'MC'
  from cassettes
order by 3, 2  -- artist, title;

Wenn id, title, artist und length die einzigen beteiligten Felder in den Tabellen sind, kann die Abfrage auch so geschrieben werden:

select c.*, 'CD' as medium
  from cds c
union
select r.*, 'LP'
  from records r
union
select c.*, 'MC'
  from cassettes c
order by 3, 2  -- artist, title;

Die Qualifizierung der “Sterne” ist hier notwendig, da sie nicht das einzige Element in der Spaltenliste sind. Beachten Sie, dass die Aliase “c” in der ersten und dritten Auswahl nicht miteinander in Konflikt geraten: Ihre Gültigkeitsbereiche sind nicht unionsweit, sondern gelten nur für ihre jeweiligen select-Abfragen.

Die nächste Abfrage ruft Namen und Telefonnummern von Übersetzern und Korrektoren ab. Übersetzer, die auch als Korrektoren tätig sind, erscheinen nur einmal in der Ergebnismenge, sofern ihre Telefonnummer in beiden Tabellen gleich ist. Das gleiche Ergebnis kann ohne DISTINCT erreicht werden. Mit ALL würden diese Personen zweimal erscheinen.

select name, phone from translators
  union distinct
select name, telephone from proofreaders;

Eine UNION innerhalb einer Unterabfrage:

select name, phone, hourly_rate from clowns
where hourly_rate < all
  (select hourly_rate from jugglers
     union
   select hourly_rate from acrobats)
order by hourly_rate;

6.1.9. ORDER BY

Wenn eine SELECT-Anweisung ausgeführt wird, wird die Ergebnismenge in keiner Weise sortiert. Es kommt oft vor, dass Zeilen chronologisch sortiert erscheinen, einfach weil sie in der gleichen Reihenfolge zurückgegeben werden, in der sie durch INSERT-Anweisungen zur Tabelle hinzugefügt wurden. Darauf sollten Sie sich nicht verlassen: Die Reihenfolge kann sich je nach Plan oder Aktualisierungen von Zeilen usw. ändern. Um eine explizite Sortierreihenfolge für die Mengenangabe anzugeben, wird eine ORDER BY-Klausel verwendet.

Syntax
SELECT ... FROM ...
...
ORDER BY <ordering-item> [, <ordering-item> …]

<ordering-item> ::=
  {col-name | col-alias | col-position | <expression>}
  [COLLATE collation-name]
  [ASC[ENDING] | DESC[ENDING]]
  [NULLS {FIRST|LAST}]
Tabelle 75. Argumente für die ORDER BY-Klausel
Argument Beschreibung

col-name

Vollständiger Spaltenname

col-alias

Spaltenalias

col-position

Spaltenposition in der SELECT-Liste

expression

Beliebiger Ausdruck

collation-name

Collationsname (Sortierreihenfolge für Stringdatentypen)

Der ORDER BY besteht aus einer durch Kommas getrennten Liste der Spalten, nach denen der Ergebnisdatensatz sortiert werden soll. Die Sortierreihenfolge kann durch den Namen der Spalte angegeben werden — aber nur, wenn die Spalte zuvor nicht als Alias in der SELECT-Spaltenliste angegeben wurde. Der Alias muss verwendet werden, wenn er in der Auswahlliste verwendet wurde. Die ordinale Positionsnummer der Spalte in der SELECT-Spaltenliste, der der Spalte in der SELECT-Liste mit Hilfe des Schlüsselworts AS gegebene Alias oder die Nummer der Spalte in der SELECT-Liste kann uneingeschränkt verwendet werden.

Die drei Ausdrucksformen der Spalten für die Sortierreihenfolge können in derselben ORDER BY-Klausel gemischt werden. Beispielsweise kann eine Spalte in der Liste durch ihren Namen und eine andere Spalte durch ihre Nummer angegeben werden.

Wenn Sie nach Spaltenposition oder Alias sortieren, wird der dieser Position (Alias) entsprechende Ausdruck aus der SELECT-Liste kopiert. Dies gilt auch für Unterabfragen, daher wird die Unterabfrage mindestens zweimal ausgeführt.

Wenn Sie die Spaltenposition verwenden, um die Sortierreihenfolge für eine Abfrage des Stils SELECT * anzugeben, erweitert der Server das Sternchen auf die vollständige Spaltenliste, um die Spalten für die Sortierung zu bestimmen. Es wird jedoch als „schlechte Praxis“ angesehen, geordnete Sets auf diese Weise zu entwerfen.

Sortierrichtung

Das Schlüsselwort ASCENDING – normalerweise abgekürzt mit ASC – gibt eine Sortierrichtung von der niedrigsten zur höchsten an. ASCENDING ist die Standardsortierrichtung.

Das Schlüsselwort DESCENDING — normalerweise abgekürzt mit DESC — gibt eine Sortierrichtung von der höchsten zur niedrigsten an.

Die Angabe einer aufsteigenden Reihenfolge für eine Spalte und einer absteigenden Reihenfolge für eine andere ist zulässig.

Sortierreihenfolge

Das Schlüsselwort COLLATE gibt die Sortierreihenfolge für eine Zeichenfolgenspalte an, wenn Sie eine andere Sortierung als die normale für diese Spalte benötigen. Die normale Sortierreihenfolge ist entweder die Standardreihenfolge für den Datenbankzeichensatz oder die explizit in der Spaltendefinition festgelegte.

NULLS-Position

Das Schlüsselwort NULLS definiert, wo NULL in der zugeordneten Spalte in der Sortierreihenfolge liegt: NULLS FIRST platziert die Zeilen mit der NULL-Spalte über Zeilen geordnet nach dem Wert dieser Spalte; NULLS LAST platziert diese Zeilen nach den geordneten Zeilen.

NULLS FIRST ist die Vorgabe.

Sortierung von UNIONs

Die diskreten Abfragen, die zu einer UNION beitragen, können keine ORDER BY-Klausel annehmen. Die einzige Möglichkeit besteht darin, die gesamte Ausgabe zu sortieren, indem eine ORDER BY-Klausel am Ende der Gesamtabfrage verwendet wird.

Die einfachste — und in manchen Fällen die einzige — Methode zum Angeben der Sortierreihenfolge ist die Ordinalspaltenposition. Es ist jedoch auch zulässig, die Spaltennamen oder Aliase aus der ersten beitragenden Abfrage nur zu verwenden.

Für dieses globale Set stehen die Direktiven ASC/DESC und/oder NULLS zur Verfügung.

Wenn eine diskrete Sortierung innerhalb der beitragenden Menge erforderlich ist, kann die Verwendung abgeleiteter Tabellen oder allgemeiner Tabellenausdrücke für diese Mengen eine Lösung sein.

Beispiele für ORDER BY

Sortieren der Ergebnismenge in aufsteigender Reihenfolge, Sortierung nach den Spalten RDB$CHARACTER_SET_ID und RDB$COLLATION_ID der Tabelle RDB$COLLATIONS:

SELECT
  RDB$CHARACTER_SET_ID AS CHARSET_ID,
  RDB$COLLATION_ID AS COLL_ID,
  RDB$COLLATION_NAME AS NAME
FROM RDB$COLLATIONS
ORDER BY RDB$CHARACTER_SET_ID, RDB$COLLATION_ID;

Das gleiche, aber nach den Spaltenaliasen sortiert:

SELECT
  RDB$CHARACTER_SET_ID AS CHARSET_ID,
  RDB$COLLATION_ID AS COLL_ID,
  RDB$COLLATION_NAME AS NAME
FROM RDB$COLLATIONS
ORDER BY CHARSET_ID, COLL_ID;

Sortieren der Ausgabedaten nach den Spaltenpositionsnummern:

SELECT
  RDB$CHARACTER_SET_ID AS CHARSET_ID,
  RDB$COLLATION_ID AS COLL_ID,
  RDB$COLLATION_NAME AS NAME
FROM RDB$COLLATIONS
ORDER BY 1, 2;

Sortieren einer SELECT *-Abfrage nach Positionsnummern — möglich, aber böse und nicht empfohlen:

SELECT *
FROM RDB$COLLATIONS
ORDER BY 3, 2;

Sortieren nach der zweiten Spalte in der BOOKS-Tabelle oder — wenn BOOKS nur eine Spalte hat — der FILMS.DIRECTOR-Spalte:

SELECT
    BOOKS.*,
    FILMS.DIRECTOR
FROM BOOKS, FILMS
ORDER BY 2;

Sortieren in absteigender Reihenfolge nach den Werten der Spalte PROCESS_TIME, wobei NULLs am Anfang der Menge stehen:

SELECT *
FROM MSG
ORDER BY PROCESS_TIME DESC NULLS FIRST;

Sortieren der Menge, die durch eine UNION von zwei Abfragen erhalten wurde. Die Ergebnisse werden in absteigender Reihenfolge nach den Werten in der zweiten Spalte sortiert, mit NULLs am Ende der Menge; und in aufsteigender Reihenfolge für die Werte der ersten Spalte mit NULLs am Anfang.

SELECT
  DOC_NUMBER, DOC_DATE
FROM PAYORDER
UNION ALL
SELECT
  DOC_NUMBER, DOC_DATE
FROM BUDGORDER
ORDER BY 2 DESC NULLS LAST, 1 ASC NULLS FIRST;

6.1.10. ROWS

Verwendet für

Abrufen eines Zeilenabschnitts aus einer geordneten Menge

Verfügbar in

DSQL, PSQL

Syntax
SELECT <columns> FROM ...
  [WHERE ...]
  [ORDER BY ...]
  ROWS m [TO n]
Tabelle 76. Argumente für die ROWS-Klausel
Argument Beschreibung

m, n

Beliebige Integer-Ausdrücke

ROWS ist keine Standard-Syntax

ROWS ist eine Firebird-spezifische Klausel. Verwenden Sie nach Möglichkeit die SQL-Standardsyntax OFFSET, FETCH.

Begrenzt die Anzahl der Zeilen, die von der SELECT-Anweisung zurückgegeben werden, auf eine angegebene Anzahl oder einen bestimmten Bereich.

Die ROWS-Klausel erfüllt auch die gleiche Aufgabe wie die FIRST- und SKIP-Klauseln, ist jedoch nicht SQL-kompatibel. Im Gegensatz zu FIRST und SKIP sowie OFFSET und FETCH akzeptieren die ROWS- und TO-Klauseln jede Art von Integer-Ausdruck als Argumente ohne Klammern. Natürlich können für verschachtelte Auswertungen innerhalb des Ausdrucks immer noch Klammern benötigt werden, und eine Unterabfrage muss immer in Klammern eingeschlossen werden.

  • Die Nummerierung der Zeilen im Zwischensatz – der Gesamtsatz, der auf der Festplatte zwischengespeichert wird, bevor der „slice“ extrahiert wird – beginnt bei 1.

  • OFFSET/FETCH, FIRST/SKIP und ROWS können alle ohne die ORDER BY-Klausel verwendet werden, obwohl dies selten sinnvoll ist – außer vielleicht, wenn Sie dies tun möchten Schauen Sie sich die Tabellendaten kurz an und kümmern Sie sich nicht darum, dass die Zeilen in einer nicht deterministischen Reihenfolge vorliegen. Zu diesem Zweck würde eine Abfrage wie “SELECT * FROM TABLE1 ROWS 20” die ersten 20 Zeilen zurückgeben statt einer ganzen Tabelle, die ziemlich groß sein könnte.

Der Aufruf von ROWS m ruft die ersten m Datensätze aus der angegebenen Menge ab.

Merkmale der Verwendung von ROWS m ohne eine TO-Klausel:
  • Wenn m größer als die Gesamtzahl der Datensätze im Zwischendatensatz ist, wird der gesamte Satz zurückgegeben

  • Wenn m = 0, wird eine leere Menge zurückgegeben

  • Wenn m < 0, schlägt der Aufruf der SELECT-Anweisung mit einem Fehler fehl

Der Aufruf von ROWS m TO n ruft die Zeilen aus der Menge ab, beginnend bei Zeile m und endend nach Zeile n — die Menge ist inklusiv.

Merkmale der Verwendung von ROWS m mit einer TO-Klausel:
  • Wenn m größer als die Gesamtzahl der Zeilen in der Zwischenmenge ist und n >= m, wird eine leere Menge zurückgegeben

  • Wenn m nicht größer als n und n größer als die Gesamtzahl der Zeilen in der Zwischenmenge ist, wird die Ergebnismenge auf Zeilen beginnend mit m bis zum Ende der Menge begrenzt

  • Wenn m < 1 und n < 1 ist, schlägt der Aufruf der SELECT-Anweisung mit einem Fehler fehl

  • Wenn n = m - 1, wird eine leere Menge zurückgegeben

  • Wenn n < m - 1, schlägt der Aufruf der SELECT-Anweisung mit einem Fehler fehl

Verwenden einer TO-Klausel ohne eine ROWS-Klausel:

Während ROWS die FIRST- und SKIP-Syntax ersetzt, gibt es eine Situation, in der die ROWS-Syntax nicht das gleiche Verhalten bietet: Die Angabe von SKIP n allein gibt den gesamten Zwischensatz zurück, ohne das erste n Reihen. Die Syntax von ROWS …​ TO benötigt dazu ein wenig Hilfe.

Bei der ROWS-Syntax benötigen Sie eine ROWS-Klausel in Verbindung mit der TO-Klausel und machen das zweite Argument (n) bewusst größer als die Größe des Zwischendatensatzes. Dies wird erreicht, indem ein Ausdruck für n erstellt wird, der eine Unterabfrage verwendet, um die Anzahl der Zeilen im Zwischensatz abzurufen und 1 hinzufügt.

Ersetzen von FIRST/SKIP und OFFSET/FETCH

Die ROWS-Klausel kann anstelle der SQL-Standard-OFFSET/FETCH- oder Nicht-Standard-FIRST/SKIP-Klauseln verwendet werden, außer wenn nur OFFSET oder SKIP verwendet wird, dass ist, wenn die gesamte Ergebnismenge zurückgegeben wird, außer dass die angegebene Anzahl von Zeilen vom Anfang übersprungen wird.

Um dieses Verhalten mit ROWS zu implementieren, müssen Sie die TO-Klausel mit einem Wert angeben, der größer als die Größe der zurückgegebenen Ergebnismenge ist.

Mischen von ROWS und FIRST/SKIP oder OFFSET/FETCH

Die ROWS-Syntax kann nicht mit FIRST/SKIP oder OFFSET/FETCH im selben SELECT-Ausdruck gemischt werden. Die Verwendung der unterschiedlichen Syntaxen in verschiedenen Unterabfragen in derselben Anweisung ist zulässig.

ROWS-Syntax in UNION-Abfragen

Wenn ROWS in einer UNION-Abfrage verwendet wird, wird die ROWS-Direktive auf die vereinigte Menge angewendet und muss nach der letzten SELECT-Anweisung platziert werden.

Wenn die Notwendigkeit besteht, die von einer oder mehreren SELECT-Anweisungen innerhalb von UNION zurückgegebenen Teilmengen zu begrenzen, gibt es mehrere Optionen:

  1. Verwenden Sie die FIRST/SKIP-Syntax in diesen SELECT-Anweisungen — beachten Sie, dass eine Sortierklausel (ORDER BY) nicht lokal auf die diskreten Abfragen angewendet werden kann, sondern nur auf die kombinierte Ausgabe.

  2. Konvertieren Sie die Abfragen in abgeleitete Tabellen mit ihren eigenen ROWS-Klauseln.

Beispiele für ROWS

Die folgenden Beispiele schreiben die Beispiele um, die im Abschnitt über FIRST und SKIP verwendet wurden, früher in diesem Kapitel.

Rufen Sie die ersten zehn Namen aus der Ausgabe einer sortierten Abfrage in der Tabelle 'PEOPLE' ab:

SELECT id, name
FROM People
ORDER BY name ASC
ROWS 1 TO 10;

oder sein Äquivalent

SELECT id, name
FROM People
ORDER BY name ASC
ROWS 10;

Alle Datensätze aus der Tabelle PEOPLE zurückgeben mit Ausnahme der ersten 10 Namen:

SELECT id, name
FROM People
ORDER BY name ASC
ROWS 11 TO (SELECT COUNT(*) FROM People);

Und diese Abfrage gibt die letzten 10 Datensätze zurück (achten Sie auf die Klammern):

SELECT id, name
FROM People
ORDER BY name ASC
ROWS (SELECT COUNT(*) - 9 FROM People)
TO (SELECT COUNT(*) FROM People);

Dieser gibt die Zeilen 81-100 aus der Tabelle PEOPLE zurück:

SELECT id, name
FROM People
ORDER BY name ASC
ROWS 81 TO 100;

ROWS kann auch mit den Anweisungen UPDATE und DELETE verwendet werden.

6.1.11. OFFSET, FETCH

Verwendet für

Abrufen eines Zeilenabschnitts aus einer geordneten Menge

Verfügbar in

DSQL, PSQL

Syntax
SELECT <columns> FROM ...
  [WHERE ...]
  [ORDER BY ...]
  [OFFSET <m> {ROW | ROWS}]
  [FETCH {FIRST | NEXT} [ <n> ] { ROW | ROWS } ONLY]

<m>, <n>  ::=
    <integer-literal>
  | <query-parameter>
Tabelle 77. Argumente für die OFFSET- und FETCH-Klausel
Argument Beschreibung

integer-literal

Ganzzahlliteral

query-parameter

Platzhalter für Abfrageparameter. ? in DSQL und :paramname in PSQL

Die Klauseln OFFSET und FETCH sind ein SQL:2008-kompatibles Äquivalent für FIRST/SKIP und eine Alternative für ROWS. Die OFFSET-Klausel gibt die Anzahl der zu überspringenden Zeilen an. Die FETCH-Klausel gibt die Anzahl der abzurufenden Zeilen an.

Wenn <n> in der FETCH-Klausel weggelassen wird (zB FETCH FIRST ROW ONLY), wird eine Zeile geholt.

Die Wahl zwischen ROW oder ROWS, oder FIRST oder NEXT in den Klauseln ist nur aus ästhetischen Gründen (zB um die Abfrage lesbarer oder grammatikalisch korrekt zu machen). Technisch gesehen gibt es keinen Unterschied zwischen OFFSET 10 ROW oder OFFSET 10 ROWS, oder FETCH NEXT 10 ROWS ONLY oder FETCH FIRST 10 ROWS ONLY.

Wie bei SKIP und FIRST können OFFSET- und FETCH-Klauseln unabhängig voneinander sowohl in Top-Level- als auch in verschachtelten Abfrageausdrücken angewendet werden.

  1. Firebird unterstützt den im SQL-Standard definierten Prozentsatz FETCH nicht.

  2. Firebird unterstützt nicht das im SQL-Standard definierte FETCH …​ WITH TIES.

  3. Die Klauseln FIRST/SKIP und ROWS sind nicht standardmäßige Alternativen.

  4. Die Klauseln OFFSET und/oder FETCH können nicht mit ROWS oder FIRST/SKIP im gleichen Abfrageausdruck kombiniert werden.

  5. Ausdrücke, Spaltenreferenzen usw. sind in keiner der Klauseln zulässig.

  6. Im Gegensatz zur ROWS-Klausel stehen OFFSET und FETCH nur bei SELECT-Anweisungen zur Verfügung.

Beispiele für OFFSET und FETCH
Alle Zeilen außer den ersten 10 zurückgeben, sortiert nach Spalte COL1
SELECT *
FROM T1
ORDER BY COL1
OFFSET 10 ROWS
Geben Sie die ersten 10 Zeilen zurück, sortiert nach Spalte COL1
SELECT *
FROM T1
ORDER BY COL1
FETCH FIRST 10 ROWS ONLY
Verwenden von OFFSET- und FETCH-Klauseln in einer abgeleiteten Tabelle und in der äußeren Abfrage
SELECT *
FROM (
  SELECT *
  FROM T1
  ORDER BY COL1 DESC
  OFFSET 1 ROW
  FETCH NEXT 10 ROWS ONLY
) a
ORDER BY a.COL1
FETCH FIRST ROW ONLY

Die folgenden Beispiele schreiben die FIRST/SKIP-Beispiele und ROWS-Beispiele weiter oben in diesem Kapitel.

Rufen Sie die ersten zehn Namen aus der Ausgabe einer sortierten Abfrage in der Tabelle "PEOPLE" ab:

SELECT id, name
FROM People
ORDER BY name ASC
FETCH NEXT 10 ROWS ONLY;

Alle Datensätze aus der Tabelle PEOPLE zurückgeben mit Ausnahme der ersten 10 Namen:

SELECT id, name
FROM People
ORDER BY name ASC
OFFSET 10 ROWS;

Und diese Abfrage gibt die letzten 10 Datensätze zurück. Im Gegensatz zu FIRST/SKIP und ROWS können wir keine Ausdrücke (einschließlich Unterabfragen) verwenden. Um die letzten 10 Zeilen abzurufen, kehren Sie die Sortierung zu den ersten (letzten) 10 Zeilen um und sortieren Sie dann in der richtigen Reihenfolge.

SELECT id, name
FROM (
  SELECT id, name
  FROM People
  ORDER BY name DESC
  FETCH FIRST 10 ROWS ONLY
) a
ORDER BY name ASC;

Dieser gibt die Zeilen 81-100 aus der Tabelle PEOPLE zurück:

SELECT id, name
FROM People
ORDER BY name ASC
OFFSET 80 ROWS
FETCH NEXT 20 ROWS;
Siehe auch

FIRST, SKIP, ROWS

6.1.12. FOR UPDATE [OF]

Syntax
SELECT ... FROM single_table
  [WHERE ...]
  [FOR UPDATE [OF <column_list>]]

FOR UPDATE tut nicht das, was der Name vermuten lässt. Der einzige Effekt besteht derzeit darin, den Prefetch-Puffer zu deaktivieren.

Es wird sich wahrscheinlich in Zukunft ändern: Der Plan ist, mit FOR UPDATE markierte Cursor zu validieren, wenn sie wirklich aktualisierbar sind, und positionierte Aktualisierungen und Löschungen für als nicht aktualisierbar bewertete Cursor abzulehnen.

Die Unterklausel OF tut überhaupt nichts.

6.1.13. WITH LOCK

Verwendet für

Begrenzte pessimistische Sperrung

Verfügbar in

DSQL, PSQL

Syntax
SELECT ... FROM single_table
  [WHERE ...]
  [FOR UPDATE [OF <column_list>]]
  WITH LOCK

WITH LOCK bietet eine begrenzte explizite pessimistische Sperrfunktion für die vorsichtige Verwendung unter Bedingungen, in denen das betroffene Rowset:

  1. extrem klein (idealerweise Singleton), und

  2. präzise gesteuert durch den Anwendungscode.

Dies ist nur für Experten!

Die Notwendigkeit einer pessimistischen Sperre in Firebird ist in der Tat sehr selten und sollte gut verstanden werden, bevor die Verwendung dieser Erweiterung in Betracht gezogen wird.

Es ist wichtig, die Auswirkungen der Transaktionsisolation und anderer Transaktionsattribute zu verstehen, bevor Sie versuchen, explizite Sperren in Ihrer Anwendung zu implementieren.

Wenn die Klausel WITH LOCK erfolgreich ist, sichert sie eine Sperre für die ausgewählten Zeilen und verhindert, dass andere Transaktionen Schreibzugriff auf eine dieser Zeilen oder ihre abhängigen Zeilen erhalten, bis Ihre Transaktion beendet ist.

WITH LOCK kann nur mit einer SELECT-Anweisung der obersten Ebene für eine einzelne Tabelle verwendet werden. Es ist nicht verfügbar:

  • in einer Unterabfragespezifikation

  • für verbundene Sets

  • mit dem DISTINCT-Operator, einer GROUP BY-Klausel oder einer anderen Aggregationsoperation

  • mit einer Sicht

  • mit der Ausgabe einer wählbaren Stored Procedure

  • mit externem Tisch

  • mit einer UNION-Abfrage

Da die Engine wiederum berücksichtigt, dass jeder Datensatz unter eine explizite Sperranweisung fällt, gibt sie entweder die aktuellste festgeschriebene Datensatzversion zurück, unabhängig vom Datenbankstatus, als die Anweisung übergeben wurde, oder eine Ausnahme.

Das Warteverhalten und die Konfliktmeldung hängen von den im TPB-Block angegebenen Transaktionsparametern ab:

Tabelle 78. How TPB settings affect explicit locking
TPB-Modus Verhalten

isc_tpb_consistency

Explizite Sperren werden durch implizite oder explizite Sperren auf Tabellenebene außer Kraft gesetzt und ignoriert.

isc_tpb_concurrency + isc_tpb_nowait

Wenn ein Datensatz von einer Transaktion geändert wird, die seit dem Versuch der Transaktion, eine explizite Sperre zu starten, festgeschrieben wurde oder eine aktive Transaktion eine Änderung dieses Datensatzes durchgeführt hat, wird sofort eine Aktualisierungskonfliktausnahme ausgelöst.

isc_tpb_concurrency + isc_tpb_wait

Wenn der Datensatz von einer Transaktion geändert wird, die seit dem Versuch der Transaktion, eine explizite Sperre zu starten, festgeschrieben wurde, wird sofort eine Aktualisierungskonfliktausnahme ausgelöst.

Wenn eine aktive Transaktion das Eigentum an diesem Datensatz hält (über eine explizite Sperre oder eine normale optimistische Schreibsperre), wartet die Transaktion, die die explizite Sperre versucht, auf das Ergebnis der blockierenden Transaktion und versucht, wenn sie beendet ist, die Sperre für die noch einmal aufnehmen. Das bedeutet, dass, wenn die blockierende Transaktion eine geänderte Version dieses Datensatzes festgeschrieben hat, eine Aktualisierungskonfliktausnahme ausgelöst wird.

isc_tpb_read_committed + isc_tpb_nowait

Wenn es eine aktive Transaktion gibt, die das Eigentum an diesem Datensatz hält (über explizites Sperren oder normale Aktualisierung), wird sofort eine Aktualisierungskonfliktausnahme ausgelöst.

isc_tpb_read_committed + isc_tpb_wait

Wenn eine aktive Transaktion das Eigentum an diesem Datensatz hält (über eine explizite Sperre oder eine normale optimistische Schreibsperre), wartet die Transaktion, die die explizite Sperre versucht, auf das Ergebnis der Sperrung der Transaktion und versucht, wenn sie beendet ist, die Sperre für die wieder aufnehmen.

Ausnahmen bei Aktualisierungskonflikten können in diesem TPB-Modus niemals durch eine explizite Sperranweisung ausgelöst werden.

Verwendung mit einer FOR UPDATE-Klausel

Wenn die Unterklausel FOR UPDATE vor der Unterklausel WITH LOCK steht, werden gepufferte Abrufe unterdrückt. Somit wird die Sperre nacheinander auf jede Zeile angewendet, sobald sie abgerufen wird. Es wird dann möglich, dass eine Sperre, die auf Anforderung erfolgreich erschien, trotzdem nachträglich fehlschlägt, wenn versucht wird, eine Zeile abzurufen, die zwischenzeitlich durch eine andere Transaktion gesperrt wurde.

Alternativ kann es in Ihren Zugriffskomponenten möglich sein, die Größe des Fetch-Puffers auf 1 zu setzen. Auf diese Weise können Sie die aktuell gesperrte Zeile verarbeiten, bevor die nächste abgerufen und gesperrt wird, oder Fehler behandeln, ohne Ihre Transaktion rückgängig zu machen.

OF <column_list>

Diese optionale Unterklausel tut überhaupt nichts.

Siehe auch

FOR UPDATE [OF]

Wie die Engine mit WITH LOCK umgeht

Wenn eine UPDATE-Anweisung versucht, auf einen Datensatz zuzugreifen, der durch eine andere Transaktion gesperrt ist, löst sie je nach TPB-Modus entweder eine Aktualisierungskonfliktausnahme aus oder wartet auf den Abschluss der Sperrtransaktion. Das Engine-Verhalten ist hier dasselbe, als ob dieser Datensatz bereits durch die Sperrtransaktion geändert worden wäre.

Bei Konflikten mit pessimistischen Sperren werden keine speziellen gdscodes zurückgegeben.

Die Engine garantiert, dass alle von einer expliziten Sperranweisung zurückgegebenen Datensätze tatsächlich gesperrt sind und ob die in der WHERE-Klausel angegebenen Suchbedingungen erfüllen, solange die Suchbedingungen nicht von anderen Tabellen abhängen, über Joins, Unterabfragen usw. Es garantiert auch, dass Zeilen, die die Suchbedingungen nicht erfüllen, nicht durch die Anweisung gesperrt werden. Es kann nicht garantieren, dass es keine Zeilen gibt, die zwar die Suchbedingungen erfüllen, aber nicht gesperrt sind.

Diese Situation kann eintreten, wenn andere, parallele Transaktionen ihre Änderungen während der Ausführung der Sperranweisung festschreiben.

Die Engine sperrt Zeilen zum Abrufzeitpunkt. Dies hat wichtige Konsequenzen, wenn Sie mehrere Zeilen gleichzeitig sperren. Viele Zugriffsmethoden für Firebird-Datenbanken holen standardmäßig die Ausgabe in Paketen von einigen hundert Zeilen (“buffered fetches”). Die meisten Datenzugriffskomponenten können Ihnen die im zuletzt abgerufenen Paket enthaltenen Zeilen nicht liefern, wenn ein Fehler aufgetreten ist.

Fallstricke bei der Verwendung von WITH LOCK
  • Das Zurücksetzen eines impliziten oder expliziten Sicherungspunkts gibt Datensatzsperren frei, die unter diesem Sicherungspunkt vorgenommen wurden, benachrichtigt jedoch keine wartenden Transaktionen. Anwendungen sollten nicht von diesem Verhalten abhängen, da es in Zukunft geändert werden kann.

  • Während explizite Sperren verwendet werden können, um ungewöhnliche Aktualisierungskonfliktfehler zu verhindern und/oder zu behandeln, wird die Anzahl von Deadlock-Fehlern zunehmen, wenn Sie Ihre Sperrstrategie nicht sorgfältig entwerfen und sie rigoros kontrollieren.

  • Die meisten Anwendungen benötigen überhaupt keine expliziten Sperren. Die Hauptzwecke von expliziten Sperren sind:

    1. um eine teure Behandlung von Updatekonfliktfehlern in stark belasteten Anwendungen zu vermeiden, und

    2. um die Integrität von Objekten aufrechtzuerhalten, die einer relationalen Datenbank in einer Clusterumgebung zugeordnet sind.

    Wenn Ihre Verwendung der expliziten Sperrung nicht in eine dieser beiden Kategorien fällt, ist dies der falsche Weg, die Aufgabe in Firebird zu erledigen.

  • Explizites Sperren ist eine erweiterte Funktion; missbrauche es nicht! Während Lösungen für diese Art von Problemen für Websites mit Tausenden von gleichzeitigen Autoren oder für ERP/CRM-Systeme, die in großen Unternehmen betrieben werden, sehr wichtig sein können, müssen die meisten Anwendungsprogramme unter solchen Bedingungen nicht funktionieren.

Beispiele für die explizite Sperrung
  1. Einfach:

    SELECT * FROM DOCUMENT WHERE ID=? WITH LOCK;
  2. Mehrere Zeilen, Verarbeitung nacheinander mit SQL-Cursor:

    SELECT * FROM DOCUMENT WHERE PARENT_ID=?
      FOR UPDATE WITH LOCK;

6.1.14. INTO

Verwendet für

SELECT-Ausgabe an Variablen übergeben

Verfügbar in

PSQL

Syntax

In PSQL wird die INTO-Klausel ganz am Ende der SELECT-Anweisung platziert.

SELECT [...] <column-list>
FROM ...
[...]
[INTO <variable-list>]

<variable-list> ::= [:]psqlvar [, [:]psqlvar ...]

Der Doppelpunkt-Präfix vor lokalen Variablennamen in PSQL ist in der INTO-Klausel optional.

In PSQL-Code (Trigger, Stored Procedures und ausführbare Blöcke) können die Ergebnisse einer SELECT-Anweisung zeilenweise in lokale Variablen geladen werden. Dies ist oft die einzige Möglichkeit, überhaupt etwas mit den zurückgegebenen Werten zu tun, es sei denn, es wird ein expliziter oder impliziter Cursorname angegeben. Anzahl, Reihenfolge und Typen der Variablen müssen mit den Spalten in der Ausgabezeile übereinstimmen.

Eine “plain” SELECT-Anweisung kann in PSQL nur verwendet werden, wenn sie höchstens eine Zeile zurückgibt, d.h. wenn es sich um eine singleton select handelt. Für mehrzeilige Selects bietet PSQL das Schleifenkonstrukt FOR SELECT, das später im PSQL-Kapitel besprochen wird. PSQL unterstützt auch die DECLARE CURSOR-Anweisung, die einen benannten Cursor an eine SELECT-Anweisung bindet. Der Cursor kann dann verwendet werden, um die Ergebnismenge zu durchlaufen.

Beispiele
  1. Auswahl einiger aggregierter Werte und Übergabe an die zuvor deklarierten Variablen min_amt, avg_amt und max_amt:

    select min(amount), avg(cast(amount as float)), max(amount)
      from orders
      where artno = 372218
      into min_amt, avg_amt, max_amt;

    Der CAST dient dazu, den Durchschnitt zu einer reellen Zahl zu machen; andernfalls, da Betrag vermutlich ein ganzzahliges Feld ist, würden SQL-Regeln es auf die nächste niedrigere ganze Zahl kürzen.

  2. Ein PSQL-Trigger, der zwei Werte als 'BLOB'-Feld abruft (unter Verwendung der 'LIST()'-Funktion) und ihm 'INTO' ein drittes Feld zuweist:

    select list(name, ', ')
      from persons p
      where p.id in (new.father, new.mother)
      into new.parentnames;

6.1.15. Common Table Expressions (“WITH …​ AS …​ SELECT”)

Verfügbar in

DSQL, PSQL

Syntax
<cte-construct> ::=
  <cte-defs>
  <main-query>

<cte-defs> ::= WITH [RECURSIVE] <cte> [, <cte> ...]

<cte> ::= name [(<column-list>)] AS (<cte-stmt>)

<column-list> ::= column-alias [, column-alias ...]
Tabelle 79. Argumente für Common Table Expressions
Argument Beschreibung

cte-stmt

Jede SELECT-Anweisung, einschließlich UNION

main-query

Die SELECT-Hauptanweisung, die sich auf die in der Präambel definierten CTEs beziehen kann

name

Alias für einen Tabellenausdruck

column-alias

Alias für eine Spalte in einem Tabellenausdruck

Ein allgemeiner Tabellenausdruck oder CTE kann als virtuelle Tabelle oder Ansicht beschrieben werden, die in einer Präambel einer Hauptabfrage definiert ist und nach der Ausführung der Hauptabfrage den Gültigkeitsbereich verlässt. Die Hauptabfrage kann auf alle CTEs verweisen, die in der Präambel definiert sind, als wären es reguläre Tabellen oder Ansichten. CTEs können rekursiv, d.h. selbstreferenzierend, aber nicht verschachtelt sein.

CTE-Hinweise
  • Eine CTE-Definition kann jede zulässige SELECT-Anweisung enthalten, solange sie keine eigene “WITH…​”-Präambel hat (keine Verschachtelung).

  • CTEs, die für dieselbe Hauptabfrage definiert sind, können aufeinander verweisen, aber es sollte darauf geachtet werden, Schleifen zu vermeiden.

  • CTEs kann von überall in der Hauptabfrage referenziert werden.

  • Jeder CTE kann in der Hauptabfrage mehrfach referenziert werden, ggf. mit unterschiedlichen Aliasnamen.

  • In Klammern eingeschlossen können CTE-Konstrukte als Unterabfragen in SELECT-Anweisungen, aber auch in UPDATEs, MERGEs usw. verwendet werden.

  • In PSQL werden CTEs auch in FOR-Schleifenheadern unterstützt:

    for
      with my_rivers as (select * from rivers where owner = 'me')
        select name, length from my_rivers into :rname, :rlen
    do
    begin
      ..
    end

Wenn in Firebird 3.0.2 und früher ein CTE deklariert wird, muss es später verwendet werden: andernfalls erhalten Sie eine Fehlermeldung wie diese: “CTE "AAA" is not used in query”.

Diese Einschränkung wurde in Firebird 3.0.3 entfernt.

Beispiele
with dept_year_budget as (
  select fiscal_year,
         dept_no,
         sum(projected_budget) as budget
  from proj_dept_budget
  group by fiscal_year, dept_no
)
select d.dept_no,
       d.department,
       dyb_2008.budget as budget_08,
       dyb_2009.budget as budget_09
from department d
     left join dept_year_budget dyb_2008
       on d.dept_no = dyb_2008.dept_no
       and dyb_2008.fiscal_year = 2008
     left join dept_year_budget dyb_2009
       on d.dept_no = dyb_2009.dept_no
       and dyb_2009.fiscal_year = 2009
where exists (
  select * from proj_dept_budget b
  where d.dept_no = b.dept_no
);
Rekursive CTEs

Ein rekursiver (selbstreferenzierender) CTE ist eine UNION, die mindestens ein nicht-rekursives Element namens anchor haben muss. Das/die nicht-rekursive(n) Element(e) muss/müssen vor dem/den rekursiven Element(en) platziert werden. Rekursive Elemente sind miteinander und mit ihrem nicht-rekursiven Nachbarn durch UNION ALL-Operatoren verknüpft. Die Vereinigungen zwischen nicht-rekursiven Mitgliedern können von jedem Typ sein.

Rekursive CTEs erfordern, dass das Schlüsselwort RECURSIVE direkt nach WITH vorhanden ist. Jedes rekursive Unionsmitglied darf nur einmal auf sich selbst verweisen, und zwar in einer FROM-Klausel.

Ein großer Vorteil rekursiver CTEs besteht darin, dass sie weit weniger Speicher und CPU-Zyklen benötigen als eine entsprechende rekursive gespeicherte Prozedur.

Ausführungsmuster

Das Ausführungsmuster eines rekursiven CTE sieht wie folgt aus:

  • Die Engine beginnt mit der Ausführung von einem nicht-rekursiven Member.

  • Für jede ausgewertete Zeile beginnt es, jedes rekursive Element nacheinander auszuführen, wobei die aktuellen Werte aus der äußeren Zeile als Parameter verwendet werden.

  • Wenn die aktuell ausgeführte Instanz eines rekursiven Members keine Zeilen erzeugt, führt die Ausführung eine Schleife zurück und ruft die nächste Zeile aus der äußeren Ergebnismenge ab.

Beispiel für rekursive CTEs
WITH RECURSIVE DEPT_YEAR_BUDGET AS (
  SELECT
      FISCAL_YEAR,
      DEPT_NO,
      SUM(PROJECTED_BUDGET) BUDGET
  FROM PROJ_DEPT_BUDGET
  GROUP BY FISCAL_YEAR, DEPT_NO
),
DEPT_TREE AS (
  SELECT
      DEPT_NO,
      HEAD_DEPT,
      DEPARTMENT,
      CAST('' AS VARCHAR(255)) AS INDENT
  FROM DEPARTMENT
  WHERE HEAD_DEPT IS NULL
  UNION ALL
  SELECT
      D.DEPT_NO,
      D.HEAD_DEPT,
      D.DEPARTMENT,
      H.INDENT || ' '
  FROM DEPARTMENT D
    JOIN DEPT_TREE H ON H.HEAD_DEPT = D.DEPT_NO
)
SELECT
    D.DEPT_NO,
    D.INDENT || D.DEPARTMENT DEPARTMENT,
    DYB_2008.BUDGET AS BUDGET_08,
    DYB_2009.BUDGET AS BUDGET_09
FROM DEPT_TREE D
    LEFT JOIN DEPT_YEAR_BUDGET DYB_2008 ON
      (D.DEPT_NO = DYB_2008.DEPT_NO) AND
      (DYB_2008.FISCAL_YEAR = 2008)
    LEFT JOIN DEPT_YEAR_BUDGET DYB_2009 ON
      (D.DEPT_NO = DYB_2009.DEPT_NO) AND
      (DYB_2009.FISCAL_YEAR = 2009);

Das nächste Beispiel gibt den Stammbaum eines Pferdes zurück. Der Hauptunterschied besteht darin, dass die Rekursion in zwei Zweigen des Stammbaums gleichzeitig auftritt.

WITH RECURSIVE PEDIGREE (
  CODE_HORSE,
  CODE_FATHER,
  CODE_MOTHER,
  NAME,
  MARK,
  DEPTH)
AS (SELECT
      HORSE.CODE_HORSE,
      HORSE.CODE_FATHER,
      HORSE.CODE_MOTHER,
      HORSE.NAME,
      CAST('' AS VARCHAR(80)),
      0
    FROM
      HORSE
    WHERE
      HORSE.CODE_HORSE = :CODE_HORSE
    UNION ALL
    SELECT
      HORSE.CODE_HORSE,
      HORSE.CODE_FATHER,
      HORSE.CODE_MOTHER,
      HORSE.NAME,
      'F' || PEDIGREE.MARK,
      PEDIGREE.DEPTH + 1
    FROM
      HORSE
      JOIN PEDIGREE
        ON HORSE.CODE_HORSE = PEDIGREE.CODE_FATHER
    WHERE
      PEDIGREE.DEPTH < :MAX_DEPTH
    UNION ALL
    SELECT
      HORSE.CODE_HORSE,
      HORSE.CODE_FATHER,
      HORSE.CODE_MOTHER,
      HORSE.NAME,
      'M' || PEDIGREE.MARK,
      PEDIGREE.DEPTH + 1
    FROM
      HORSE
      JOIN PEDIGREE
        ON HORSE.CODE_HORSE = PEDIGREE.CODE_MOTHER
    WHERE
      PEDIGREE.DEPTH < :MAX_DEPTH
)
SELECT
  CODE_HORSE,
  NAME,
  MARK,
  DEPTH
FROM
  PEDIGREE
Hinweise zu rekursiven CTEs
  • Aggregate (DISTINCT, GROUP BY, HAVING) und Aggregatfunktionen (SUM, COUNT, MAX usw.) sind in rekursiven Unionselementen nicht erlaubt.

  • Eine rekursive Referenz kann nicht an einem Outer Join teilnehmen.

  • Die maximale Rekursionstiefe beträgt 1024.

6.2. INSERT

Verwendet für

Einfügen von Datenzeilen in eine Tabelle

Verfügbar in

DSQL, ESQL, PSQL

Syntax
INSERT INTO target
  {DEFAULT VALUES | [(<column_list>)] <value_source>}
  [RETURNING <returning_list> [INTO <variables>]]

<column_list> ::= colname [, colname ...]

<value_source> ::= VALUES (<value_list>) | <select_stmt>

<value_list> ::= <value> [, <value> ...]

<returning_list> ::=
  <ret_value> [[AS] ret_alias] [, <ret_value> [[AS] ret_alias] ...]

<ret_value> ::= { colname | target.colname | <value> }

<variables> ::= [:]varname [, [:]varname ...]
Tabelle 80. Arguments for the INSERT-Anweisungsparameter
Argument Beschreibung

target

Der Name der Tabelle oder Ansicht, zu der eine neue Zeile oder ein Zeilenstapel hinzugefügt werden soll

colname

Spalte in der Tabelle oder Ansicht

value

Ein Ausdruck, dessen Wert zum Einfügen in die Tabelle oder zum Zurückgeben verwendet wird

ret_value

Der in der RETURNING-Klausel zurückzugebende Ausdruck

varname

Name einer lokalen PSQL-Variablen

Die INSERT-Anweisung wird verwendet, um einer Tabelle oder einer oder mehreren Tabellen, die einer Ansicht zugrunde liegen, Zeilen hinzuzufügen:

  • Wenn die Spaltenwerte in einer VALUES-Klausel übergeben werden, wird genau eine Zeile eingefügt

  • Die Werte können stattdessen durch einen SELECT-Ausdruck bereitgestellt werden, in diesem Fall können null bis viele Zeilen eingefügt werden

  • Bei der DEFAULT VALUES-Klausel werden überhaupt keine Werte angegeben und genau eine Zeile eingefügt.

Einschränkungen
  • Spalten, die an die Kontextvariablen NEW.column_name in Triggern zurückgegeben werden, sollten keinen Doppelpunkt (“:”) vor ihrem Namen haben

  • In der Spaltenliste darf keine Spalte mehr als einmal vorkommen.

ALERT : BEFORE INSERT-Triggers

Achten Sie unabhängig von der zum Einfügen von Zeilen verwendeten Methode auf alle Spalten in der Zieltabelle oder -ansicht, die von BEFORE INSERT-Triggern gefüllt werden, wie z. B. Primärschlüssel und Suchspalten, bei denen die Groß-/Kleinschreibung nicht beachtet wird. Diese Spalten sollten sowohl aus der column_list als auch aus der VALUES-Liste ausgeschlossen werden, wenn die Trigger den NEW.column_name wie gewünscht auf NULL testen.

6.2.1. INSERT …​ VALUES

Die VALUES-Liste muss für jede Spalte in der Spaltenliste einen Wert in der gleichen Reihenfolge und vom richtigen Typ liefern. Die Spaltenliste muss nicht jede Spalte im Ziel angeben, aber wenn die Spaltenliste nicht vorhanden ist, benötigt die Engine einen Wert für jede Spalte in der Tabelle oder Ansicht (ohne berechnete Spalten).

Einführungssyntax bietet eine Möglichkeit, den Zeichensatz eines Werts zu identifizieren, der eine Zeichenfolgenkonstante (Literal) ist. Die Introducer-Syntax funktioniert nur mit Literal-Strings: Sie kann nicht auf String-Variablen, Parameter, Spaltenreferenzen oder Werte angewendet werden, die Ausdrücke sind.

Beispiele
INSERT INTO cars (make, model, year)
VALUES ('Ford', 'T', 1908);

INSERT INTO cars
VALUES ('Ford', 'T', 1908, 'USA', 850);

-- notice the '_' prefix (introducer syntax)
INSERT INTO People
VALUES (_ISO8859_1 'Hans-Jörg Schäfer');

6.2.2. INSERT …​ SELECT

Bei dieser Einfügemethode müssen die Ausgabespalten der SELECT-Anweisung für jede Zielspalte in der Spaltenliste einen Wert in der gleichen Reihenfolge und vom richtigen Typ liefern.

Literale Werte, Kontextvariablen oder Ausdrücke kompatiblen Typs können für jede Spalte in der Quellzeile ersetzt werden. In diesem Fall werden eine Quellspaltenliste und eine entsprechende VALUES-Liste benötigt.

Wenn die Spaltenliste fehlt – wie bei der Verwendung von SELECT * für den Quellausdruck – muss die column_list die Namen jeder Spalte in der Zieltabelle oder Sicht enthalten (berechnete Spalten ausgeschlossen).

Beispiele
INSERT IN