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:
-
Nehmen Sie an den Mailinglisten teil (siehe https://www.firebirdsql.org/en/mailing-lists/)
-
Melden Sie Fehler oder senden Sie Pull-Anfragen auf GitHub (https://github.com/FirebirdSQL/)
-
Werden Sie Entwickler (für Dokumentationen kontaktieren Sie uns unter firebird-docs, für Firebird im Allgemeinen verwenden Sie die Firebird-Entwickler-Mailingliste)
-
Spenden Sie an die Firebird Foundation (siehe https://www.firebirdsql.org/en/donate/)
-
Werden Sie zahlendes Mitglied oder Sponsor der Firebird Foundation (siehe https://www.firebirdsql.org/en/firebird-foundation/)
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 DatentypTIMESTAMP
ist ebenfalls verfügbar, der mit dieserDATE
-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
undDECIMAL
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
undNUMERIC
) 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.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 einerSELECT
-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 . DieORDER 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, dieDISTINCT
-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
<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
<delimited name> ::= "<permitted_character>[<permitted_character> ...]"
Ein durch Trennzeichen getrennter Bezeichner wie |
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.
<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.
<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.
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 mitALTER TABLE
ändern -
deklarieren oder ändern Sie eine Domäne mit den Anweisungen
CREATE DOMAIN
oderALTER 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
Name | Größe | Präzision & Grenzen | Beschreibung |
---|---|---|---|
|
64 Bits |
Von -263 bis (263 - 1) |
Nur in Dialekt 3 verfügbar |
|
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. |
|
8 Bits |
false, true, unknown |
Boolean-Datentyp |
|
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. |
|
32 Bits |
von 01.01.0001 AD bis 31.12.9999 AD |
|
|
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: |
|
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. |
|
32 bits |
1.175 * 10-38 bis 3.402 * 1038 |
Einfache Präzision nach IEEE, ~7 Stellen |
|
32 Bits |
-2.147.483.648 up to 2.147.483.647 |
Ganzzahlen mit Vorzeichen |
|
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: |
|
16 Bits |
-32.768 bis 32.767 |
Ganzzahlen mit Vorzeichen (word) |
|
32 Bits |
0:00 to 23:59:59.9999 |
|
|
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 |
|
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
-BeispieleCREATE 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
-BeispieleCREATE 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.
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 INTEGER
s 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.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.
Skalierung | Datentyp | Dialekt 1 | Dialekt 3 |
---|---|---|---|
1 - 4 |
|
|
|
1 - 4 |
|
|
|
5 - 9 |
|
|
|
10 - 18 |
|
|
|
3.3.1. NUMERIC
NUMERIC | NUMERIC(precision) | NUMERIC(precision, scale)
Parameter | Beschreibung |
---|---|
precision |
Präzision zwischen 1 und 18. Standardmäßig auf 9. |
scale |
Skala, zwischen 0 und scale. Standardmäßig auf 0. |
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 |
3.3.2. DECIMAL
DECIMAL | DECIMAL(precision) | DECIMAL(precision, scale)
Parameter | Beschreibung |
---|---|
precision |
Präzision zwischen 1 und 18. Standardmäßig auf 9. |
scale |
Skala, zwischen 0 und scale. Standardmäßig auf 0. |
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 |
-
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
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 |
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 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 |
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.
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.
Operand 1 | Operation | Operand 2 | Ergebnis |
---|---|---|---|
|
|
|
|
|
|
Numerischer Wert |
|
|
|
|
|
|
|
Numerischer Wert |
|
|
+ |
Numerischer Wert |
|
|
- |
|
Anzahl der vergangenen Tage innerhalb des Bereichs |
|
- |
Numerischer Wert |
|
|
- |
|
Anzahl der vergangenen Sekunden, innerhalb des Bereichs |
|
- |
Numerischer Wert |
|
|
- |
|
Anzahl der Tage und der Tageszeit, innerhalb des Bereichs |
|
- |
Numerischer Wert |
|
Hinweise
Der Typ |
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 |
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
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.
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
-Klausel in der Spaltendefinition angegeben werden.collation
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
UTF8
-Sortierreihenfolgen
Die folgende Tabelle zeigt die möglichen Sortierfolgen für den Zeichensatz UTF8
.
Kollation | Eigenschaften |
---|---|
|
Die Sortierung funktioniert nach der Position des Zeichens in der Tabelle (binär). In Firebird 2.0 hinzugefügt |
|
Die Sortierung funktioniert nach dem UCA-Algorithmus (Unicode Collation Algorithm) (alphabetisch). In Firebird 2.0 hinzugefügt |
|
Die standardmäßige, binäre Sortierung, identisch mit |
|
Sortierung ohne Berücksichtigung der Groß-/Kleinschreibung, funktioniert ohne Berücksichtigung der Groß-/Kleinschreibung. Hinzugefügt in Firebird 2.1 |
|
Groß-/Kleinschreibung, akzentunabhängige Sortierung, arbeitet alphabetisch ohne Berücksichtigung von Groß-/Kleinschreibung oder Akzenten. Hinzugefügt in Firebird 2.5 |
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.
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.
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. |
CREATE DATABASE
, Sortierreihenfolge, SELECT
, WHERE
, GROUP BY
, ORDER BY
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.
{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 |
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.
{VARCHAR | {CHAR | CHARACTER} VARYING} (length) [CHARACTER SET <set>] [COLLATE <name>]
Formal ist die |
NCHAR
NCHAR
ist ein Zeichendatentyp fester Länge mit dem vordefinierten Zeichensatz ISO8859_1
.
Ansonsten ist es dasselbe wie CHAR
.
{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
.
|
BOOLEAN
-Beispiele
-
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>
-
Test auf Wert
TRUE
SELECT * FROM TBOOL WHERE BVAL; ID BVAL ============ ======= 1 <true>
-
Test auf Wert
FALSE
SELECT * FROM TBOOL WHERE BVAL IS FALSE; ID BVAL ============ ======= 2 <false>
-
Test auf Wert
UNKNOWN
SELECT * FROM TBOOL WHERE BVAL IS UNKNOWN; ID BVAL ============ ======= 3 <null>
-
Boolean-Werte in
SELECT
-AnweisungSELECT ID, BVAL, BVAL AND ID < 2 FROM TBOOL; ID BVAL ============ ======= ======= 1 <true> <true> 2 <false> <false> 3 <null> <false>
-
PSQL-Deklaration mit Startwert
DECLARE VARIABLE VAR1 BOOLEAN = TRUE;
-
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
|
3.7. Binärdatentypen
BLOB
s (Binary Large Objects) sind komplexe Strukturen, die verwendet werden, um Text und binäre Daten undefinierter Länge, oft sehr groß, zu speichern.
BLOB [SUB_TYPE <subtype>] [SEGMENT SIZE <segment size>] [CHARACTER SET <character set>] [COLLATE <collation name>]
BLOB (<segment size>) BLOB (<segment size>, <subtype>) BLOB (, <subtype>)
Formal ist die |
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 istBINARY
. 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 BeispielBLOB 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. EinCHARACTER 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 eineCOLLATE
-Klausel unterstützt.Die Angabe eines
CHARACTER SET
ohneSUB_TYPE
impliziertSUB_TYPE TEXT
.
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
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 BLOB
s 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.
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) |
|
|
|
|
|
|
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 imBLOB
-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 denBLOB
-Datensatz geschrieben werden. Dies ist ein Level-2-BLOB. -
Level höher als 2 werden nicht unterstützt.
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.
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
auf0
(fürNOT NULL
)Zweiter Parameter (
NULL
Test): setzesqldata
aufnull
(Nullzeiger, nicht SQLNULL
) und*sqlind
auf0
(fürNOT NULL
) - Der Benutzer hat das Feld leer gelassen
-
Beide Parameter: setze
sqldata
aufnull
(Nullzeiger, nicht SQLNULL
) und*sqlind
auf-1
(zeigtNULL
)
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.
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.
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
Von Datentyp | Zu Datentyp |
---|---|
Numerische Typen |
Numerische Typen, |
|
|
|
|
|
|
|
|
|
|
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 |
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 | . | : | , | - | /
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 |
Literal |
Beschreibung |
Datentyp |
|
---|---|---|---|
Dialekt 1 |
Dialekt 3 |
||
|
Aktuelles Datum und Zeit |
|
|
|
Aktuelles Datum |
|
|
|
Aktualles Datum + 1 (Tag) |
|
|
|
Aktualles Datum - 1 (Tag) |
|
|
Die Verwendung der vollständigen Jahresangabe in vierstelliger Form — |
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.
<data_type> 'date_literal_string'
-- 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 Wenn die Zeit bei jeder Ausführung ausgewertet werden soll, verwenden Sie die vollständige
Firebird 4 lässt diese impliziten Datetime-Werte wie |
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.
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)
CREATE DOMAIN BOOL3 AS SMALLINT
CHECK (VALUE IS NULL OR VALUE IN (0, 1));
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.
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 |
|
|
Ja |
Um der Prüfung neue Bedingungen hinzuzufügen, können Sie die entsprechenden |
|
Nein |
Oft ist es besser, die Domain in ihrer Definition nullbar zu lassen und zu entscheiden, ob sie auf |
3.10.3. Erstellen und Verwalten von Domains
Eine Domain wird mit der DDL-Anweisung CREATE DOMAIN
erstellt.
CREATE DOMAIN name [AS] <type> [DEFAULT {<const> | <literal> | NULL | <context_var>}] [NOT NULL] [CHECK (<condition>)] [COLLATE <collation>]
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
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>}]
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 |
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.
DROP DOMAIN name
Jeder mit der Datenbank verbundene Benutzer kann eine Domäne löschen. |
DROP DOMAIN Test_Domain
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.
<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)]
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.
<blob_datatype> ::= BLOB [SUB_TYPE {subtype_num | subtype_name}] [SEGMENT SIZE seglen] [CHARACTER SET charset] | BLOB [(seglen [, subtype_num])]
Argument | Beschreibung |
---|---|
charset |
Zeichensatz (wird für andere Untertypen als |
subtype_num |
|
subtype_name |
mnemonischer Name des 'BLOB'-Untertyps;
dies kann |
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
<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 ...] ']'
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.
- 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
sowieOR
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
undIS [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ädikateEXISTS
,SINGULAR
,ALL
,ANY
undSOME
können nur mit Unterabfragen verwendet werden. - Konstante oder Litaral
-
In Apostrophen eingeschlossene Zahlen oder String-Literale, Boolesche Werte
TRUE
,FALSE
undUNKOWN
, `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.
|
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.
{x|X}'<hexstring>' <hexstring> ::= eine gerade Anzahl von <hexdigit> <hexdigit> ::= eines aus 0..9, A..F, a..f
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.
<alternative string literal> ::= { q | Q } <quote> <start char> [<char> ...] <end char> <quote>
Regeln
Wenn Innerhalb des Strings, d. h. |
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 |
---|---|
|
|
|
|
|
`NUMERIC(18, n)`wobei n von der Anzahl der Nachkommastellen abhängt |
|
|
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
.
0{x|X}<hexdigits> <hexdigits> ::= 1-16 of <hexdigit> <hexdigit> ::= one of 0..9, A..F, a..f
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
-
Hex-Nummern im Bereich 0 .. 7FFF FFFF sind positive
INTEGER
mit Dezimalwerten zwischen 0 .. 2147483647. Um eine Zahl alsBIGINT
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 alsINTEGER
ausgewertet wird. Wenn Sie jedoch eine positive Ganzzahl innerhalb des 16-Bit-Bereichs 0x0000 (Dezimal-Null) bis 0x7FFF (Dezimalzahl 32767) schreiben, wird sie transparent inSMALLINT
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.
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.
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.
SELECT LAST_NAME || ', ' || FIRST_NAME AS FULL_NAME
FROM EMPLOYEE
Arithmetische Operatoren
Operator | Zweck | Vorrang |
---|---|---|
|
unäres Plus |
1 |
|
unäres Minus |
1 |
|
Multiplikation |
2 |
|
Division |
2 |
|
Addition |
3 |
|
Subtraktion |
3 |
UPDATE T
SET A = 4 + 1/(B-C)*D
Wenn Operatoren den gleichen Vorrang besitzen, werden diese von links nach rechts ausgewertet. |
Vergleichsoperatoren
Operator | Zweck | Priorität |
---|---|---|
|
Überprüft, ob der Ausdruck auf der linken Seite (nicht) |
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.
IF (SALARY > 1400) THEN
…
Logische Operatoren
Operator | Zweck | Priorität |
---|---|---|
|
Negierung eines Suchkriteriums |
1 |
|
Kombiniert zwei oder mehr Prädikate, wobei jedes als wahr angesehen werden muss, damit der Gesamtausdruck ebenfalls als wahr aufgelöst wird |
2 |
|
Kombiniert zwei oder mehr Prädikate, wobei mindestens eines als wahr angesehen werden muss, damit der Gesamtausdruck ebenfalls als wahr aufgelöst wird |
3 |
IF (A < B OR (A > C AND A > D) AND NOT (C = D)) THEN …
NEXT VALUE FOR
DSQL, PSQL
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 |
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
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
… 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.
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
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.
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.
|
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 |
(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
- undHAVING
-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.
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. |
-
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
-
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.
-
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;
-
Abrufen von Informationen über alle Punktmatrixdrucker, die weniger als $300 kosten:
SELECT * FROM Printer WHERE ptrtype = 'matrix' AND price < 300;
-
Die folgende Abfrage gibt keine Daten zurück, auch nicht wenn es Drucker ohne zugewiesenen Typ gibt, da ein Prädikat, das
NULL
mitNULL
vergleicht,NULL
zurückgibt:SELECT * FROM Printer WHERE ptrtype = NULL AND price < 300;
Andererseits kann
ptrtype
aufNULL
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 |
Andere Vergleichsprädikate
Andere Vergleichsprädikate werden durch Schlüsselwörter gekennzeichnet.
BETWEEN
DSQL, PSQL, ESQL
<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.
SELECT *
FROM EMPLOYEE
WHERE HIRE_DATE BETWEEN date '1992-01-01' AND CURRENT_DATE
LIKE
DSQL, PSQL, ESQL
<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.
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.
ESCAPE
-Zeichen-OptionWenn 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.
LIKE
-
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.
ÜberLIKE
und den OptimizerEigentlich verwendet das
LIKE
-Prädikat keinen Index. Wird das Prädikat jedoch in Form vonLIKE 'string%'
verwendet, wird dieses zum PrädikatSTARTING WITH
konvertiert, welches einen Index verwendet.Somit gilt — wenn Sie nach einem Wortanfang suchen, sollten Sie das Prädikat
STARTING WITH
anstelle vonLIKE
verwenden. -
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'
-
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 dasCONTAINING
-Prädikat, anstelle desLIKE
-Prädikates, verwenden. -
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
DSQL, PSQL, ESQL
<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.
Suche nach Mitarbeitern deren Namen mit “Jo” beginnen:
SELECT LAST_NAME, FIRST_NAME
FROM EMPLOYEE
WHERE LAST_NAME STARTING WITH 'Jo'
CONTAINING
DSQL, PSQL, ESQL
<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.
-
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.
-
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;
SIMILAR TO
DSQL, PSQL
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
.
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
Dieser Abschnitt behandelt die Elemente und Regeln zum Aufbau Regulärer Ausdrücke in SQL.
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
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.
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
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.
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,}
.
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
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
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
DSQL, PSQL
<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.
Operandenwerte |
Ergebnis verschiedener Prädikate |
|||
---|---|---|---|---|
|
|
|
|
|
Gleiche Werte |
|
|
|
|
Verschiedene Werte |
|
|
|
|
Beide |
|
|
|
|
Einer |
|
|
|
|
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]
DSQL, PSQL
<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 |
-- 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>
IS [NOT] NULL
DSQL, PSQL, ESQL
<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).
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 |
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
DSQL, PSQL, ESQL
[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 |
-
Finde die Mitarbeiter, die Projekte haben.
SELECT * FROM employee WHERE EXISTS(SELECT * FROM employee_project ep WHERE ep.emp_no = employee.emp_no)
-
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
DSQL, PSQL, ESQL
<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:
-
wenn der geprüfte Wert
NULL
ist und dieIN ()
-Liste nicht leer ist -
wenn der geprüfte Wert keinen Treffer in der
IN ()
-Liste enthält und mindestens ein ElementNULL
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 |
-
Finde Mitarbeiter mit den Namen “Pete”, “Ann” und “Roger”:
SELECT * FROM EMPLOYEE WHERE FIRST_NAME IN ('Pete', 'Ann', 'Roger');
-
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');
SINGULAR
DSQL, PSQL, ESQL
[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
.
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
DSQL, PSQL, ESQL
<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.
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
DSQL, PSQL, ESQL
<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.
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
Erstellen einer neuen Datenbank
DSQL, ESQL
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. |
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 |
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 |
service |
Dienstname.
Muss mit dem Parameterwert von RemoteServiceName in der Datei |
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 |
password |
Passwort des Benutzers als Datenbankbesitzer.
Bei Verwendung des Authentifizierungs-Plugins |
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 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
undPASSWORD
-
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 UmgebungsvariablenISC_USER
undISC_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 (normalerweiseRDB$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 WertLENGTH
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
undBLOB SUB_TYPE TEXT
verwendet. Standardmäßig wird der ZeichensatzNONE
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 unterALTER 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:
-
Benutzer mit dem Privileg `CREATE DATABASE`BASE
Beispiele für die Verwendung von CREATE DATABASE
-
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;
-
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 mitUNICODE_CI_AI
als Standardsortierung.CREATE DATABASE '/home/firebird/test.fdb' USER 'wizard' PASSWORD 'player' DEFAULT CHARACTER SET UTF8 COLLATION UNICODE_CI_AI;
-
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 verwendetUTF8
als Standardzeichensatz.CREATE DATABASE 'baseserver:test' USER 'wizard' PASSWORD 'player' DEFAULT CHARACTER SET UTF8;
-
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ärdateitest.fdb2
zuzuordnen. Wenn auch diese Datei maximal gefüllt ist, wirdtest.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 einLENGTH
-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;
-
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
Ändern der Dateiorganisation einer Datenbank, Umschalten ihres "kopiersicheren" Zustands, Verwalten der Verschlüsselung und anderer datenbankweiter Konfigurationen
DSQL, ESQL — eingeschränkter Funktionsumfang
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:
Mehrfaches Vorkommen von add_sec_clause ( |
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)
|
Wer kann die Datenbank ändern?
Die ALTER DATABASE
-Anweisung kann ausgeführt werden durch:
-
Benutzer mit dem
ALTER DATABASE
-Privileg
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
undEND 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
undDROP LINGER
können in einer einzigen Anweisung kombiniert werden, aber die letzte Klausel "gewinnt". Zum Beispiel setztALTER DATABASE SET LINGER TO 5 DROP LINGER
die linger-Verzögerung auf 0 (kein linger), währendALTER 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 vonSET 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 derLINGER
-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
undSET 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
-
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;
-
Angabe von Pfad und Name der Delta-Datei:
ALTER DATABASE ADD DIFFERENCE FILE 'D:\test.diff';
-
Löschen der Beschreibung der Delta-Datei:
ALTER DATABASE DROP DIFFERENCE FILE;
-
Umschalten der Datenbank in den “kopiersicher” Modus:
ALTER DATABASE BEGIN BACKUP;
-
Zurückschalten der Datenbank vom “copy-safe”-Modus in den normalen Betriebsmodus:
ALTER DATABASE END BACKUP;
-
Ändern des Standardzeichensatzes für eine Datenbank in
WIN1251
ALTER DATABASE SET DEFAULT CHARACTER SET WIN1252;
-
Einstellen einer linger-verzögerung von 30 Sekunden
ALTER DATABASE SET LINGER TO 30;
-
Verschlüsseln der Datenbank mit einem Plugin namens
DbCrypt
ALTER DATABASE ENCRYPT WITH DbCrypt;
-
Entschlüsseln der Datenbank
ALTER DATABASE DECRYPT;
5.1.3. DROP DATABASE
Löschen der Datenbank, mit der Sie gerade verbunden sind
DSQL, ESQL
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 |
Wer kann eine Datenbank löschen?
Die DROP DATABASE
-Anweisung kann ausgeführt werden durch:
-
Benutzer mit dem Privileg `DROP DATABASE`BASE
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
Schatten für die aktuelle Datenbank erstellen
DSQL, ESQL
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]]]
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 |
Wer kann einen Schatten erstellen
Die CREATE SHADOW
-Anweisung kann ausgeführt werden durch:
-
Benutzer mit dem
ALTER DATABASE
-Privileg
Beispiele für die Verwendung von CREATE SHADOW
-
Erstellen eines Schattens für die aktuelle Datenbank als “Schattennummer 1”:
CREATE SHADOW 1 'g:\data\test.shd';
-
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
Löschen eines Schattens aus der aktuellen Datenbank
DSQL, ESQL
DROP SHADOW sh_num [{DELETE | PRESERVE} FILE]
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:
-
Benutzer mit dem
ALTER DATABASE
-Privileg
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
Erstellen einer neuen Domain
DSQL, ESQL
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 !!
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_list |
Eine |
select_expr |
Eine |
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 TypenCHAR
,VARCHAR
undBLOB
(SUB_TYPE TEXT
) angeben. Wenn der Zeichensatz nicht angegeben ist, wird der alsDEFAULT CHARACTER SET
angegebene Zeichensatz der Datenbank verwendet. Wenn kein Zeichensatz angegeben wurde, wird beim Erstellen einer Zeichendomäne standardmäßig der ZeichensatzNONE
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 dieINSERT
-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, alsNULL
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
undDEFAULT 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 ErgebnisseTRUE
,FALSE
undUNKNOWN
zurückgeben kann. Eine Bedingung gilt als erfüllt, wenn das Prädikat den WertTRUE
oder „unknown value
“ (entsprichtNULL
) zurückgibt. Liefert das PrädikatFALSE
, 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 derCHECK
-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ßlichBLOB
s 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:
-
Benutzer mit der Berechtigung
CREATE DOMAIN
CREATE DOMAIN
-Beispiele
-
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);
-
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'));
-
Erstellen einer Domain mit dem Zeichensatz
UTF8
und der KollatierungssequenzUNICODE_CI_AI
.CREATE DOMAIN FIRSTNAME AS VARCHAR(30) CHARACTER SET UTF8 COLLATE UNICODE_CI_AI;
-
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;
-
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.
-
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
Die aktuellen Attribute einer Domain ändern oder umbenennen
DSQL, ESQL
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 !!
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, alsNULL
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 eineCHECK
-Beschränkung hinzuzufügen. Wenn die Domain bereits eineCHECK
-Beschränkung hat, muss sie zuerst mit einerALTER DOMAIN
-Anweisung gelöscht werden, die eineDROP 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:
-
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
-
Ändern des Datentyps auf
INTEGER
und Einstellen oder Ändern des Standardwerts auf 2.000:ALTER DOMAIN CUSTNO TYPE INTEGER SET DEFAULT 2000;
-
Domainnamen ändern
ALTER DOMAIN D_BOOLEAN TO D_BOOL;
-
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');
-
Ä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');
-
Ändern des Datentyps, um die zulässige Zeichenanzahl zu erhöhen:
ALTER DOMAIN FIRSTNAME TYPE VARCHAR(50) CHARACTER SET UTF8;
-
Hinzufügen einer
NOT NULL
-Einschränkung:ALTER DOMAIN FIRSTNAME SET NOT NULL;
-
Entfernen einer
NOT NULL
-Einschränkung:ALTER DOMAIN FIRSTNAME DROP NOT NULL;
5.3.3. DROP DOMAIN
Löschen einer bestehenden Domain
DSQL, ESQL
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:
-
Der Inhaber der Domain
-
Benutzer mit dem
DROP ANY DOMAIN
-Privileg
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
Erstellen einer neuen Tabelle (Relation)
DSQL, ESQL
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 !!
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 |
collation |
Collation |
select_one |
Eine skalare |
select_list |
Eine |
select_expr |
Eine |
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 |
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 -VerhaltenDer SQL-Standard verlangt, dass Dies wird in Firebird 4 behoben, siehe auch CORE-6376. |
-
Der Datentyp einer Identitätsspalte muss ein exakter Zahlentyp mit Nullskala sein. Erlaubte Typen sind somit
SMALLINT
,INTEGER
,BIGINT
,NUMERIC(p[,0])
undDECIMAL(p[,0])
. -
Eine Identitätsspalte darf keinen
DEFAULT
- oderCOMPUTED
-Wert haben.
|
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) oderRDB$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.
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.
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 KeysDie SQL-99-kompatiblen Regeln von Firebird für UNIQUE
-Beschränkungen erlauben eine oder mehrere NULL
s 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.
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. |
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.
...
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
-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.
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)
);
Wer kann eine Tabelle erstellen
Die CREATE TABLE
-Anweisung kann ausgeführt werden durch:
-
Benutzer mit dem Privileg
CREATE TABLE
Der Benutzer, der die Anweisung CREATE TABLE
ausführt, wird Eigentümer der Tabelle.
CREATE TABLE
-Beispiele
-
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 );
-
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) );
-
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) );
-
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 );
-
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
-
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.
CREATE GLOBAL TEMPORARY TABLE tablename (<column_def> [, {<column_def> | <table_constraint>} ...]) [ON COMMIT {DELETE | PRESERVE} ROWS]
Syntax notes
|
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
undON 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:
Für einen Überblick über die Typen aller Relationen in der Datenbank:
Das Feld |
Beispiele für globale temporäre Tabellen
-
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;
-
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, daDELETE 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
|
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.
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
.
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
Ändern der Struktur einer Tabelle.
DSQL, ESQL
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!!
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 |
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.
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.
-
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
|
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
-
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.
-
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 Eine explizite |
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 |
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:
-
Der Besitzer der Tabelle
-
Benutzer mit der Berechtigung
ALTER ANY TABLE
Beispiele für die Verwendung von ALTER TABLE
-
Hinzufügen der Spalte "CAPITAL" zur Tabelle "COUNTRY".
ALTER TABLE COUNTRY ADD CAPITAL VARCHAR(25);
-
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;
-
Hinzufügen der Prüfbedingung
CHK_SALARY
und eines Fremdschlüssels zur TabelleJOB
.ALTER TABLE JOB ADD CONSTRAINT CHK_SALARY CHECK (MIN_SALARY < MAX_SALARY), ADD FOREIGN KEY (JOB_COUNTRY) REFERENCES COUNTRY (COUNTRY);
-
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;
-
Neustart der Sequenz einer Identitätsspalte.
ALTER TABLE objects ALTER ID RESTART WITH 100;
-
Ändern der berechneten Spalten
NEW_SALARY
undSALARY_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
Löschen (Löschen) einer Tabelle
DSQL, ESQL
DROP TABLE tablename
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:
-
Der Besitzer der Tabelle
-
Benutzer mit dem Privileg
DROP ANY TABLE
5.4.4. RECREATE TABLE
Erstellen einer neuen Tabelle (Relation) oder Wiederherstellen einer bestehenden Tabelle
DSQL
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.
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
Erstellen eines Index für eine Tabelle
DSQL, ESQL
CREATE [UNIQUE] [ASC[ENDING] | DESC[ENDING]] INDEX indexname ON tablename {(col [, col …]) | COMPUTED BY (<expression>)}
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 |
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
|
Wer kann einen Index erstellen?
Die CREATE INDEX
-Anweisung kann ausgeführt werden durch:
-
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 NULL
s 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.
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.
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
-
Erstellen eines Index für die Spalte
UPDATER_ID
in der TabelleSALARY_HISTORY
CREATE INDEX IDX_UPDATER ON SALARY_HISTORY (UPDATER_ID);
-
Erstellen eines Index mit in absteigender Reihenfolge sortierten Schlüsseln für die Spalte
CHANGE_DATE
in der TabelleSALARY_HISTORY
CREATE DESCENDING INDEX IDX_CHANGE ON SALARY_HISTORY (CHANGE_DATE);
-
Erstellen eines Multi-Segment-Index für die Spalten
ORDER_STATUS
,PAID
in der TabelleSALES
CREATE INDEX IDX_SALESTAT ON SALES (ORDER_STATUS, PAID);
-
Erstellen eines Index, der keine doppelten Werte für die Spalte
NAME
in der TabelleCOUNTRY
zulässtCREATE UNIQUE INDEX UNQ_COUNTRY_NAME ON COUNTRY (NAME);
-
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');
5.5.2. ALTER INDEX
Aktivieren oder Deaktivieren eines Indexes; Neuerstellung eines Index
DSQL, ESQL
ALTER INDEX indexname {ACTIVE | INACTIVE}
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 derDROP 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 AttributNOWAIT
besitzt. Wenn sich die Transaktion imWAIT
-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 beiCOMMIT
neu aufzubauen, werden andere Transaktionen, die diese Tabelle ändern, fehlschlagen oder warten, entsprechend ihrenWAIT
/NO WAIT
-Attributen. Genauso verhält es sich mitCREATE 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:
-
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.
5.5.3. DROP INDEX
Einen Index löschen (löschen)
DSQL, ESQL
DROP INDEX indexname
Parameter | Beschreibung |
---|---|
indexname |
Indexname |
Die DROP INDEX
-Anweisung löscht (löscht) den benannten Index aus der Datenbank.
Ein Einschränkungsindex kann nicht mit |
Wer kann einen Index löschen?
Die DROP INDEX
-Anweisung kann ausgeführt werden durch:
-
Der Besitzer der Tabelle
-
Benutzer mit dem
ALTER ANY TABLE
-Privileg
5.5.4. SET STATISTICS
Neuberechnung der Selektivität eines Index
DSQL, ESQL
SET STATISTICS INDEX indexname
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:
-
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 |
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.
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
Erstellen einer Ansicht
DSQL
CREATE VIEW viewname [<full_column_list>] AS <select_statement> [WITH CHECK OPTION] <full_column_list> ::= (colname [, colname ...])
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
|
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 keineORDER BY
-,GROUP BY
- oderHAVING
-Klausel -
die
SELECT
-Anweisung enthält weder das SchlüsselwortDISTINCT
noch zeilenbeschränkende Schlüsselwörter wieROWS
,FIRST
,SKIP
,OFFSET
oderFETCH
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 Darüber hinaus werden View-Felder, die in der Bei Ansichten, die nicht über |
Wer kann eine Ansicht erstellen?
Die CREATE VIEW
-Anweisung kann ausgeführt werden durch:
-
Benutzer mit dem Privileg
CREATE VIEW
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
-
Erstellen einer Ansicht, die die Spalten
JOB_CODE
undJOB_TITLE
nur für die Jobs zurückgibt, bei denenMAX_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;
-
Erstellen einer Ansicht, die die Spalten
JOB_CODE
undJOB_TITLE
nur für Jobs zurückgibt, bei denenMAX_SALARY
weniger als 15.000 USD beträgt. Immer wenn ein neuer Datensatz eingefügt oder ein vorhandener Datensatz aktualisiert wird, wird die BedingungMAX_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;
-
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;
-
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;
-
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
Ändern einer vorhandenen Ansicht
DSQL
ALTER VIEW viewname [<full_column_list>] AS <select_statement> [WITH CHECK OPTION] <full_column_list> ::= (colname [, colname ...])
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:
-
Der Besitzer der Ansicht
-
Benutzer mit der Berechtigung
ALTER ANY VIEW
5.6.3. CREATE OR ALTER VIEW
Erstellen einer neuen Ansicht oder Ändern einer vorhandenen Ansicht.
DSQL
CREATE OR ALTER VIEW viewname [<full_column_list>] AS <select_statement> [WITH CHECK OPTION] <full_column_list> ::= (colname [, colname ...])
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
.
5.6.4. DROP VIEW
Löschen einer Ansicht
DSQL
DROP VIEW viewname
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:
-
Der Besitzer der Ansicht
-
Benutzer mit dem Privileg
DROP ANY VIEW
5.6.5. RECREATE VIEW
Erstellen einer neuen Ansicht oder Neuerstellen einer vorhandenen Ansicht
DSQL
RECREATE VIEW viewname [<full_column_list>] AS <select_statement> [WITH CHECK OPTION] <full_column_list> ::= (colname [, colname ...])
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.
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:
-
ein “Datenbank-Trigger” kann angegeben werden, um zu Beginn oder am Ende einer Benutzersitzung (Verbindung) oder einer Benutzertransaktion auszulösen.
-
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
Einen neuen Trigger erstellen
DSQL, ESQL
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
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:
-
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.
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
-
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
-
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
-
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:
-
Benutzer mit dem
ALTER DATABASE
-Privileg
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.
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.
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
In einem zweiphasigen Commit-Szenario löst TRANSACTION COMMIT
das Auslösen in der Vorbereitungsphase aus, nicht beim Commit.
-
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 -
Die Ereignistrigger
DISCONNECT
undTRANSACTION 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
“
-
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
-
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 |
In gewisser Weise sind DDL-Trigger ein Untertyp von Datenbank-Triggern.
Wer kann einen DDL-Trigger erstellen?
DDL-Trigger können erstellt werden durch:
-
Benutzer mit dem
ALTER DATABASE
-Privileg
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
-
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 ;!
-
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.
-
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 ) ====================================================
ALTER TRIGGER
, CREATE OR ALTER TRIGGER
, RECREATE TRIGGER
, DROP TRIGGER
, DDL-Trigger im Kapitel Procedural SQL (PSQL)-Anweisungen
5.7.2. ALTER TRIGGER
Ändern und Deaktivieren eines bestehenden Triggers
DSQL, ESQL
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 Mehrere DML-Ereignisse – Das Schlüsselwort |
Wer kann einen Trigger ändern?
DML-Trigger können geändert werden durch:
-
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:
-
Benutzer mit dem
ALTER DATABASE
-Privileg
Beispiele mit ALTER TRIGGER
-
Den Trigger
set_cust_no
deaktivieren (in den inaktiven Zustand schalten).ALTER TRIGGER set_cust_no INACTIVE;
-
Ändern der Position der Zündreihenfolge des Triggers
set_cust_no
.ALTER TRIGGER set_cust_no POSITION 14;
-
Den Trigger
TR_CUST_LOG
in den inaktiven Zustand schalten und die Ereignisliste ändern.ALTER TRIGGER TR_CUST_LOG INACTIVE AFTER INSERT OR UPDATE;
-
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
CREATE TRIGGER
, CREATE OR ALTER TRIGGER
, RECREATE TRIGGER
, DROP TRIGGER
5.7.3. CREATE OR ALTER TRIGGER
Erstellen eines neuen Triggers oder Ändern eines bestehenden Triggers
DSQL
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.
5.7.4. DROP TRIGGER
Löschen eines vorhandenen Triggers
DSQL, ESQL
DROP TRIGGER trigname
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:
-
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:
-
Benutzer mit dem
ALTER DATABASE
-Privileg
5.7.5. RECREATE TRIGGER
Erstellen eines neuen Triggers oder Neuerstellen eines vorhandenen Triggers
DSQL
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 |
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
Erstellen einer neuen gespeicherten Prozedur
DSQL, ESQL
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 !!
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:
-
Benutzer mit dem Privileg
CREATE PROCEDURE
Der Benutzer, der die Anweisung CREATE PROCEDURE
ausführt, wird Eigentümer der Tabelle.
Beispiele
-
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
-
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
Ändern einer vorhandenen gespeicherten Prozedur
DSQL, ESQL
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 |
Wer kann ein Verfahren ändern
Die Anweisung ALTER PROCEDURE
kann ausgeführt werden durch:
-
Der Besitzer der gespeicherten Prozedur
-
Benutzer mit der Berechtigung
ALTER ANY PROCEDURE
5.8.3. CREATE OR ALTER PROCEDURE
Erstellen einer neuen gespeicherten Prozedur oder Ändern einer vorhandenen Prozedur
DSQL
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.
5.8.4. DROP PROCEDURE
Löschen einer gespeicherten Prozedur
DSQL, ESQL
DROP PROCEDURE procname
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:
-
Der Besitzer der gespeicherten Prozedur
-
Benutzer mit dem Privileg
DROP ANY PROCEDURE
5.8.5. RECREATE PROCEDURE
Erstellen einer neuen gespeicherten Prozedur oder Neuerstellen einer vorhandenen Prozedur
DSQL
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 |
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
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.
5.9.1. CREATE FUNCTION
Erstellen einer neuen gespeicherten Funktion
DSQL
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 !!
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, dassNULL
ü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 derCOLLATE
-Klausel anzugeben. Als Rückgabetyp können Sie einen Datentyp, einen Domänennamen, den Typ einer Domäne (mitTYPE OF
) oder den Typ einer Spalte einer Tabelle oder View (mitTYPE 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 Dies lässt sich leicht an einem Beispiel demonstrieren:
|
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 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:
-
Benutzer mit dem Privileg `CREATE FUNCTION`TION
Der Benutzer, der die gespeicherte Funktion erstellt hat, wird deren Eigentümer.
CREATE FUNCTION
-Beispiele
-
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);
-
Erstellen einer deterministischen gespeicherten Funktion
CREATE FUNCTION FN_E() RETURNS DOUBLE PRECISION DETERMINISTIC AS BEGIN RETURN EXP(1); END
-
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
-
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 istwait_event
.CREATE FUNCTION wait_event ( event_name varchar (31) CHARACTER SET ascii ) RETURNS INTEGER EXTERNAL NAME 'udrcpp_example!Wait_event' ENGINE udr
-
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
Ändern einer vorhandenen gespeicherten Funktion
DSQL
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 |
Wer kann eine Funktion ändern
Die ALTER FUNCTION
-Anweisung kann ausgeführt werden durch:
-
Inhaber der gespeicherten Funktion
-
Benutzer mit der Berechtigung
ALTER ANY FUNCTION
5.9.3. CREATE OR ALTER FUNCTION
Erstellen einer neuen oder Ändern einer vorhandenen gespeicherten Funktion
DSQL
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.
5.9.4. DROP FUNCTION
Löschen einer gespeicherten Funktion
DSQL
DROP FUNCTION funcname
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:
-
Inhaber der gespeicherten Funktion
-
Benutzer mit dem Privileg
DROP ANY FUNCTION
Beispiele für DROP FUNCTION
DROP FUNCTION ADD_INT;
CREATE FUNCTION
, CREATE OR ALTER FUNCTION
, RECREATE FUNCTION
5.9.5. RECREATE FUNCTION
Erstellen einer neuen gespeicherten Funktion oder Neuerstellen einer vorhandenen Funktion
DSQL
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 |
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.
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 |
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 ( |
5.10.1. DECLARE EXTERNAL FUNCTION
Deklarieren einer benutzerdefinierten Funktion (UDF) zur Datenbank
DSQL, ESQL
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 }]
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 |
Der exportierte Name der Funktion |
library_name |
Der Name des Moduls ( |
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
angegeben wird, wird bei der Übergabe von `0
’ oder eine leere Zeichenfolge (“''
”).
Wenn nach einem Parameter das Schlüsselwort `NULLNULL
-Werten der Nullzeiger an die externe Funktion übergeben.
Das Deklarieren einer Funktion mit dem Schlüsselwort |
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 KlauselBY DESCRIPTOR
angegeben ist, wird der Ausgabeparameter vom Deskriptor übergeben. Wenn die KlauselBY 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 Modulib_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 infirebird.conf
konfigurierten Speicherort befindet, erleichtert es das Verschieben der Datenbank zwischen verschiedene Plattformen. Der ParameterUDFAccess
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:
-
Benutzer mit dem Privileg
CREATE FUNCTION
Der Benutzer, der die Funktion erstellt hat, wird ihr Besitzer.
Beispiele für die Verwendung von DECLARE EXTERNAL FUNCTION
-
Deklarieren der externen Funktion
addDay
im Modulfbudf
. Die Eingabe- und Ausgabeparameter werden als Referenz übergeben.DECLARE EXTERNAL FUNCTION addDay TIMESTAMP, INT RETURNS TIMESTAMP ENTRY_POINT 'addDay' MODULE_NAME 'fbudf';
-
Deklarieren der externen Funktion
invl
im Modulfbudf
. 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';
-
Deklarieren der externen Funktion
isLeapYear
im Modulfbudf
. 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';
-
Deklarieren der externen Funktion
i64Truncate
im Modulfbudf
. 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';
ALTER EXTERNAL FUNCTION
, DROP EXTERNAL FUNCTION
, CREATE FUNCTION
5.10.2. ALTER EXTERNAL FUNCTION
Ändern des Einstiegspunkts und/oder des Modulnamens für eine benutzerdefinierte Funktion (UDF)
DSQL
ALTER EXTERNAL FUNCTION funcname [ENTRY_POINT 'new_entry_point'] [MODULE_NAME 'new_library_name']
Parameter | Beschreibung |
---|---|
funcname |
Funktionsname in der Datenbank |
new_entry_point |
Der neue exportierte Name der Funktion |
new_library_name |
Der neue Name des Moduls ( |
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:
-
Inhaber der externen Funktion
-
Benutzer mit der Berechtigung
ALTER ANY FUNCTION
5.10.3. DROP EXTERNAL FUNCTION
Entfernen einer benutzerdefinierten Funktion (UDF) aus einer Datenbank
DSQL, ESQL
DROP EXTERNAL FUNCTION funcname
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:
-
Inhaber der externen Funktion
-
Benutzer mit dem Privileg
DROP ANY FUNCTION
5.11. PACKAGE
Ein Paket ist eine Gruppe von Prozeduren und Funktionen, die als eine Einheit verwaltet werden.
5.11.1. CREATE PACKAGE
Paket-Header deklarieren
DSQL
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 !!
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:
-
Benutzer mit dem Privileg
CREATE PACKAGE
Der Benutzer, der den Paketheader erstellt hat, wird sein Besitzer.
5.11.2. ALTER PACKAGE
Ändern des Paketheaders
DSQL
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:
-
Der Besitzer des Pakets
-
Benutzer mit der Berechtigung
ALTER ANY PACKAGE
Beispiel für ALTER PACKAGE
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
CREATE PACKAGE
, DROP PACKAGE
, ALTER PACKAGE BODY
, RECREATE PACKAGE BODY
5.11.3. CREATE OR ALTER PACKAGE
Erstellen eines neuen oder Ändern eines bestehenden Paket-Headers
DSQL
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
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
Einen Paket-Header löschen
DSQL
DROP PACKAGE package_name
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:
-
Der Besitzer des Pakets
-
Benutzer mit der Berechtigung
DROP ANY PACKAGE
5.11.5. RECREATE PACKAGE
Erstellen eines neuen oder erneuten Erstellens eines vorhandenen Paketheaders
DSQL
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
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
CREATE PACKAGE
, DROP PACKAGE
, CREATE PACKAGE BODY
, RECREATE PACKAGE BODY
5.12. PACKAGE BODY
5.12.1. CREATE PACKAGE BODY
Erstellen des Paketrumpfes
DSQL
CREATE PACKAGE BODY name AS BEGIN [ <package_item> ... ] [ <package_body_item> ... ] END <package_item> ::= !! Siehe auchCREATE 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 auchCREATE PACKAGE
-Syntax !! !! Siehe auch die Regeln weiter unten !! <out_params> ::= !! Siehe auchCREATE PACKAGE
-Syntax !! <domain_or_non_array_type> ::= !! Siehe auch Syntax der Skalardatentypen !!
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 |
procedure_impl |
Verfahrensimplementierung
Im Wesentlichen eine |
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. |
-
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 ( |
Wer kann einen Paketkörper erstellen
Die Anweisung CREATE PACKAGE BODY
kann ausgeführt werden durch:
-
Der Besitzer des Pakets
-
Benutzer mit der Berechtigung
ALTER ANY PACKAGE
Beispiel für CREATE PACKAGE BODY
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
Ändern des Paketrumpfes
DSQL
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:
-
Der Besitzer des Pakets
-
Benutzer mit der Berechtigung
ALTER ANY PACKAGE
Beispiel für ALTER PACKAGE BODY
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
Löschen des Paketrumpfes
DSQL
DROP PACKAGE package_name
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:
-
Der Besitzer des Pakets
-
Benutzer mit der Berechtigung
ALTER ANY PACKAGE
5.12.4. RECREATE PACKAGE BODY
Erstellen eines neuen oder erneuten Erstellens eines vorhandenen Paketrumpfes
DSQL
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
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
CREATE PACKAGE BODY
, ALTER PACKAGE BODY
, DROP PACKAGE BODY
, ALTER PACKAGE
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
Deklarieren eines 'BLOB'-Filters für die Datenbank
DSQL, ESQL
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
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 |
sub_type |
|
number |
|
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:
Nachdem die Transaktion festgeschrieben wurde, können die mnemonischen Namen in Deklarationen verwendet werden, wenn Sie neue Filter erstellen. Der Wert der Spalte Warnung
Ab Firebird 3.0 können die Systemtabellen von Benutzern nicht mehr geschrieben werden.
Das Einfügen von benutzerdefinierten Typen in |
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
infirebird.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:
-
Benutzer mit der Berechtigung
CREATE FILTER
Der Benutzer, der die Anweisung DECLARE FILTER
ausführt, wird Eigentümer des Filters.
Beispiele für DECLARE FILTER
-
Erstellen eines 'BLOB'-Filters mit Untertypnummern.
DECLARE FILTER DESC_FILTER INPUT_TYPE 1 OUTPUT_TYPE -4 ENTRY_POINT 'desc_filter' MODULE_NAME 'FILTERLIB';
-
Erstellen eines 'BLOB'-Filters unter Verwendung von mnemonischen Subtypnamen.
DECLARE FILTER FUNNEL INPUT_TYPE blr OUTPUT_TYPE text ENTRY_POINT 'blr2asc' MODULE_NAME 'myfilterlib';
5.13.2. DROP FILTER
Entfernen einer 'BLOB'-Filterdeklaration aus der Datenbank
DSQL, ESQL
DROP FILTER filtername
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:
-
Der Besitzer des Filters
-
Benutzer mit der Berechtigung
DROP ANY 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
Erstellen einer neuen SEQUENCE
(GENERATOR
)
DSQL, ESQL
CREATE {SEQUENCE | GENERATOR} seq_name [START WITH start_value] [INCREMENT [BY] increment]
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 |
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 Das Erstellen einer Sequenz ohne eine 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 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:
-
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
-
Erstellen der Sequenz
EMP_NO_GEN
mitCREATE SEQUENCE
.CREATE SEQUENCE EMP_NO_GEN;
-
Erstellen der Sequenz
EMP_NO_GEN
mitCREATE GENERATOR
.CREATE GENERATOR EMP_NO_GEN;
-
Erstellen der Sequenz
EMP_NO_GEN
mit einem Anfangswert von 5 und einem Inkrement von 1. Siehe auch Bug mitSTART WITH
undINCREMENT [BY]
.CREATE SEQUENCE EMP_NO_GEN START WITH 5;
-
Erstellen der Sequenz
EMP_NO_GEN
mit einem Anfangswert von 1 und einem Inkrement von 10. Siehe auch Bug mitSTART WITH
undINCREMENT [BY]
.CREATE SEQUENCE EMP_NO_GEN INCREMENT BY 10;
-
Erstellen der Sequenz
EMP_NO_GEN
mit einem Anfangswert von 5 und einem Inkrement von 10. Siehe auch Bug mitSTART WITH
undINCREMENT [BY]
.CREATE SEQUENCE EMP_NO_GEN START WITH 5 INCREMENT BY 10;
5.14.2. ALTER SEQUENCE
Den Wert einer Sequenz oder eines Generators auf einen bestimmten Wert setzen
DSQL
ALTER {SEQUENCE | GENERATOR} seq_name [RESTART [WITH newvalue]] [INCREMENT [BY] increment]
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 |
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 Außerdem startet Dieser Fehler wird in Firebird 4 behoben, siehe auch CORE-6386 |
Eine falsche Verwendung der |
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 |
Wer kann eine Sequenz ändern?
Die Anweisung ALTER SEQUENCE
(ALTER GENERATOR
) kann ausgeführt werden durch:
-
Der Besitzer der Sequenz
-
Benutzer mit dem Privileg
ALTER ANY SEQUENCE
(ALTER ANY GENERATOR
)
Beispiele für ALTER SEQUENCE
-
Setzen des Werts der Sequenz
EMP_NO_GEN
auf 145.ALTER SEQUENCE EMP_NO_GEN RESTART WITH 145;
-
Zurücksetzen des Basiswerts der Sequenz
EMP_NO_GEN
auf den in den Metadaten gespeicherten InitialwertALTER SEQUENCE EMP_NO_GEN RESTART;
-
Ändern der Schrittweite der Sequenz
EMP_NO_GEN
auf 10ALTER SEQUENCE EMP_NO_GEN INCREMENT BY 10;
5.14.3. CREATE OR ALTER SEQUENCE
Erstellen einer neuen oder Ändern einer bestehenden Sequenz
DSQL, ESQL
CREATE OR ALTER {SEQUENCE | GENERATOR} seq_name {RESTART | START WITH start_value} [INCREMENT [BY] increment]
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 |
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 vonNEXT VALUE FOR
verwendet
5.14.4. DROP SEQUENCE
Löschen einer Sequenz SEQUENCE
(GENERATOR
)
DSQL, ESQL
DROP {SEQUENCE | GENERATOR} seq_name
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:
-
Der Besitzer der Sequenz
-
Benutzer mit dem Privileg
DROP ANY SEQUENCE
(DROP ANY GENERATOR
)
Beispiel für DROP SEQUENCE
EMP_NO_GEN
-Reihe:DROP SEQUENCE EMP_NO_GEN;
CREATE SEQUENCE
, CREATE OR ALTER SEQUENCE
, RECREATE SEQUENCE
5.14.5. RECREATE SEQUENCE
Sequenz erstellen oder neu erstellen (Generator)
DSQL, ESQL
RECREATE {SEQUENCE | GENERATOR} seq_name [START WITH start_value] [INCREMENT [BY] increment]
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 |
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.
5.14.6. SET GENERATOR
Den Wert einer Sequenz oder eines Generators auf einen bestimmten Wert setzen
DSQL, ESQL
SET GENERATOR seq_name TO new_val
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 |
Wer kann einen SET GENERATOR
verwenden?
Die SET GENERATOR
-Anweisung kann ausgeführt werden durch:
-
Der Besitzer der Sequenz (Generator)
-
Benutzer mit dem Privileg
ALTER ANY SEQUENCE
(ALTER ANY GENERATOR
)
Beispiel für SET GENERATOR
EMP_NO_GEN
auf 145 setzen:SET GENERATOR EMP_NO_GEN TO 145;
Das gleiche kann mit
|
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
Erstellen einer neuen Ausnahme zur Verwendung in PSQL-Modulen
DSQL, ESQL
CREATE EXCEPTION exception_name '<message>' <message> ::= <message-part> [<message-part> ...] <message-part> ::= <text> | @<slot> <slot> ::= one of 1..9
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 |
Benutzerdefinierte Ausnahmen werden in der Systemtabelle |
Wer kann eine Ausnahme erstellen
Die CREATE EXCEPTION
-Anweisung kann ausgeführt werden durch:
-
Benutzer mit dem Privileg
CREATE EXCEPTION`
Der Benutzer, der die Anweisung CREATE EXCEPTION
ausführt, wird Eigentümer der Ausnahme.
CREATE EXCEPTION
-Beispiele
E_LARGE_VALUE
CREATE EXCEPTION E_LARGE_VALUE
'The value is out of range';
E_INVALID_VALUE
CREATE EXCEPTION E_INVALID_VALUE
'Invalid value @1 for field @2';
Tips
Die Gruppierung von |
5.15.2. ALTER EXCEPTION
Ändern der von einer benutzerdefinierten Ausnahme zurückgegebenen Nachricht
DSQL, ESQL
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:
-
Der Inhaber der Ausnahme
-
Benutzer mit der Berechtigung
ALTER ANY EXCEPTION
5.15.3. CREATE OR ALTER EXCEPTION
Ändern der von einer benutzerdefinierten Ausnahme zurückgegebenen Nachricht, falls die Ausnahme vorhanden ist; andernfalls eine neue Ausnahme erstellen
DSQL
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.
5.15.4. DROP EXCEPTION
Löschen einer benutzerdefinierten Ausnahme
DSQL, ESQL
DROP EXCEPTION exception_name
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:
-
Der Inhaber der Ausnahme
-
Benutzer mit dem Privileg
DROP ANY EXCEPTION
5.15.5. RECREATE EXCEPTION
Erstellen einer neuen benutzerdefinierten Ausnahme oder Neuerstellen einer vorhandenen Ausnahme
DSQL
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.
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
Erstellen einer neuen Kollatierung für einen unterstützten Zeichensatz, der für die Datenbank verfügbar ist
DSQL
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
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 |
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”.
Atrribute | Werte | Gültig für | Hinweis |
---|---|---|---|
|
|
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 |
|
|
1 bpc |
Deaktiviert Erweiterungen. Erweiterungen bewirken, dass bestimmte Zeichen (z.B. Ligaturen oder umlaute Vokale) als Zeichenfolgen behandelt und entsprechend sortiert werden |
|
Standard oder M.m |
UNI |
Gibt die zu verwendende ICU-Bibliotheksversion an.
Gültige Werte sind diejenigen, die im entsprechenden |
|
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) |
|
|
1 bpc |
Verwendet mehr als eine Sortierebene |
|
|
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) |
|
|
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 Damit dies funktioniert, muss der Zeichensatz auf dem System vorhanden und in einer |
Wer kann eine Kollation erstellen
Die CREATE COLLATION
-Anweisung kann ausgeführt werden durch:
-
Benutzer mit dem Privileg `CREATE COLLATION`ATION
Der Benutzer, der die CREATE COLLATION
-Anweisung ausführt, wird Eigentümer der Kollation.
Beispiele zur Nutzung von CREATE COLLATION
-
Erstellen einer Kollatierung mit dem Namen aus der Datei
fbintl.conf
(Groß-/Kleinschreibung beachten)CREATE COLLATION ISO8859_1_UNICODE FOR ISO8859_1;
-
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');
-
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;
-
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';
-
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 ...);
5.16.2. DROP COLLATION
Eine Kollation aus der Datenbank entfernen
DSQL
DROP COLLATION collname
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 |
Wer kann eine Kollation abgeben
Die Drop COLLATION
-Anweisung kann ausgeführt werden durch:
-
Der Besitzer der Kollation
-
Benutzer mit dem Privileg
DROP ANY COLLATION
5.17. CHARACTER SET
5.17.1. ALTER CHARACTER SET
Festlegen der Standardsortierung für einen Zeichensatz
DSQL
ALTER CHARACTER SET charset SET DEFAULT COLLATION collation
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. |
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
Metadaten dokumentieren
DSQL
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
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.
|
Kommentare zu Benutzern sind für diesen Benutzer über die virtuelle Tabelle |
Wer kann einen Kommentar hinzufügen
Die COMMENT ON
-Anweisung kann ausgeführt werden durch:
-
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
-
Kommentar zur aktuellen Datenbank hinzufügen
COMMENT ON DATABASE IS 'It is a test (''my.fdb'') database';
-
Kommentar für die Tabelle
METALS
hinzufügenCOMMENT ON TABLE METALS IS 'Metal directory';
-
Hinzufügen eines Kommentars für das Feld
ISALLOY
in der TabelleMETALS
COMMENT ON COLUMN METALS.ISALLOY IS '0 = fine metal, 1 = alloy';
-
Kommentar zu einem Parameter hinzufügen
COMMENT ON PARAMETER ADD_EMP_PROJ.EMP_NO IS 'Employee ID';
-
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
Abfrage von Daten
DSQL, ESQL, PSQL
[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
Abrufen eines Zeilenabschnitts aus einer geordneten Menge
DSQL, PSQL
SELECT [FIRST <m>] [SKIP <n>] FROM ... ... <m>, <n> ::= <integer-literal> | <query-parameter> | (<integer-expression>)
Argument | Beschreibung |
---|---|
integer-literal |
Ganzzahlliteral |
query-parameter |
Platzhalter für Abfrageparameter.
|
integer-expression |
Ausdruck, der einen ganzzahligen Wert zurückgibt |
FIRST und SKIP sind Nicht-Standard-Syntax
|
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
undSKIP
, 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/oderFIRST
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ürFIRST
bereitgestellt wurde, wird diese kleinere Anzahl von Zeilen zurückgegeben. Dies sind gültige Ergebnisse, keine Fehlerbedingungen.
Beispiele für FIRST/SKIP
-
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
-
Die folgende Abfrage gibt alles zurück, aber die ersten 10 Namen:
select skip 10 id, name from People order by name asc
-
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
-
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
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
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.
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 ...]
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. Richtige Verwendung:
Falsche Verwendung:
|
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.
(<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
Außerdem,
|
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.
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
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:
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.
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
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:
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.
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>)
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:
ID | S |
---|---|
87 |
Just some text |
235 |
Silence |
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.
<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 SpaltenSEA
undSHIP
zweimal: einmal aus der TabelleFLOTSAM
und einmal aus der TabelleJETSAM
. 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
Jedoch:
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. |
<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 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.
<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 |
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 NULL
s 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 NULL
s 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.
SELECT ... FROM ... [...] WHERE <search-condition> [...]
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.
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
Argument | Beschreibung |
---|---|
non-aggr-expression |
Jeder nicht aggregierende Ausdruck, der nicht in der |
column-copy |
Eine wörtliche Kopie aus der |
column-alias |
Der Alias aus der |
column-position |
Die Positionsnummer in der |
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:
-
Durch wörtliches Kopieren des Artikels aus der Auswahlliste, z.B. “
class
” oder “'D:' || upper(doccode)
”. -
Durch Angabe des Spaltenalias, falls vorhanden.
-
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.
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 ...]
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 |
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 |
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 JOIN
s und MERGE
s 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
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.
<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.
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}]
Argument | Beschreibung |
---|---|
col-name |
Vollständiger Spaltenname |
col-alias |
Spaltenalias |
col-position |
Spaltenposition in der |
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 |
Wenn Sie die Spaltenposition verwenden, um die Sortierreihenfolge für eine Abfrage des Stils |
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 UNION
s
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 NULL
s 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 NULL
s am Ende der Menge;
und in aufsteigender Reihenfolge für die Werte der ersten Spalte mit NULL
s 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
Abrufen eines Zeilenabschnitts aus einer geordneten Menge
DSQL, PSQL
SELECT <columns> FROM ... [WHERE ...] [ORDER BY ...] ROWS m [TO n]
Argument | Beschreibung |
---|---|
m, n |
Beliebige Integer-Ausdrücke |
ROWS ist keine Standard-Syntax
|
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.
|
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:
-
Verwenden Sie die
FIRST
/SKIP
-Syntax in diesenSELECT
-Anweisungen — beachten Sie, dass eine Sortierklausel (ORDER BY
) nicht lokal auf die diskreten Abfragen angewendet werden kann, sondern nur auf die kombinierte Ausgabe. -
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;
6.1.11. OFFSET
, FETCH
Abrufen eines Zeilenabschnitts aus einer geordneten Menge
DSQL, PSQL
SELECT <columns> FROM ... [WHERE ...] [ORDER BY ...] [OFFSET <m> {ROW | ROWS}] [FETCH {FIRST | NEXT} [ <n> ] { ROW | ROWS } ONLY] <m>, <n> ::= <integer-literal> | <query-parameter>
Argument | Beschreibung |
---|---|
integer-literal |
Ganzzahlliteral |
query-parameter |
Platzhalter für Abfrageparameter.
|
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.
|
Beispiele für OFFSET
und FETCH
SELECT *
FROM T1
ORDER BY COL1
OFFSET 10 ROWS
SELECT *
FROM T1
ORDER BY COL1
FETCH FIRST 10 ROWS ONLY
OFFSET
- und FETCH
-Klauseln in einer abgeleiteten Tabelle und in der äußeren AbfrageSELECT *
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;
6.1.12. FOR UPDATE [OF]
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 |
Die Unterklausel OF
tut überhaupt nichts.
6.1.13. WITH LOCK
Begrenzte pessimistische Sperrung
DSQL, PSQL
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:
-
extrem klein (idealerweise Singleton), und
-
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, einerGROUP 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:
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. |
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:
-
um eine teure Behandlung von Updatekonfliktfehlern in stark belasteten Anwendungen zu vermeiden, und
-
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.
6.1.14. INTO
SELECT
-Ausgabe an Variablen übergeben
PSQL
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 |
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
-
Auswahl einiger aggregierter Werte und Übergabe an die zuvor deklarierten Variablen
min_amt
,avg_amt
undmax_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, daBetrag
vermutlich ein ganzzahliges Feld ist, würden SQL-Regeln es auf die nächste niedrigere ganze Zahl kürzen. -
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
”)
DSQL, PSQL
<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 ...]
Argument | Beschreibung |
---|---|
cte-stmt |
Jede |
main-query |
Die |
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 inUPDATE
s,MERGE
s 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
-
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
Einfügen von Datenzeilen in eine Tabelle
DSQL, ESQL, PSQL
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 ...]
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 |
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
|
ALERT :
BEFORE INSERT -TriggersAchten Sie unabhängig von der zum Einfügen von Zeilen verwendeten Methode auf alle Spalten in der Zieltabelle oder -ansicht, die von |
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. |
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).
INSERT IN