Die Quelle des meist kopierten Referenzmaterials: Paul Vinkenoog
Weitere Quelle kopierten Referenzmaterials: Thomas Woinke
Übersetzung ins Deutsche: Martin Köditz, Mark Harris
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.
Dieser Band ist eine Zusammenstellung von Themen rund um die Firebird SQL-Sprache, geschrieben von Mitgliedern der russischsprachigen Firebird Benutzer- und Entwickler-Gemeinschaft. Im Jahr 2014 erreichte diese Zusammenstellung ihren Höhepunkt in einem russischsprachigen Referenzhandbuch. Auf Veranlassung von Alexey Kovyazin wurde eine Kampagne unter den weltweit verteilten Firebird-Benutzern gestartet, um Mittel für eine professionelle Übersetzung ins Englische freizumachen. Hieraus sollen wiederum Übersetzungen in andere Sprachen unter der Schirmherrschaft des Firebird Dokumentations-Projektes erstellt werden.
1. Über die Firebird SQL Sprachreferenz: für Firebird 2.5
Diese Firebird SQL Sprachreferenz ist das erste 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. Diese Sprachreferenz hat eine lange Vorgeschichte.
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
Das Material für die Zusammenstellung dieser Sprachreferenz wurde durch die Open Source Community der Firebird-Entwickler über die letzten 15 Jahre zusammengetragen. Die Übergabe der Interbase 6 Open Source-Codebasis im Juli 2000 durch das (damaligen) Inprise / Borland-Konglomerat war herzlich willkommen. Allerdings kam diese Übergabe ohne Rechte zu bestehenden Dokumentationen. Seit dem Fork der Code-Basis durch die Besitzer für private und kommerzielle Zwecke, wurde klar, dass die nichtkommerzielle Firebird Open Source-Community niemals Nutzungsrechte hierfür erhalten würde.
Die zwei wichtigen Bücher des InterBase 6-Sammlung waren das Data Definition Guide und die Language Reference. Während ersteres die Untermenge der Data Definition Language (DDL) abdeckte, war der Rest im zweiten Buch enthalten. Glücklicherweise war es jahrelang für Firebird-Benutzer möglich, die PDF-Handbücher online zu finden.
1.2.1. Aktualisierungen der Sprachreferenz
Das Data Definition Guide, welches die Erstellung und Wartung der Metadaten für Datenbanken behandelt, war über Jahre hinweg “gut genug”: Die Data Definition Language eines DBMS ist üblicherweise stabil und wächst nur langsam im Vergleich zur Data Manipulation Language (DML), die der Abfrage dient und die PSQL für Serverbasierte Programmierung nutzt.
Der Vorsitzende des Firebird Dokumentations-Teams, Paul Vinkenoog, nahm sich der Aufgabe an, die große Masse an Verbesserungen und Neuerungen in der DML und PSQL im Rahmen der Veröffentlichungen zu dokumentieren. Paul war persönlich verantwortlich für das Zusammentragen sowie Zusammenstellen und, zum Großteil, Schreiben der kumulativen Reihe der “Language Reference Updates” — eine für jede Hauptversion seit v.1.5.
1.2.2. Die Geburt des Bigbooks
Ab etwa 2010 arbeitete Paul, mit Firebird Projektleitung Dmitry Yemanov und einem Dokumentationskollegen Thomas Woinke, an der Planung und dem Aufbau einer kompletten SQL-Sprachreferenz für Firebird. Sie begannen damit auf Basis der LangRef-Updates, die voluminös ist. Es würde eine große Aufgabe sein und für alle Beteiligten ein Freizeitersatz.
Dann, in 2013-4, zeigten zwei wohltätige Unternehmen — MICEX und IBSurgeon — erbarmen und beschäftigten drei Autoren, die sich der liegengebliebenen Arbeit annahmen und die Firebird 2.5 Language Reference in russisch veröffentlichten. Sie schrieben den Großteil der fehlenden DDL-Abschnitte und erstellten, übersetzten oder verwendeten DML- und PSQL-Material aus den LangRef Updates, russischsprachigen Support-Forums, Firebird Release Notes, Readme-Dateien und anderen Quellen. Ende 2014 war die Aufgabe weitestgehend erfüllt, in Form eines Microsoft Word-Dokumentes.
Übersetung …
Die russischen Sponsoren erkannten, dass ihre Anstrengungen mit der weltweiten Firebird Community geteilt werden mussten, und fragten einige Projektmitglieder eine Crowd-Funding-Kampagne zu starten, welche den russischen Text professionell ins Englische übersetzen sollte. Dieser übersetzte Text würde dann bearbeitet und in das Projektkonforme DocBook-Format übertragen sowie als Zusatz in die offene Dokumentationsbibliothek der Firebird-Projektes aufgenommen werden. Hier wären die Quellen für weitere Übersetzungen in andere Sprachen verfügbar.
Die Kampagne zum Einsammeln der Gelder startete Ende 2014 und war erfolgreich. Im Juni, 2015, begann der professionelle Übersetzer Dmitry Borodin mit der Übersetzung des russischen Textes. Durch ihn gelangte der Text zur Überarbeitung und DocBook-Konvertierung an Helen Borrie — und hier ist Die Firbeird SQL Sprachreferenz für V.2.5, von …allen!
… und noch mehr Übersetzungen
Nach dem Erscheinen der DocBook-Quellen im CVS, werden unsere vertrauensvollen Übersetzer hoffentlich mit der Arbeit an deutschen, japanischen, italienischen, französischen, portugisischen, spanischen und tschechischen Übersetzungen beginnen. Gewiss ist, dass wir nie genug Übersetzer haben, so bitten wir Sie, die Firebirder, welche der englischen Sprache mächtig sind, über die Übersetzungsarbeiten in Ihre Muttersprache nachzudenken. Auch die Übersetzung von Abschnitten ist hilfreich.
1.2.3. Mitwirkende
Direkter Inhalt
-
Dmitry Filippov (writer)
-
Alexander Karpeykin (writer)
-
Alexey Kovyazin (writer, editor)
-
Dmitry Kuzmenko (writer, editor)
-
Denis Simonov (writer, editor, coordinator)
-
Paul Vinkenoog (writer, designer)
-
Thomas Woinke (writer)
-
Dmitry Yemanov (writer)
Ressourcen Inhalt
-
Adriano dos Santos Fernandes
-
Alexander Peshkov
-
Vladyslav Khorsun
-
Claudio Valderrama
-
Helen Borrie
-
and others
Übersetzungen
-
Dmitry Borodin, megaTranslations.ru
-
Martin Köditz, it & synergy GmbH
Bearbeitung und Konvertierung des englischen Textes
-
Helen Borrie
1.3. Anmerkungen
Die erste komplette Sprachreferenz wäre ohne die Finanzierung nicht möglich gewesen. Wir danken allen Beteiligten für Ihren Beitrag.
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.
Sponsoren des Übersetzungsprojektes
-
Syntess Software BV (Niederlande)
-
Mitaro Business Solutions (Liechtenstein)
Weitere Spender
Folgend sind die Namen der Unternehmen und Personen aufgelistet, die Barmittel für die Kosten der Übersetzung ins Englische, Weiterverwarbeitung des rohen übersetzten Textes und die Konvertierung des Ganzen in das DocBook 4-Format des Firebird-Projektes freigemacht haben.
Integrity Software Design, Inc. (U.S.A.) |
dimari GmbH (Deutschland) |
beta Eigenheim GmbH (Deutschland) |
KIMData GmbH (Deutschland) |
Jason Wharton (U.S.A) |
Trans-X (Schweden) |
Sanchez Balcewich (Uruguay) |
Cointec Ingenieros y Consultores, S.L. (Spanien) |
Aage Johansen (Norwegen) |
Mattic Software (Niederlande) |
André Knappstein (Deutschland) |
Paul F. McGuire (U.S.A.) |
Marcus Marques da Rocha (Brasilien) |
Martin Kerkhoff |
Thomas Vedel (Dänemark) |
Bulhan Bulhan (Australien) |
Alexandre Benson Smith (Brasilien) |
Guillermo Nabrink (Brasilien) |
Pierre Voirin (Frankreich) |
Heiko Tappe (Deutschland) |
Doug Chamberlain (U.S.A.) |
Craig Cox (U.S.A.) |
OMNet, Inc. (Schweiz) |
Alfred Steller (Deutschland) |
Konrad Butz (Deutschland) |
Thomas Smekal (Kanada) |
Carlos H. Cantu (Brasilien) |
XTRALOG SARL (Frankreich) |
Laszlo Urmenyi (Brasilien) |
Fernando Pimenta (Brasilien) |
Rudolf Grauberger (Deutschland) |
Thomas Steinmaurer (Austria) |
Rene Lobsiger (Schweiz) |
Hian Pin Tjioe |
Xavier Codina |
Mick Arundell (Australien) |
Russell Belding (Neuseeland) |
Anticlei Scheid (Brasilien) |
Luca Minuti (Italien) |
Mark Rotteveel (Niederlande) |
Chris Mathews (U.S.A.) |
Hannes Streicher (Deutschland) |
Wolfgang Lemmermeyer (Deutschland) |
Paolo Sciarrini (Italien) |
Acosta Belzusarri |
Daniel Motos Guerra |
Alberto Alfonso Luna |
Simeon Bodurov |
Cees Meijer |
Robert Nixon |
Olivier Dehorter (Frankreich) |
András Omacht (Ungarn) |
Web Express |
Sergio Conzalez |
Marc Bleuwart |
Gabor Boros |
Shaymon Gracia Campos |
Cserna Zsombor (Ungarn) |
David Keith |
Uwe Gerold |
Daniele Teti (Italien) |
Pedro Pablo Busto Criado |
Istvan Szabo |
Spiridon Pavlovic |
J. L. Garcia Naranjo |
A. Morales Morales |
Helen Cullen (Neuseeland) |
Francisco Ibarra Ozuna |
|
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 ergänzt Anweisungen der Dynamic SQL um lokale Variablen, Zuweisungen, Bedingungen, Schleifen und andere prozedurale Verhalten.
PSQL entspricht dem 4. Teil (SQL/PSM) der SQL-Spezifikationen.
Ursprüunglich waren PSQL-Erweiterungen nur über persistent gespeicherte Module (Prozeduren und Trigger) verfügbar.
In späteren Releases wurden diese jedoch auch in Dynamic SQL aufgenommen (vergleichen Sie hierzu auch EXECUTE BLOCK
).
Embedded SQL berschreibt die Untermenge von DSQL, die von Firebird gpre unterstützt wird. Dies ist die Anwendung, welche es erlaubt, SQL-Konstrukte in Ihre Host-Programmiersprache (C, C++, Pascal, Cobol, etc.) einzubetten und diese in gültigen Firebird API-Aufrufen auszuführen.
Nur ein Teil der in DSQL implementierten Anweisungen und Ausdrücke werden in ESQL unterstützt. |
Interactive ISQL wird durch die Sprache beschrieben, die mittels Firebirds isql ausgeführt werden kann. Dies ist die Befehlszeilenanwendung, für den interaktiven Zugriff auf Datenbanken. Da dies eine reguläre Client-Anwendung ist, ist ihre native Sprache in DSQL verfasst. Sie nutzt außerdem einige zusätzliche Befehle, die nicht außerhalb ihrer spezifischen Umgebung gelten.
Sowohl DSQL wie auch PSQL werden vollständig in dieser Referenz behandelt. Dies gilt nicht für ESQL und ISQL, sofern nicht ausdrücklich beschrieben.
2.1.2. SQL-Dialekte
Der Begriff SQL dialect beschreibt ein spezifisches Feature der SQL-Sprache, das bei Zugriff einer Datenbank zur Verfügung steht. SQL-Dialekte können auf Datenbankebene definiert und auf Verbindungsebene spezifiziert werden. Drei Dialekte stehen zur Verfügung:
-
Dialect 1 dient aussschließlich der Abwärtskompatibilität mit sehr alten InterBase-Datenbanken bis Version 5. Dialekt 1-Datenbanken beinhalten einige Features, die sich von Dialekt 3, dem Firebird-Standard, unterscheiden.
-
Datums- und Zeitinformationen werden als DATE-Datentyp gespeichert. Ein
TIMESTAMP
-Datentyp ist ebenfalls verfügbar, der identisch mit dieserDATE
-Implementierung ist. -
Doppelte Anführungszeichen dürfen als Alternative für das Apostroph als Textbegrenzer verwendet werden. Dies ist gegensätzlich zum SQL-Standard — doppelte Anführungszeichen sind für den einen bestimmten Zweck sowohl in Standard SQL wie auch in Dialekt 3 reserviert. Als Textbegrenzer sollten doppelte Anführungszeichen demnach energisch vermieden werden.
-
Die Präzision für
NUMERIC
- undDECIMAL
-Datentypen ist geringer als im Dialekt 3 und falls die Präzision einer Dezimalzahl größer als 9 Stellen sein soll, wird Firebird diese intern als Fließkommazahl (DOUBLE PRECISION
) speichern. -
Der Datentyp
BIGINT
(64-Bit Integer) wird nicht unterstützt. -
Bezeichner unterscheiden Groß- und Kleinschreibung und müssen immer den Regeln für Bezeichner entsprechen — vergleichen Sie den Abschnitt Bezeichner.
-
Obwohl Generator-Werte als 64-Bit-Zahlen gespeichert werden, wird ein Clientaufruf von zum Beispiel
SELECT GEN_ID (MyGen, 1)
immer einen 32-Bit-Wert zurückgeben.
-
-
Dialect 2 ist nur in der Firebird-Clientverbindung verfügbar und kann nicht in der Datenbank definiert werden. Hintergrund ist, Entwickler beim Debugging zu unterstützen, wenn eine reguläre Datenbank von Dialekt 1 zu Dialekt 3 migriert werden soll.
-
In Dialect 3-Datenbanken,
-
werden Zahlen (
DECIMAL
- undNUMERIC
-Datentypen) intern als lange Festkommawerte (skalierte Ganzzahlen) gespeichert, sobald die Präzision größer als 9 Stellen ist. -
Der
TIME
-Datentyp ist nur für das Speichern von Tageszeiten gedacht. -
Der
DATE
-Datentyp speichert nur Datumsinformationen. -
Der
BIGINT
-Datentyp für 64-Bit-Integer ist verfügbar. -
Doppelte Anführungszeichen sind reserviert als Begrenzer für irreguläre Bezeichner. Diese ermöglichen es Objektnamen zu definieren, die Groß- und Kleinschreibung unterscheiden oder die Anforderungen an reguläre Bezeichner nicht erfüllen.
-
Alle Zeichenketten müssen in einfachen Anführungszeichen begrenzt werden.
-
Generatorwerte werden als 64-Bit-Ganzzahlen gespeichert.
-
Die Verwendung von Dialekt 3 wird strengstens für die Entwicklung neuer Datenbanken und Anwendungen empfohlen. Sowohl die Datenbank- als auch die Verbindungsdialekte sollte zueinander passen. Ausnahme bildet die Migration mittels Dialekt 2. Diese Referenz beschreibt die Semantic unter SQL Dialekt 3, solange nicht anders angegeben. |
2.2. Grundelemente: Statements, Klauseln, Schlüsselwörter
Das grundlegendste Konstrukt in SQL ist das Statement. Ein Statement definert was das DBMS mit bestimmten Daten oder Metadaten-Objekten tun soll. Komplexere Statements bedienen sich einfacher Konstrukte — Klauseln und Optionen.
- Klauseln
-
Eine Klausel definiert eine besteimmte Art von Derektiven in einem Statement. So bestimmt zum Beispiel die
WHERE
-Klausel in einemSELECT
-Statement und anderen manipulativen Statements (UPDATE
,DELETE
) Kriterien, um Daten innerhalb einer oder mehrerer Tabellen auszuwählen, zu aktualisieren oder zu löschen. Die KlauselORDER BY
gibt an, in welcher Reihenfolge die ausgegebenen Daten — Rückgabesatz — sortiert werden sollen. - Optionen
-
Optionen sind die einfachsten Konstrukte und werden in Verbindung mit bestimmten Schlüsselwörtern eingesetzt. Wo Optionen zum Einsatz kommen, wird eine als Standard hinterlegt, solange nichts anderes angegeben wurde. So wird zum Beispiel das
SELECT
-Statement alle Datenzeilen zurückgeben, die die erforderlichen Kriterien der Abfrage erfüllen, es sei denn die OptionDISTINCT
schränkt diese Ausgabe auf eindeutige Zeilen ein. - Schlüsselwörter
-
Alle Schlüsselwörter die im SQL-Katalog enthalten sind, werden als Schlüsselwörter bezeichnet. Einige Schlüsselwörter sind reserviert, das heißt ihr Gebrauch als Bezeichner für Datenbankobjekte, Parameternamen oder Variablen ist in bestimmten oder gar allen Kontexten untersagt. Nichtreservierte Schlüsselwörter können als Bezeichner verwendet werden, obwohl dies nicht empfohlen wird. Von Zeit zu Zeit kann es vorkommen, dass nichtreservierte Schlüsselwörter im Zuge von Spracherweiterungen reserviert werden.
Im Beispiel wird das folgende Statement ohne Fehler ausgeführt, obwohl
ABS
ein Schlüsselwort und nicht reserviert ist.CREATE TABLE T (ABS INT NOT NULL);
Andererseits wird das folgende Statement mit einem Fehler beendet, da
ADD
sowohl ein Schlüsselwort als auch ein reserviertes Wort ist.CREATE TABLE T (ADD INT NOT NULL);
Bitte vergleichen Sie auch die Auflistung reservierter Wörter und Schlüsselwörter im Abschnitt Reservierte Wörter und Schlüsselwörter.
2.3. Bezeichner
Alle Datenbankobjekte haben Namen, häufig auch Bezeichner genannt. Zwei Namensarten sind gültige Bezeichner: reguläre Namen, ähnlich den Variablennamen in regurlären Programmiersprachen, und begrenzte Namen, die spezifisch für SQL sind. Um als gültig erachtet zu werden, muss jeder Bezeichnertyp konform zu gewissen Regeln sein:
2.3.1. Regeln für reguläre Objektbezeichner
-
Die Zeichenlänge darf 31 Zeichen nicht übersteigen
-
Der Name muss mit einem unakzentuierten, 7-Bit ASCII-Zeichen beginnen. Zahlen sind nicht erlaubt. Die weiteren Zeichen dürfen aus weiteren 7-Bit ASCII-Zeichen, Zahlen, Unterstrichen oder Dollarzeichen bestehen. Keine anderen Zeichen, darunter auch Leerzeichen, dürfen Verwendung finden. Beim Namen wird nicht zwischen Groß- und Kleinschreibung unterschieden. Das heißt, der Name kann sowohl in Klein- als auch Großschreibung verwendet werden. Somit sind folgende Namen für das System gleichzusetzen:
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 begrenzte Objektbezeichner
-
Die Zeichenlänge darf 31 Zeichen nicht überschreiten.
-
Die gesamte Zeichenkette muss in doppelte Anführungszeichen eingeschlossen werden, z.B.
"anIdentifier"
. -
Es darf jedes Zeichen eines Latin-Zeichensatzes verwendet werden, inklusive akzentuierte Zeichen, Leerzeichen und Sonderzeichen.
-
Ein Bezeichner darf ein reserviertes Wort sein.
-
Begrenzte Objektbezeichner unterscheiden immer zwischen Groß- und Kleinschreibung.
-
Führende Leerzeichen werden entfernt, so wie bei jeder konstanten Zeichenkette.
-
Begrenzte Objektbezeichner sind nur in Dialekt 3 verfügbar. Für mehr Details, vgl. SQL-Dialekte
<delimited name> ::= "<permitted_character>[<permitted_character> …]"
Ein begrenzter Bezeichner wie |
2.4. Literale
Literale werden verwendet um Daten in einem bestimmten Format zu repräsentieren. Beispiele hierfür sind:
integer - 0, -34, 45, 0X080000000;
real - 0.0, -3.14, 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';
null state - null
Wie Literale für die diversen Datentypen verwendet werden, wird im nächsten Abschnitt Datentypen und Unterdatentypen behandelt.
2.5. Operatoren und Sonderzeichen
Einige Sonderzeichen sind für die Verwendung als Operator oder Separator reserviert.
<special char> ::=
<space> | " | % | & | ' | ( | ) | * | + | , | -
| . | / | : | ; | < | = | > | ? | [ | ] | ^ | { | }
Einige dieser Zeichen, allein oder in Kombination, dürfen als Operatoren (arithmetisch, Zeichenkette, logisch), als SQL Befehlsseparatoren, zum Anführen von Bezeichnern und zum Markieren von Begrenzungen von Zeichenketten 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
Für weitere Details zu Operatoren, vgl. Ausdrücke.
2.6. Kommentare
Kommentare können in SQL-Skripten, -Statements und PSQL-Modulen eingefügt werden. Ein Kommentar kann dabei jede Art von Text sein, die der Autor üblicherweise zum Dokumentieren seines Codes verwendet. Der Parser ignoriert Kommentartexte.
Firebird unterstützt zwei Arten von Kommentaren: block und in-line.
<comment> ::= <block comment> | <single-line comment> <block comment> ::= /* <ASCII char>[<ASCII char> …] */ <single-line comment> ::= -- <ASCII char>[<ASCII char> …]<end line>
Block-Kommentare starten mit Zeichenpaar /*
und enden mit dem Zeichenpaar */
.
Text innerhalb der Block-Kommentare kann jede Länge haben und darf aus mehreren Zeilen bestehen.
In-line-Kommentare starten mit dem Zeichenpaar --
und gelten bis zum Ende der Zeile.
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 Unterdatentypen
Daten unterschiedlicher Typen werden verwendet für:
-
die Spaltendefinition in Datenbanktabellen mittels
CREATE TABLE
-Statement oder zum Ändern der Spalten mittelsALTER TABLE
-
die Deklaration oder Änderung einer Domain unter Verwendung der Statements
CREATE DOMAIN
oderALTER DOMAIN
-
die Deklaration lokaler Variablen in gespeicherten Prozeduren, PSQL-Blöcken und Triggern sowie spezifizierter Parameter in gespeicherten Prozeduren
-
die indirekte Spezifikation von Argumenten und Rückgabewerten bei der Deklaration externer Funktionen (UDFs — user-defined functions)
-
die zur Verfügungstellung von Argumenten für die Funktion
CAST()
um Daten von einem Typ zu einem anderen zu konvertieren
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. |
|
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. Integer-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 Datentyp SMALLINT
dient dem Speichern von Kleinstzahlen mit einem Bereich -216 bis 216 - 1, also von -32.768 bis 32.767.
CREATE DOMAIN DFLAG AS SMALLINT DEFAULT 0 NOT NULL
CHECK (VALUE=-1 OR VALUE=0 OR VALUE=1);
CREATE DOMAIN RGB_VALUE AS SMALLINT;
3.1.2. INTEGER
Daten vom Typ INTEGER
werden als 4-Byte-Integer repräsentiert.
Der Kurzname dieses Datentyps ist INT
.
Der gültige Bereich des Datentyps INTEGER
reicht von -232 bis 232 - 1, also von -2.147.483.648 bis 2.147.483.647.
CREATE TABLE CUSTOMER (
CUST_NO INTEGER NOT NULL,
CUSTOMER VARCHAR(25) NOT NULL,
CONTACT_FIRST VARCHAR(15),
CONTACT_LAST VARCHAR(20),
...
PRIMARY KEY (CUST_NO) )
3.1.3. BIGINT
BIGINT
ist ein SQL:99-konformer, 64-Bit langer Integer-Datentyp, der nur in Dialekt 3 zur Verfügung steht.
Wenn der Client Dialekt 1 verwendet, sendet der Server den auf 32 Bit (Integer) reduzierten Generatorwert.
Bei Dialekt 3 wird der Datentyp BIGINT
verwendet.
Der Zahlenraum des Datentyps BIGINT liegt im Bereich zwischen -263 und 263 - 1, also 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
Seit Firebird 2.5 können Zahlen vom Datentyp BIGINT
auch im Hexadezimalen Format geschrieben werden.
Dabei können 9 bis 16 Stellen angegeben werden.
Kürzere Hexadezimalzahlen werden als INTEGER
interpretiert.
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
im obigen Beispiel werden automatisch in den BIGINT
-Datentyp gewandelt, bevor sie in die Tabelle eingefügt werden.
Jedoch passiert dies nachdem der numerische Wert bestimmt wurde, wodurch 0x80000000 (8 Stellen) und 0x080000000 (9 Stellen) als unterschiedliche BIGINT
-Werte gespeichert werden.
3.2. Fließkomma-Datentypen
Fließkomma-Datentypen werden nach dem IEEE 754-Standard binär gespeichert und enthalten Vorzeichen, Exponent und Mantisse.
Die Genauigkeit ist dynamisch, je nach verwendetem Speicherformat, welches 4 Bytes für FLOAT
oder 8 Bytes für DOUBLE PRECISION
im Speicher belegt.
Angesichts der Besonderheiten bei der Speicherung von Fließkommazahlen in einer Datenbank, werden diese Datentypen nicht zum Speichern von monetären Daten empfohlen. Aus den gleichen Gründen sollte die Verwendung dieser Felder für Schlüssel und Eindeutigkeiten vermieden werden.
Für die Prüfung von Daten in Spalten mit Fließkomma-Datentypen sollten Ausdrücke auf bestimmte Bereiche, statt auf Übereinstimmungen geprüft werden.
Wenn diese Datentypen in Ausdrücken Verwendung finden, ist äußerste Sorgfalt in Bezug auf die Rundung der Ergebnisse zu legen.
3.3. Festkomma-Datentypen
Festkomma-Datentypen sorgen für die Vorhersehbarkeit bei Multiplikations- und Divisionoperationen.
Somit sind sie die erste Wahl zum Speichern monetärer Daten.
Firebird implementiert zwei Festkomma-Datentypen: NUMERIC
und DECIMAL
.
Gemäß dem Standard, werden beide Datentypen mit der Anzahl zu speichernder Stellen (Stellen nach dem Komma) begrenzt.
Verschiedene Behandlungen beschränken die Genauigkeit für jeden Typ: Die Genauigkeit für NUMERIC
-Felder ist genau “wie deklariert”, wohingegen DECIMAL
-Felder Zahlen akzeptieren, deren Genauigkeit mindestens der Deklaration entspricht.
Beispielsweise definiert NUMERIC(4, 2)
eine Zahl mit insgesamt 4 Stellen und 2 Nachkommastellen;
das heißt, es können bis zu zwei Stellen vor dem Komma und maximal 2 Stellen nach dem Komma verwendet werden.
Wird die Zahl 3,1415 in ein Feld dieses Datentyps geschrieben, wird der Wert 3,14 gespeichert.
Die Art der Deklaration für Festkomma-Datentypen, zum Beispiel NUMERIC(p, s)
, ist beiden Typen gleich.
Zu beachten ist, dass die Skalierung mittels des Arguments s, vielmehr die Bedeutung “Anzahl der Stellen nach dem Dezimalkomma” hat.
Das Verständnis wie Festkomma-Datentypen ihre Daten speichern und abrufen zeigt auch, warum dies so ist: beim Speichern wird die Zahl mit 10s multipliziert (10 hoch s) und als Integer gespeichert;
beim Lesen wird der Integer wieder zurückkonvertiert.
Die Art und Weise wie Festkomma-Daten im DBMS abgelegt werden, hängt von diversen Faktoren ab: Genauigkeit, Datenbankdialekt, Deklartionstyp.
Skalierung | Datentyp | Dialekt 1 | Dialekt 3 |
---|---|---|---|
1 - 4 |
|
|
|
1 - 4 |
|
|
|
5 - 9 |
|
|
|
10 - 18 |
|
|
|
3.3.1. NUMERIC
NUMERIC | NUMERIC(precision) | NUMERIC(precision, scale)
In Bezug auf das obige Beispiel, wird das DBMS NUMERIC
-Daten in Abhängigkeit von der angegebenen Genauigkeit (precision) und der Skalierung (scale) speichern.
Weitere Beispiele:
NUMERIC(4) wird gespeichert als SMALLINT (exakte Daten) NUMERIC(4,2) SMALLINT (Daten * 102) NUMERIC(10,4) (Dialekt 1) DOUBLE PRECISION (Dialekt 3) BIGINT (Daten * 104)
Beachten Sie, dass das Speicherformat von der Genauigkeit abhängt.
So können Sie zum Beispiel die Spalte als |
3.3.2. DECIMAL
DECIMAL | DECIMAL(precision) | DECIMAL(precision, scale)
Das Speicherformat in der Datenbank für DECIMAL
-Felder ist ähnlich dem von NUMERIC
, mit dem Unterschied, dass es leichter zu verstehen ist, mit ein paar Beispielen:
DECIMAL(4) gespeichert als INTEGER (exakte Daten) DECIMAL(4,2) INTEGER (Daten * 102) DECIMAL(10,4) (Dialekt 1) DOUBLE PRECISION (Dialekt 3) BIGINT (Daten * 104)
3.4. Datentypen für Datum und Zeit
Die Datentypen DATE
, TIME
und TIMESTAMP
werden zur Arbeit mit Daten verwendet, die Daten und Zeiten beinhalten.
Dialekt 3 unterstützt alle drei Typen, während Dialekt 1 nur DATE
kennt.
Der Datentyp DATE
in Dialekt 3 ist “nur Datum”, wohingegen der Datentyp DATE
in Dialekt 1 Datum und Tageszeit speichert.
Somit ist dieser equivalent zu TIMESTAMP
in Dialekt 3.
Dialekt 1 kennt keinen “nur Datum”-Typ.
In Dialekt 1 können |
Werden Sekundenbruchteile in Datums- und Zeitfeldern benötigt, speichert Firebird diese in zehntausendstel Sekunden. Wird eine geringere Genauigkeit bevorzugt, kann diese explizit als tausendstel, hundertstel oder zehntel Sekunde in Dialekt 3-Datenbanken ab ODS 11 spezifiziert werden.
Einige nützliche Informationen zur Genauigkeit von Sekundenbruchteilen:
Der Zeitanteil eines
Die Genauigkeit auf Basis von Dezimalmillisekunden ist selten und wird derzeit nicht in Spalten oder Variablen gespeichert.
Obwohl Firebird die Werte von |
3.4.1. DATE
Der Datentyp DATE
in Dialekt 3 speichert nur das Datum ohne Zeitangabe.
Der verfügbare Bereich reicht vom 1. Januar 0001 bis zum 31. Dezember 9999.
In Dialekt 1 gibt es keinen “nur Datum”-Datentyp.
In Dialekt 1, erhalten Datums-Literale ohne Zeitangabe, genauso wie Sollte es für Sie wichtig sein, aus welchem Grund auch immer, einen Zeitstempel-Literal mit einem expliziten Null-Zeitteil in Dialekt 1 zu speichern, wird die Datenbank ein Literal wie |
3.4.2. TIME
Der Datentyp TIME
ist nur in Dialekt 3 verfügbar.
Er speichert die Tageszeit im Bereich von 00:00:00.0000 bis 23:59:59.9999.
Sollten Sie den Zeitteil eines DATE
in Dialekt 1 benötigen, können Sie die Funktion EXTRACT
verwenden.
EXTRACT
EXTRACT (HOUR FROM DATE_FIELD)
EXTRACT (MINUTE FROM DATE_FIELD)
EXTRACT (SECOND FROM DATE_FIELD)
3.4.3. TIMESTAMP
Der Datentyp TIMESTAMP
ist in Dialekt 1 und 3 verfügbar.
Er besteht aus zwei 32-Bit-Teilen — einem Datums- und einem Zeitteil — womit eine Struktur gebildet wird, die sowohl Datum wie auch Tageszeit aufnimmt.
Der Datentyp ist identisch mit DATE
in Dialekt 1.
Die Funktion EXTRACT
arbeitet für TIMESTAMP
genauso wie für DATE
in Dialekt 1.
3.4.4. Operationen, die Datums- und Zeitwerte verwenden
Das Verfahren zum Speichern der Datums- und Zeitwerte macht es möglich, diese als Operanden in arithmetischen Operationen zu verwenden. Gespeichert, wird ein Datumswert als Zahl seit dem “Datum Null” — 17. November 1898 — repräsentiert, ein Zeitwert als Anzahl der Sekunden (unter Berücksichtigung der Sekundenbruchteile) seit Mitternacht.
Ein Beispiel ist das Subtrahieren eines früheren Datum, einer Zeit oder eines Zeitstempels von einem späteren, was in einm Zeitintervall, in Tagen und Bruchteilen von Tagen resultiert.
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 Datentyp |
3.5. Zeichendatentypen
Für die Arbeit mit Zeichendaten bietet Firebird die Datentypen CHAR
mit Festlänge und VARCHAR
mit variabler Zeichenlänge.
Die Maximalgröße der hiermit speicherbaren Daten beträgt 32.767 Bytes für CHAR
und 32.765 Bytes für VARCHAR
.
Das Maximum der möglichen Zeichen, das in diese Grenzen passt, hängt vom verwendeten Zeichensatz (CHARACTER SET
) ab.
Die Sortiermethode COLLATE
wirkt sich nicht auf die Maximalgrenze aus, wohingegen sie durchaus die maximale Größe eines Indexes auf dieser Spalte beeinflussen kann.
Wurde während der Definition eines Zeichenobjektes kein expliziter Zeichensatz festgelegt, wird der Standardzeichensatz der Datenbank verwendet.
Wurde für die Datenbank kein Standardzeichensatz festgelegt, erhält das Feld den Zeichensatz NONE
.
3.5.1. Unicode
Die meisten aktuellen Entwicklertools unterstützen Unicode, welches in Firebird mit den Zeichensätzen UTF8
und UNICODE_FSS
integriert ist.
UTF8
bietet Sortierungen für viele Sprachen.
UNICODE_FSS
ist deutlich begrenzter und wird hauptsächlich intern durch Firebird für das Speichern von Metadaten verwendet.
Beachten Sie, dass ein UTF8
-Zeichen bis zu 4 Bytes beanspruchen kann, wodurch die Größe von CHAR
-Feldern auf 8.191 Zeichen reduziert werden kann (32.767/4).
Der genaue Wert der “Bytes pro Zeichen” hängt vom Bereich des Zeichens ab. Nicht-akzentuierte Latin-Buchstaben beanspruchen 1 Byte, kyrillische Zeichen mit WIN1251-Enkodierung beanspruchen 2 Bytes, andere Zeichenenkodierungen können bis zu 4 Bytes beanspruchen. |
Der in Firebird implementierte UTF8
-Zeichensatz, unterstützt die aktuellste Version des Unicode-Standards.
Somit ist dieser für internationale Datenbanken empfohlen.
3.5.2. Client-Zeichensatz
Während der Arbeit mit Zeichenketten, ist es notwendig, den Zeichensatz des Clients zu berücksichtigen.
Sollte eine Diskrepanz zwischen den Zeichensätzen der gespeicherten Daten und der Clientverbindung existieren, werden Ausgaben für Textfelder automatisch neu enkodiert.
Dies gilt für Daten, die vom Client zum Server gesendet werden und anders herum.
Wurde die Datenbank beispielsweise mit WIN1251
enkodiert, der Client verwendet jedoch KOI8R
oder UTF8
, stellt sich die Diskrepanz transparent dar.
3.5.3. Spezielle Zeichensätze
NONE
Der Zeichensatz NONE
ist ein Spezialzeichensatz in Firebird.
Er kann so beschrieben werden, als wäre jedes Byte Teil einer Zeichenkette, die jedoch keine Angaben zur Beschreibung eines Zeichens macht: Zeichenenkodierung, Sortierung, Klein- und Großschreibung, etc. sind einfach unbekannt.
Es liegt in der Verantwortung der Client-Anwendung mit den Daten umzugehen und die richtigen Mittel bereitzustellen, um die Folge von Bytes in irgendeiner Weise interpretieren zu können, die für die Anwendung sinnvoll sind und für den Menschen lesbar.
OCTETS
Daten der OCTETS
-Enkodierung werden als Bytes behandelt, die nicht direkt als Zeichen interpretiert werden.
OCTETS
bieten einen Weg um Binärdaten zu speichern, was die Ergebnisse einiger Firebird-Funktionen sein könnten.
Die Datenbank weiß nicht was mit einer Zeichenkette aus Bits in OCTETS
zu tun ist, außer diese zu speichern und abzufragen.
Auch hier ist wieder der Client verantwortlich für die Validierung der Daten und diese sowohl der Anwendung wie auch dem Benutzer in verständlicher Form anzuzeigen.
Dies gilt auch für Ausnahmen, die durch die Enkodierung und Dekodierung verursacht werden.
3.5.4. COLLATION
Jeder Zeichensatz hat eine Standardsortiermethode (COLLATE
).
Üblicherweise stellt diese nicht mehr bereit als die Reihenfolge basierend auf einem numerischen Code der Zeichen und einer Basiszuordnung der Klein- und Großbuchstaben.
Wird ein Verhalten außerhalb der Collation benötigt und eine alternative Methode für den Zeichensatz unterstützt, kann eine COLLATE Sortier
-Klausel in der Felddefinition verwendet werden.
Eine COLLATE Sortier
-Klausel kann auch in anderen Zusammenhängen neben der Spaltendefinition angewandt werden.
Für größer-als-/kleiner-als-Vergleiche, kann sie in die WHERE
-Klausel des SELECT
-Statements aufgenommen werden.
Wird eine speziell alphabetisch geordnete oder Groß- und Kleinschreibungsinsensitive Ausgabe benötigt, und sollte eine passende Collation existieren, dann kann die COLLATE
-Klausel auch in der ORDER BY
-Klausel verwendet werden.
Groß- und Kleinschreibungsinsensitive Suche
Für die Groß- und Kleinschreibungsinsensitive Suche, kann die Funktion UPPER
verwendet werden, damit das Suchargument und der die gesuchte Zeichenkette in Großbuchstaben gewandelt werden, bevor der Vergleich stattfindet:
…
where upper(name) = upper(:flt_name)
Für Zeichenketten eines Zeichensatzes, der keine Groß- und Kleinschreibungsinsensitive Sortierung bereitstellt, können sie die Sortierung anwenden, um das Suchargument und die gesuchte Zeichenkette direkt miteinander zu vergleichen.
Beispielsweise ist unter dem Zeichensatz WIN1251
die Sortierung PXW_CYRL
Groß- und Kleinschreibungsinsensitiv.
Somit gilt:
…
WHERE FIRST_NAME COLLATE PXW_CYRL >= :FLT_NAME
…
ORDER BY NAME COLLATE PXW_CYRL
UTF8
-Collation
Die folgende Tabelle zeigt mögliche Sortiermethoden für den UTF8
-Zeichensatz.
Sortierung | Merkmale |
---|---|
|
Sortierung arbeitet abhängig von der Position des Zeichens in der Tabelle (binär). Hinzugefügt in Firebird 2.0 |
|
Sortierung arbeitet abhängig vom UCA-Algorithmus (Unicode Collation Algorithm) (alphabetisch). Hinzugefügt in Firebird 2.0 |
|
Die Standard-Sortierung, binär, identisch zu |
|
Groß- und Kleinschreibungsinsensitive Sortierung, arbeitet ohne Groß- und Kleinschreibung zu berücksichtigen. Hinzugefügt in Firebird 2.1 |
|
Groß- und Kleinschreibungsunabhängige, akzentunabhängige Sortierung, die weder Groß- und Kleinschreibung noch Akzentuierung von Zeichen berücksichtigt. Arbeitet alphabetisch. Hinzugefügt in Firebird 2.5 |
Ein Beispiel einer Sortierung für den UTF8-Zeichensatz ohne Berücksichtigung der Groß- und Kleinschreibung oder Akzentuierung der Zeichen (ähnlich zu COLLATE PXW_CYRL
).
...
ORDER BY NAME COLLATE UNICODE_CI_AI
3.5.5. Zeichenindizes
In Firebird-Versionen vor 2.0 kann es zu Problemen beim Erstellen eines Index für Zeichenspalten kommen, die eine außergewöhnliche Collation nutzen: die Länge eines indizierten Feldes ist auf 252 Bytes beschränkt, sofern kein COLLATE
spezifiziert wurde, andernfalls sind es 84 Bytes.
Multi-Byte-Zeichensätze schränken die Indizes weiter ein.
Ab Firebird 2.0 beschränkt sich die Indexlänge auf ein Viertel der Seitengröße (page size), z.B. von 1.024 bis 4.096 Bytes. Die größtmögliche Länge einer indizierten Zeichenkette liegt bei 9 Bytes weniger als die Viertel-Seiten-Grenze.
Die folgende Formel berechnet der maximale Länge eines indizierten Zeichenfeldes (in Zeichen):
max_char_length = FLOOR((page_size / 4 - 9) / N)
wobei N die Anzahl der Bytes pro Zeichen im Zeichensatz darstellt.
Die folgende Tabelle zeigt die Maximallänge einer indizierten Zeichenkette (in Zeichen), abhängig von der Seitengröße und Zeichensatz. Die Maximallänge wurde mittels der o.a. Formel berechnet.
Seitengröße (Page Size) |
Maximale Länge einer indizierten Zeichenkette für ein Zeichen, Bytes / 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 |
Mit Collations (“_CI”), die Groß- und Kleinschreibungsinsensitiv sind, wird ein Zeichen im Index nicht 4, sondern 6 Bytes, beanspruchen. Hierdurch wird die maximale Schlüssellänge für eine Seitengröße von zum Beispiel 4.096 Bytes auf 169 Zeichen begrenzt. |
3.5.6. Zeichendatentypen im Detail
CHAR
CHAR
ist ein Festlängen-Datentyp.
Ist die eingegebene Anzahl der Zeichen kleiner als die definierte Länge, werden Leerzeichen zu dem Feld hinzugefügt.
Grundsätzlich muss das Füllzeichen kein Leerzeichen sein: dies hängt vom Zeichensatz ab.
So ist das Füllzeichen für den OCTETS
-Zeichensatz beispielsweise die Null.
Der volle Name dieses Datentyps ist CHARACTER
, es ist jedoch nicht erforderlich volle Namen zu verwenden und die Leute tun dies auch sehr selten.
Daten für Festlängen-Zeichen können verwendet werden, um Codes zu speichern, deren Länge standardisiert ist und die eine definierte “Breite” besitzen. Ein Beispiel hierfür ist ein EAN13-Barcode — 13 Zeiche, alle gefüllt.
{ CHAR | CHARACTER } [ (length) ] [CHARACTER SET <set>] [COLLATE <name>]
Wurde keine Länge (length) angegeben, wird 1 verwendet. Eine gültige Länge (length) befindet sich im Bereich von 1 bis maximal so vielen Zeichen, die in 32,767 Bytes passen. |
VARCHAR
VARCHAR
ist der Standarddatentyp zum Speichern von Texten variabler Länge.
Die Zeichen müssen in 32.765 Bytes passen.
Die Speicherstruktur ist identisch mit der aktuellen Datengröße plus 2 Bytes.
Alle Zeichen, die von der Client-Anwendung an die Datenbank gesendet werden, werden als sinnvoll erachtet, was auch für führende und nachrangige Leerzeichen gilt. Jedoch werden angestellte Leerzeichen nicht gespeichert: Sie werden wiederhergestellt, sobald die Daten abgerufen werden. Die Zeichenkette wird dann bis zu der gespeicherten Länge mit dem Leerzeichen aufgefüllt.
Der volle Name dieses Datentyps ist CHARACTER VARYING
.
Eine weitere Variante dieses Namens wird auch mit CHAR VARYING
bezeichnet.
{ VARCHAR | CHAR VARYING | CHARACTER VARYING } (length) [CHARACTER SET <set>] [COLLATE <name>]
NCHAR
NCHAR
ist ein Festlängen-Datentyp.
Der ISO8859_1
-Zeichensatz ist vordefiniert.
In allen anderen Bezügen verhält sich dieser Datentyp identisch zu CHAR
.
{ NCHAR | NATIONAL { CHAR | CHARACTER } } [ (length) ]
Für variable Längen gibt es einen ähnlichen Datentyp: NATIONAL CHARACTER VARYING
.
3.6. Binärdatentypen
BLOB
s (Binary Large Objects) nutzen komplexe Strukturen, um Texte und binäre Daten beliebiger Länge zu speichern.
Diese Daten sind häufig sehr groß.
BLOB [SUB_TYPE <subtype>] [SEGMENT SIZE <segment size>] [CHARACTER SET <character set>] [COLLATE <collation name>]
BLOB (<segment size>) BLOB (<segment size>, <subtype>) BLOB (, <subtype>)
Das Spezifizieren von BLOB-Segmenten ist ein Rückschritt in vergangene Zeiten, als die Programme zur Verarbeitung von BLOB-Daten noch mit Hilfe des gpre-Pre-Compilers in C (Embedded SQL) geschrieben wurden. Heutzutage ist dies irrelevant. Die Segmentgröße für BLOB-Daten wird auf Client-Seite festgelegt und ist üblicherweise größer als die Größe der Datenseite.
3.6.1. BLOB
Untertypen
Der optionale Parameter SUB_TYPE
gibt die Art der zu schreibenden Felddaten an.
Firebird unterstützt zwei vordefinierte Untertypen für die Datenspeicherung:
- Untertyp 0:
BINARY
-
Wurde kein Untertyp angegeben, wird von einem unspezifizierten Datentyp ausgegangen und somit als Standard
SUB_TYPE 0
verwendet. Der Alias für diesen Untertyp istBINARY
. Dies ist der Untertyp, der bei der Arbeit mit jeglichen Binärdaten Verwendung findet: Bilder, Audio, Textdokumente, PDFs usw. - Untertyp 1:
TEXT
-
Untertyp 1 besitzt den Alias
TEXT
, welcher in Deklarationen und Definitionen verwendet werden kann. Zum BeispielBLOB SUB_TYPE TEXT
. Dies ist ein spezieller Untertyp zum Speichern von Textdaten, die zu lang für die üblichen Zeichenketten sind. Ein Zeichensatz (CHARACTER SET
) kann definiert werden, sofern dieser vom Standard der Datenbank abweicht. Seit Firebird 2.0 ist die Angabe derCOLLATE
-Klausel ebenfalls gültig.
Weiterhin ist es möglich eigene Untertypen zu definieren, wofür der Zahlenbereich von -1 bis -32.768 reserviert ist. Selbstdefinierte Untertypen im positiven Zahlenbereich werden nicht unterstützt, da Firebird diese, von 2 aufwärts, für interne Untertypen in den Metadaten verwendet.
3.6.2. BLOB
Specifics
Die Maximalgröße eines BLOB
-Feldes ist auf 4GB begrenzt, unanhängig vom darunterliegenden 32- oder 64-Bit-Server.
(Die internen Strukturen im Zusammenhang mit BLOB
s nutzen ihre eigenen 4-Byte-Zähler.)
Für Seitengrößen (page size) von 4 KB (4096 Bytes) ist die Maximalgröße geringer — etwas weniger als 2 GB.
Text-BLOBs beliebiger Länge und jeder Zeichensatz — inklusive Multi-Byte — können Operanden für praktisch jede Art von Statement oder interne Funktionen sein. Die folgenden Operatoren werden vollständig unterstützt:
= |
(Zuweisung) |
=, <>, <, ⇐, >, >= |
(Vergleich) |
|| |
(Verkettung) |
|
|
|
|
|
|
Teilunterstützt:
-
Ein Fehler wird ausgeworfen, wenn das Suchkritierium größer als 32 KB ist:
STARTING [WITH]
,LIKE
,CONTAINING
-
Aggregations-Klauseln funktionieren nicht auf Basis der eigentlichen Feldwerte, jedoch mit den BLOB-IDs. Dementsprechend gibt es ein paar Macken:
SELECT DISTINCT
Gibt fälschlicherweise mehrere NULL-Werte zurück, sofern diese vorhanden sind.
ORDER BY
—
-
Verkettet die gleichen Zeichenketten, wenn sie aufeinanderfolgen, jedoch nicht, wenn sie voneinader entfernt liegen.
BLOB
-Speicherung-
Standardmäßig wird ein regulärer Datensatz für jeden BLOB erstellt und in der Datenseite (data page), die hierfür zugewiesen wurde, gespeichert. Passt das gesamte BLOB in diese Seite, spricht man auch von einem Level 0 BLOB. Die Seitenzahl dieses speziellen Datensatzes wird im Tabellendatensatz gespeichert und belegt 8 Bytes.
-
Passt das BLOB hingegen nicht in die Datenseite, wird der Inhalt auf separate Seiten verteilt, die exklusive hierfür belegt werden (BLOB-Seiten). Die Seitennummern werden in den BLOB-Datensatz geschrieben. Hierbei spricht man von einem Level 1 BLOB.
-
Falls das Array der Seitenzahlen, die die BLOB-Daten enthalten nicht in die Datenseite passen, wird diese Array auf separate BLOB-Seiten verteilt, während die Seitenzahlen dieser Seiten in den BLOB-Datensatz geschrieben werden. Hierbei spricht man von einem Level 2 BLOB.
-
Level größer als 2 werden nicht unterstützt.
3.6.3. ARRAY
Type
Die Unterstützung von Arrays im Firebird DBMS ist eine Abkehr vom traditionellen relationalen Modell. Unterstützung von Arrays im DBMS könnte es einfacher machen, Datenverarbeitungsaufgaben mit großen Mengen von ähnlichen Daten zu lösen.
Array werden in Firebird mittels speziellen BLOB
-Typen gespeichert.
Arrays können ein- und multidimensional sein.
Sie dürfen aus beliebigen Datentypen, außer BLOB
und ARRAY
bestehen.
CREATE TABLE SAMPLE_ARR (
ID INTEGER NOT NULL PRIMARY KEY,
ARR_INT INTEGER [4]
);
Dieses Beispiel erstellt eine Tabelle mit einem Array-Feld, welches 4 Ganzzahlenelemente (Integer) enthält. Die Indizes werden von 1 bis 4 gezählt.
Festlegen expliziter Dimensionsgrenzen
Standardmäßig sind die Indizes 1-basierend — Zählung begind bei 1. Um die Ober- und Untergrenzen explizit festzulegen, nutzen Sie folgende Syntax:
'[' <lower>:<upper> ']'
Weitere Dimensionen hinzufügen
Eine neue Dimension wird nach dem Komma in der Syntax hinzugefügt. In diesem Beispiel erstellen wir eine Tabelle mit einem zweidimensionalen Array, mit der unteren Indizegrenze von 0 beginnend:
CREATE TABLE SAMPLE_ARR2 (
ID INTEGER NOT NULL PRIMARY KEY,
ARR_INT INTEGER [0:3, 0:3]
);
Das DBMS bietet, im Vergleich mit anderen Sprachen oder Werkzeugen, nicht viele Möglichkeiten zur Arbeit mit Array-Inhalten.
Die Datenbank employee.fdb
, die im Unterverzeichnis ../examples/empbuild
jedes Firebird-Pakets gefunden werden kann, beinhaltet eine beispielhafte Gespeicherte Prozedur (stored procedure), die einfache Routinen im Umgang mit Arrays zeigt:
PSQL Quelltext für SHOW_LANGS
, eine Prozedur zur Arbeit mit Arrays
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
/* PRINTS 'NULL' INSTEAD OF BLANKS */
LANGUAGES = 'NULL';
I = I +1;
SUSPEND;
END
END
Reichen die beschriebenen Features für Ihre Aufgaben aus, können Sie darüber nachdenken, Arrays in Ihrem Projekt zu verwenden. Derzeit planen die Firebird-Entwickler keine Verbesserungen im Bereich Arrays.
3.7. Spezialdatentypen
“Spezial”-Datentypen …
3.7.1. SQL_NULL
-Datentyp
Der Datentyp SQL_NULL
besitzt keinen Wert, sondern nur einen Status: null (NULL
) oder nicht null (NOT NULL
).
Er ist nicht verfügbar als Datentyp zur Deklaration von Tabellenfeldern, PSQL-Variablen oder als Parameterbeschreibung.
Dieser Datentyp wurde eingeführt, um untypiserte Parameter in Ausdrücken mit dem IS NULL
-Prädikat zu verwenden.
Ein Auswertungsproblem tritt auf, wenn optionale Filter verwendet werden, um Abfragen der folgenden Art zu erstellen:
WHERE col1 = :param1 OR :param1 IS NULL
Nach der Verarbeitung auf API-Ebene, sieht die Abfrage folgendermaßen aus:
WHERE col1 = ? OR ? IS NULL
Dies ist der Fall, wenn ein Entwickler eine SQL-Abfrage schreibt und der Auffassung ist, dass sich :param1
wie eine Variable verhält, sodass er diese auf zwei Arten verwenden kann.
Jedoch erhält sich die Abfrage auf der API-Ebene zwei unabhängige Parameter.
Der Server kann den Typen des zweiten Parameters nicht ermitteln, da dieser in Verbindung mit IS NULL
steht.
Der Datentyp SQL_NULL
löst dieses Problem.
Wannimmer die Datenbank auf das '? IS NULL'
-Prädikat in einer Abfrage stößt, weist sie dem Parameter den Typ SQL_NULL
zu, was wiederum dazu führt, dass dieser Parameter nur auf “Nullbarkeit” geprüft werden muss und der Datentyp bzw. der Wert nicht wichtig ist.
Das folgende Beispiel demonstriert die praktische Verwendung.
Es verwendet 2 benannte Parameter — sagen wir, :size
und :colour
— welche, im Beispiel, zwei Werte vom Bildschirmtext oder Drop-Down-Listen erhalten.
Jeder dieser Parameter korrospondiert mit zwei Parametern 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 verstehen was hier passiert, muss der Leser mit der Firebird-API sowie der Übergabe von Parametern in der XSQLVAR-Struktur vertraut sein — was unter der Haube passiert, ist nicht von Belang, sofern keine Treiber oder Anwendungen geschrieben werden, die die “nackte” API verwenden.
Die Anwendung übergibt die parameteresierte Abfrage an den Server in der üblichen ?
-Form.
Paare “identischer” Parameter können nicht in einem zusammengeführt werden, somit werden für zwei optionale Filter, beispielsweise, 4 Parameter benötigt: einer für jede ?
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 ihrer speziellen Verbindung zu dem ersten bzw. dritten Parameter: hierfür ist allein die Anwendung verantwortlich.
Sobald die Werte für Größe (size) und Farbe (colour) festgelegt wurden (oder auch nicht), und die Abfrage ausgeführt wurde, wird jedes Paar der XSQLVARs folgendermaßen ausgefüllt:
- Benutzer hat einen Wert angegeben
-
Erster Parameter (vergleiche Werte): Lege
*sqldata
auf den übergebenen Wert fest und*sqlind
auf0
(fürNOT NULL
)Zweiter Parameter (teste auf
NULL
): Legesqldata
aufnull
fest (null-Zeiger, nicht SQLNULL
) und*sqlind
auf0
(fürNOT NULL
) - Benutzer hat das Feld leer gelassen
-
Beide Parameter: Setze
sqldata
aufnull
(null-Zeiger, nicht SQLNULL
) und*sqlind
auf-1
(repräsentiertNULL
)
In anderen Worten: Der Parameter zum Vergleich des Wertes wird wie üblich gesetzt.
Der SQL_NULL
-Parameter wird genauso verwendet, mit dem Unterschied, dass sqldata
über die gesamte Zeit hinweg null
bleibt.
3.8. Datentyp-Konvertierungen
Wenn Sie einen Ausdruck erstellen oder eine Operation spezifizieren, sollte das Ziel sein, kompatible Datentypen als Operanden zu verwenden. Wenn die Notwendigkeit besteht, verschiedene Datentypen zu verwenden, müssen Sie einen Weg finden, diese inkompatiblen Typen vor der eigentlichen Operation zu konvertieren. Wenn Sie mit Dialekt 1 arbeiten, kann dies zum Problem werden.
3.8.1. Explizite Datentyp-Konvertierung
Die CAST
-Funktion ermöglicht die explizite Konvertierung zwischen diversen Datentypen.
CAST ( { <value> | NULL } AS <data_type>) <data_type> ::= <sql_datatype> | [TYPE OF] domain | TYPE OF COLUMN relname.colname
Casting zu einer Domain
Beim Ausführen eines Cast zu einer Domain, werden alle Constraints berücksichtigt, die hierfür deklariert wurden, z.B. NOT NULL
- oder CHECK
-Constraints.
Wenn der Wert (value) diese Prüfung nicht besteht, schlägt der Cast fehl.
Wird TYPE OF
zusätzlich angegeben — Umwandlung auf den Basistyp — , werden alle Domain-Contraints während des Cast ignoriert.
Wird TYPE OF
mit einem Zeichentyp (CHAR/VARCHAR
) verwendet, werden der Zeichensatz und die Collation beibehalten.
Casting zu TYPE OF COLUMN
Werden Operanden auf den Datentyp einer Spalte konvertiert, kann die angegebene Spalte aus einer Tabelle oder View stammen.
Nur der Typ der Spalte selbst wird verwendet. Für Zeichentypen wird der Zeichensatz bei der Konvertierung inkludiert, jedoch nicht die Collation. Die Constraints und Vorgabewerte der Quellspalte werden nicht angewandt.
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;
Mögliche konvertierungen für die CAST
-Funktion
von Datentyp | zu Datentyp |
---|---|
Numerische Typen |
Numerische Typen, |
|
|
|
|
|
|
|
|
Beachten Sie, dass Informationsteile möglicherweise verloren gehen.
Zum Beispiel geht der Zeitteil bei der Konvertierung eines |
Literalformate
Für den Cast von String-Datentypen zu DATE
-, TIME
- oder TIMESTAMP
-Datentypen, müssen Sie eines der vordefinierten Datum- bzw. Zeit-Literale (vgl. Tabelle 3.7) oder ein gültiges Datum-Zeit-Literal-Format verwenden:
<timestamp_format> ::= { 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> ::= { 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 |
---|---|
datetime_literal |
Datum- und Zeit-Literal |
time_literal |
Zeit-Literal |
date_literal |
Datum-Literal |
YYYY |
vierstelliges Jahr |
YY |
zweistelliges Jahr |
MM |
Monat. Dieser kann aus 1 oder 2 Stellen bestehen (1-12 oder 01-12). Sie können auch die mit drei Buchstaben abgekürzte Form oder den vollen Monatsnamen in englisch angeben. Dies berücksichtigt keine Groß- und Kleinschreibung. |
DD |
Tag. Kann aus 1 oder 2 Stellen bestehen (1-31 oder 01-31) |
HH |
Stunde. Kann aus 1 oder 2 Stellen bestehen (0-23 oder 00-23) |
mm |
Minute. Kann aus 1 oder 2 Stellen bestehen (0-59 oder 00-59) |
SS |
Sekunde. Kann aus 1 oder 2 Stellen bestehen (0-59 oder 00-59) |
NNNN |
Zehntausendstel Sekunde. Kann aus 1 bis 4 Stellen bestehen (0-9999) |
p |
Separator, jedes gültige Zeichen. Führende und nachgestellte Leerzeichen werden ignoriert. |
Literal |
Beschreibung |
Datentyp |
|
---|---|---|---|
Dialekt 1 |
Dialekt 3 |
||
|
Aktuelles Datum und Zeit |
|
|
|
Aktuelle Datum |
|
|
|
Heutiges Datum + 1 (Tag) |
|
|
|
Heutiges Datum - 1 (Tag) |
|
|
Die Nutzung des Jahres in vierstelliger Schreibweise — |
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 with current year
cast('04.12' as date) as d7,
-- MM/DD with current year
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
Kurzschreibweisen für Casts von Datums- und Zeit-Datentypen
Firebird erlaubt die Verwendung von Kurzschreibweisen (“C-Stil”) für die Konvertierung von Strings zu DATE
-, TIME
- und TIMESTAMP
-Typen.
<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 Kurzschreibweisen werden direkt während des Parsens ausgewertet, so als ob das Statement bereits für die Ausführung vorbereitet wäre.
Dadurch hat beispielsweise Benötigen Sie hingegen eine Möglichkeit, die die Zeit bei jeder Ausführung neu ermittelt, müssen Sie die vollständige
|
3.8.2. Implizite Datentyp-Konvertierung
Implizite Datenkonvertierung ist in Dialekt 3 nicht möglich — die CAST
-Funktion wird fast immer benötigt, um Datentyp-Probleme zu vermeiden.
In Dialekt 1 wird in vielen Fällen ein Datentyp implizit in einen anderen gewandelt, ohne die CAST
-Funktion verwenden zu müssen.
Beispielsweise ist die folgende Klausel in einem SELECT
-Statement in Dialekt 1 gültig:
WHERE DOC_DATE < '31.08.2014'
und der String-Typ wird implizit in den Datums-Datentyp gewandelt.
In Dialekt 1 ist das Vermischen von Ganzzahlendaten und numerischen Zeichenketten grundsätzlich möglich, da der Parser versuchen wird, die Zeichenketten implizit zu konvertieren. Beispielsweise wird:
2 + '1'
korrekt ausgeführt.
In Dialekt 3 wird dieser Ausdruck einen Fehler ausgeben.
Somit sind Sie gezwungen, einen CAST
-Ausdruck zu erstellen:
2 + CAST('1' AS SMALLINT)
Die Ausnahme bildet hier die String-Verkettung.
Implizite Konvertierung während der String-Verkettung
Werden mehrere Elemente miteinander verkettet, werden alle nicht-Zeichen-Daten unter der Hand zu einer Zeichenkette konvertiert, sofern möglich.
SELECT 30||' days hath September, April, June and November' CONCAT$
FROM RDB$DATABASE;
CONCAT$
------------------------------------------------
30 days hath September, April, June and November
3.9. Benutzerdefinierte Datentypen — Domains
In Firebird wurde das Konzept “benutzerdefinierter Datentypen” in Form von Domains implementiert. Beim Erstellen einer Domain wird nicht wirklich ein neuer Datentyp generiert. Eine Domain kapselt vielmehr einen bestehenden Datentyp mit diversen Attributen und macht diese “Kaspel” für verschiedene Anwendungsbereiche in der Datenbank verfügbar. Wenn mehrere Tabellen Spalten benötigen, die die gleichen oder nahezu gleichen Eigenschaften haben sollen, macht die Verwendung von Domains Sinn.
Die Verwendung von Domains ist nicht auf die Spaltendefinition in Tabellen oder Views begrenzt. Sie können auch als Übergabe- und Rückgabeparameter sowie Variablen in PSQL-Code verwendet werden.
3.9.1. Domain-Eigenschaften
Eine Domain-Definition beinhaltet benötigte sowie optionale Eigenschaften. Der Datentyp ist ein benötigtes Attribut. Optionale Eigenschaften sind:
-
ein Vorgabewert
-
erlauben oder verbieten von
NULL
-
CHECK
Constraints -
Zeichensatz (für Textdatentypen und Text-BLOB-Felder)
-
Collation (für Textdatentypen)
CREATE DOMAIN BOOL3 AS SMALLINT
CHECK (VALUE IS NULL OR VALUE IN (0, 1));
Explicite Datentyp-Konvertierung für die Beschreibung der Unterschiede im Mechanismus der Datenkonvertierung, wenn Domains für die TYPE OF
- und TYPE OF COLUMN
-Modifizierer verwendet werden.
3.9.2. Domain-Überschreibung
Während des Festlegens der Domain-Definitionen, ist es möglich einige geerbte Eigenschaften zu überschreiben. Tabelle 3.9 fasst die Regeln hierfür zusammen.
Eigenschaft | Überschreiben? | Hinweis |
---|---|---|
Datentyp |
Nein |
|
Vorgabewert |
Ja |
|
Zeichensatz |
Ja |
Dies kann auch verwendet werden, um die Vorgabewerte der Datenbank für die Spalte wiederherzustellen. |
Sortiermethode |
Ja |
|
|
Ja |
Zum Hinzufügen von neuen Kriterien, können Sie die korrospondierenden |
|
Nein |
Häufig ist es besser Domains nullbar in ihren Definitionen zu lassen und erst beim Erstellen der Spaltedefinition über |
3.9.3. Domains erstellen und verwalten
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 Data Definition Language (DDL).
Ändern einer Domain
Zum Ändern der Domain-Eigenschaften, nutzen Sie die Anweisung ALTER DOMAIN
.
Mit dieser Anweisung können Sie
-
die Domain umbenennen
-
den Datentyp ändern
-
den derzeitigen Vorgabewert ändern
-
einen neuen Vorgabewert festlegen
-
einen vorhandenen
CHECK
-Constraint löschen -
einen neuen
CHECK
-Constraint hinzufügen
ALTER DOMAIN name [{TO <new_name>}] [{SET DEFAULT {<literal> | NULL | <context_var>} | DROP DEFAULT}] [{ADD [CONSTRAINT] CHECK (<dom_condition>) | DROP CONSTRAINT}] [{TYPE <datatype>}]
Wenn Sie vorhaben eine Domain zu ändern, müssen Sie die vorhandenen Abhängigkeiten berücksichtigen: wurden Tabellenspalten, Variablen, Übergabe- und/oder Rückgabeparameter mit dieser Domain in PSQL deklariert? Wenn Sie Domains voreilig ändern, ohne diese eingehend zu überprüfen, kann es passieren, dass Ihr Code anschließend nicht mehr läuft!
Wenn Sie Datentypen einer Domain ändern, dürfen Sie keine Konvertierungen durchführen, die zu Datenverlust führen können.
Zum Beispiel sollten Sie vorab prüfen, ob das Konvertieren von |
ALTER DOMAIN
im Abschnitt Data Definition Language (DDL).
Löschen einer Domain
Die Anweisung DROP DOMAIN
löscht eine Domain aus der Datenbank, sofern diese nicht von anderen Datenbankobjekten verwendet wird.
DROP DOMAIN name
Jeder mit der Datenbank verbundene Benutzer kann eine Domain löschen. |
DROP DOMAIN Test_Domain
DROP DOMAIN
im Abschnitt Data Definition Language (DDL).
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. - Konstanten
-
Eine Zahl; oder eine Zeichenkette, die in Apostrophs eingeschlossen wird.
- 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 '25.12.2016 15:30:35', die zu einem Datum und/oder einer Zeit aufgelöst werden können.
- Kontextvariablen
-
Eine 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 '25.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
- undVARCHAR
-Datentypen angewendet werden kann, um die Collation für String-Vergleiche festzulegen. NEXT VALUE FOR Sequenz
-
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 aufwärts, können Stringliterale in hexadezimaler Schreibweise eingegeben werden, die sogenannten “Binary Strings”.
Jedes Paar hexadezimaler Stellen definiert ein Byte der Zeichenkette.
Zeichenketten die in dieser Form eingegeben werden, besitzen den Zeichensatz OCTETS
als Standard.
Die Introducer-Syntax kann auch genutzt werden um zu erzwingen, dass die Zeichenkette 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, a 6-byte 'binary' string
select _ascii x'4E657276656E' from rdb$database
-- liefert 'Nerven' (same string, now interpreted as ASCII text)
select _iso8859_1 x'53E46765' from rdb$database
-- liefert 'Säge' (4 chars, 4 bytes)
select _utf8 x'53C3A46765' from rdb$database
-- liefert 'Säge' (4 chars, 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. |
Introducer-Syntax für String-Literale
Bei Bedarf kann ein String-Literal einem Zeichensatznamen vorangestellt werden, dem ein Unterstrich “_” vorangestellt ist. Dies wird als Introducer-Syntax bezeichnet. Sein Ziel ist es, die Engine darüber zu informieren, wie man den eingehenden String interpretiert und speichert.
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.
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 als <hexdigit> <hexdigit> ::= eins aus 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 Verknüpfen 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 | Zweick | 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 |
---|---|---|
|
Ist gleich, ist identisch mit |
1 |
|
Ist ungleich zu |
1 |
|
Ist größer als |
1 |
|
Ist kleiner als |
1 |
|
Ist größer oder gleich als |
1 |
|
Ist kleiner oder gleich als |
1 |
|
Ist nicht gößer als |
1 |
|
Ist nicht kleiner als |
1 |
Diese Gruppe enthält außerdem die Vergleichsprädikate BETWEEN
, LIKE
, CONTAINING
, SIMILAR TO
, IS
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
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.
NEXT VALUE FOR Sequenzname
NEW.CUST_ID = NEXT VALUE FOR CUSTSEQ;
Anders als |
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 …
Wenn diese Variante verwendet wird, wird test-expr mit expr 1, expr 2 etc. verglichen, bis ein Treffer gefunden und das passende Ergebnis zurückgegeben wird.
Wenn kein passender Treffer vorhanden ist, wird defaultresult aus der optionalen ELSE
-Klausel zurückgegeben, andernfalls NULL
.
Der Trefferwahl funktioniert identisch zum ‘=
’-Operator.
Daher gilt, wenn test-expr gleich NULL
ist, wird kein Treffer für expr ermittelt, nicht einmal wenn dieser zu NULL
aufgelöst wird.
Das zurückgegebene Ergebnis muss kein literaler Wert sein: Es kann ein Feld oder ein Variablenname, ein Ausdruck oder 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
kein Wert in SQL, sondern ein Status der anzeigt, dass der Wert des Elements entweder unbekannt (engl.
unknown) ist oder nicht existiert.
Es ist weder null, noch void, noch ein “leerer String”, und es verhält sich auch nicht wie ein anderer Wert.
Wenn Sie NULL
in numerischen, String- oder Datums/Zeit-Ausdrücken verwenden, wird das Ergebnis immer NULL
sein.
Verwenden Sie NULL
in logischen (Boolean) Ausdrücken, hängt das Ergebnis von der Art der Operation ab und anderen partizipierenden Werten.
Wenn Sie einen Wert mit NULL
vergleichen, wird das Ergebnis unknown sein.
Zu beachten
|
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
Bis einschließlich Firebird 2.5.x existiert keine Implementierung für logische (Boolean) Datentypen — diese gibt es erst seit Firebird 3. Jedoch gibt es logische Ausdrücke (Prädikate), die true, false oder unknown zurückgeben können.
(1 = NULL) or (1 <> 1) -- liefert NULL
(1 = NULL) or (1 = 1) -- liefert TRUE
(1 = NULL) or (1 = NULL) -- liefert NULL
(1 = NULL) and (1 <> 1) -- liefert FALSE
(1 = NULL) and (1 = 1) -- liefert NULL
(1 = NULL) and (1 = NULL) -- liefert 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 korrelierend sein. Sie ist korellierend, wenn die Hauptafrage und die Unterabfrage voneinander abhängig sind. Um einen Datensatz in der Unterabfrage zu verarbeiten, ist es notwendig einen Datensatz in der Hauptabfrage zu holen; beispielsweise hängt die Unterabfrage 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. Behauptungen
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;
Auf der anderen Seite kann
ptrtype
aufNULL
getestet werden; mit dem Ergebnis, dass die kein Vergleichstest ist:SELECT * FROM Printer WHERE ptrtype IS NULL AND price < 300;
— siehe
IS [NOT] NULL
.
Hinweis zu String-Vergleichen
Werden |
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 BETWEEN
-Prädikat prüft, ob ein Wert innerhalb eines angegebenen Bereichs zweier Werte liegt.
(NOT BETWEEN
prüft, ob dieser Wert außerhalb der beiden Grenzen liegt.)
Die Operanden des BETWEEN
-Prädikates sind zwei Argumente kompatibler Datentypen.
Anders als in anderen DBMS ist das BETWEEN
-Prädikat nicht symmetrisch — ist der kleinere Wert nicht das erste Argument, wird immer FALSE zurückgegeben.
Die Suche ist inkludiert (die Werte beider Argumente werden in die Suche eingebunden).
Anders ausgedrückt bedeutet dies, dass das BETWEEN
-Prädikat auch anders geschrieben werden kann:
<value> >= <value_1> AND <value> <= <value_2>
Wird BETWEEN
in Suchkriterien für DML-Abfragen verwendet, kann der Firebird-Optimizer einen Index auf der Suchspalte nutzen, sofern verfügbar.
SELECT *
FROM EMPLOYEE
WHERE HIRE_DATE BETWEEN date '01.01.1992' 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 LIKE
-Prädikat vergleicht zeichenbasierte Ausdrücke mit dem im zweiten Ausdruck definierten Muster.
Groß- und Kleinschreibung bzw. Akzent-Sensitivität für den Vergleich wird durch die zugrunde liegende Collation bestimmt.
Eine Collation kann für jeden Operanden angegeben werden, wenn erforderlich.
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. -
Suche nach Mitarbeitern deren Namen aus 5 Buchstaben bestehen, die mit “Sm” beginnen und mit “th” enden. Das Prädikat wird wahr für die 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. -
Suche nach Tabellen, die das Unterstrich-Zeichen im Namen besitzen. Das Zeichen ‘
#
’ wird als Escape-Zeichen definiert: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 innerhalb von Zeichenketten oder zeichenkettenähnlichen Datentypen nach der Buchstabenfolge, die im Argument angegeben wurde.
Es kann für alphanumerische (zeichenkettenähnliche) Suchen auf Zahlen und Daten genutzt werden.
Eine Suche mit CONTAINING
unterscheidet nicht nach Groß- und Kleinschreibung.
Wird jedoch eine akzentsensitive Collation verwendet, ist auch die Suche akzentsensitiv.
Wenn CONTAINING
als Suchkriterium in DML-Abfragen verwendet wird, kann der Firebird-Optimizer einen Index der Suchspalte nutzen, sofern ein passender existiert.
-
Suche nach Projekten, deren Namen die Zeichenkette “Map” enhalten:
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 einer eckigen Klammer beginnt, wird alles, was folgt, von der Klasse ausgeschlossen. Alle anderen Zeichen entsprechen:
'Framboise' similar to 'Fra[^ck-p]boise' -- false
'Framboise' similar to 'Fr[^a][^a]boise' -- false
'Framboise' similar to 'Fra[^[:DIGIT:]]boise' -- true
Wird die eckige Klammer nicht am Anfang der Reihe platziert, enthält die Klasse alles vor dieser, mit Ausnahme der Elemente die nach der Klammer vorkommen:
'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, direkt von einem weiteren Zeichen oder Klasse gefolgt, gibt an, dass das folgende Element gar nicht oder mehrmals vorkommen darf:
'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 Plus-Zeichen, direkt von einem weiteren Zeichen oder Klasse gefolgt, gibt an, dass das folgende Element einmalig oder mehrmals vorkommen darf:
'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
Folgt eine Zahl in Klammern auf ein Zeichen oder eine Klasse, muss letzteres genau so oft wie angegeben vorkommen:
'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
Wird eine Zahl von einem Komma gefolgt, bedeutet dies, dass das Element mindestens so oft wie angegeben vorkommen muss:
'Limone' similar to 'Li{2,}mone' -- false
'Limone' similar to 'Li{1,}mone' -- true
'Limone' similar to 'Li[nezom]{2,}' -- true
Wenn die Klammern zwei Zahlen enthalten, die mittels Komma getrennt sind, die zweite Zahl nicht kleiner als die erste ist, muss das Element mindestens so oft wie die erste Zahl vorkommen und maximal so oft wie in der zweiten Zahl angegeben:
'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
Soll eine Übereinstimmung auf Sonderzeichen innerhalb eines regulären Ausdrucks geprüft werden, muss dieses Zeichen escaped werden. Es gibt kein Standard-Escape-Zeichen; stattdessen definiert der Benutzer eines, wenn dies benötigt wird:
'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] 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 beinhaltet die, die Unterabfragen nutzen um Werte für alle möglichen Arten von Behauptungen zu prüfen.
Existenzprädikate werden so genannt, da sie verschiedene Methoden verwenden, um auf die Exiszenz oder nicht-Existenz von Behauptungen zu prüfen.
Die Prädikate geben TRUE
zurück, wenn die Existenz oder nicht-Existenz bestätigt wurde, andernfalls `FALSE
.
EXISTS
DSQL, PSQL, ESQL
[NOT] EXISTS (<select_stmt>)
Das Prädikat EXISTS
nutzt einen Unterabfrage-Ausdruck als Argument.
Es gibt TRUE
zurück, wenn die Unterabfrage mindestens einen Datensatz zurückgibt;
andernfalls gibt es FALSE
zurück.
NOT EXISTS
gibt FALSE
zurück, wenn die Unterabfrage mindestens eine Datenzeile zurückgibt;
es gibt andernfalls 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.
Hinweis
Wenn es im Bereich des Möglichen liegt, 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 SINGULAR
-Prädikat verwendet eine Unterabfrage als Argument und gibt True zurück, wenn diese exakt eine Datenzeile zurückgibt;
andernfalls wird das Prädikat zu False aufgelöst.
Die Unterabfrage kann mehrere Ausgabespalten beinhalten, da die Zeilen ja nicht wirklich ausgegeben werden.
Sie werden nur auf (einzigartige) Existenz geprüft.
Der Kürze halbe, wird häufig nur ‘SELECT *
’ verwendet.
Das Prädikat SINGULAR
kann nur zwei Werte zurückgeben: TRUE
oder FALSE
.
Finde 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 Unterabfrage-Ausdrücken machen es Quantifizierer-Prädikate möglich einzelne Werte mit den Ergebnissen von Unterabfragen zu vergleichen; sie besitzen 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. Statements der Data Definition (DDL)
DDL ist die Untermenge der SQL-Sprache von Firebird zum Festlegen von Datendefinitionen. DDL-Anweisungen werden zum Erstellen, Ändern und Löschen von Datenbankobjekten verwendet, die von Benutzern erstellt wurden. Wenn eine DDL-Anweisung commited wird, werden die Metadaten für die Objekte 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 löschen. Außerdem wird erläutert, wie Sie eine Datenbank auf zwei verschiedene Arten sichern können und wie Sie die Datenbank in den “kopiersicheren” Modus umwandeln können, um ein externes Backup sicher durchzuführen.
5.1.1. CREATE DATABASE
Erstellen einer neuen Datenbank
DSQL, ESQL
CREATE {DATABASE | SCHEMA} <filespec> [USER 'username' [PASSWORD 'password']] [PAGE_SIZE [=] size] [LENGTH [=] num [PAGE[S]] [SET NAMES 'charset'] [DEFAULT CHARACTER SET default_charset [COLLATION collation]] -- not supported in ESQL [<sec_file> [<sec_file> ...]] [DIFFERENCE FILE 'diff_file'] -- not supported in ESQL <filespec> ::= "'" [server_spec]{filepath | db_alias} "'" <server_spec> ::= servername[/{port|service}]: | \\servername\ <sec_file> ::= FILE 'filepath' [LENGTH [=] num [PAGE[S]] [STARTING [AT [PAGE]] pagenum]
Parameter | Beschreibung |
---|---|
filespec |
Dateispezifikation für primäre Datenbankdatei |
server_spec |
Spezifikation des Remoteservers im TCP / IP- oder Windows-Netzwerkstil. Enthält optional eine Portnummer oder einen Servicenamen |
filepath |
Vollständiger Pfad und Dateiname einschließlich seiner Erweiterung. Der Dateiname muss gemäß den Regeln des verwendeten Plattformdateisystems angegeben werden. |
db_alias |
Datenbank-Alias, der zuvor in der Datei |
servername |
Hostname oder IP-Adresse des Servers, auf dem die Datenbank erstellt werden soll. |
username |
Benutzername des Eigentümers der neuen Datenbank. Es kann aus bis zu 31 Zeichen bestehen. Groß- / Kleinschreibung ist vernachlässigbar. |
password |
Kennwort des Benutzers oder Datenbankeigentümers. Die maximale Länge beträgt 31 Zeichen; es werden jedoch nur die ersten 8 Zeichen berücksichtigt. Groß- / Kleinschreibung beachten. |
size |
Seitengröße der Datenbank in Bytes. Mögliche Werte sind 4096 (Standard), 8192 und 16384 |
num |
Maximale Größe des primären Datenbankdatei, oder einer sekundären, in Seiten (pages). |
charset |
Legt den Zeichensatz der Verbindung fest, die von einem Client verwendet wird, nachdem die Datenbank erfolgreich erstellt wurde. Einfache Anführungszeichen sind zu verwenden. |
default_charset |
Legt den Standardzeichensatz für String-Datentypen fest. |
collation |
Standard-Collation für den Standardzeichensatz |
sec_file |
Dateispezifikation für eine sekundäre Datei |
pagenum |
Startseitenzahl für eine sekundäre Datenbankdatei |
diff_file |
Dateipfad und -name für DIFFERENCE-Dateien (.delta-Dateien) |
Das Statement CREATE DATABASE
erstellt eine neue Datenbank.
Sie können sowohl CREATE DATABASE
wie auch CREATE SCHEMA
verwenden.
Dies sind Synonyme füreinander.
Eine Datenbank besteht aus einer oder mehrerer Dateien. Die erste (Haupt-) Datei wird auch als die primäre Datei bezeichnet, folgende als sekundäre Datei[en].
Multi-Datei-Datenbanken
Heutzutage gelten Multi-File-Datenbanken als rückschrittlich. Es ist sinnvoll, Datenbanken mit mehreren Dateien auf alten Dateisystemen zu verwenden, bei denen die Größe einer Datei begrenzt ist. Sie können beispielsweise keine Datei mit mehr als 4 GB auf FAT32 erstellen. |
Die primäre Dateispezifikation ist der Name der Datenbankdatei und ihrer Erweiterung mit dem vollständigen Pfad zu den Regeln des verwendeten Betriebssystemplattformdateisystems. Die Datenbankdatei darf zum Zeitpunkt der Datenbankerstellung nicht vorhanden sein. Wenn dies der Fall ist, erhalten Sie eine Fehlermeldung und die Datenbank wird nicht erstellt.
Wenn der vollständige Pfad zur Datenbank nicht angegeben ist, wird die Datenbank in einem der Systemverzeichnisse erstellt. Das bestimmte Verzeichnis hängt vom Betriebssystem ab. Geben Sie daher immer den absoluten Pfad an, wenn Sie entweder die Datenbank oder einen Alias dafür erstellen, es sei denn, Sie haben einen guten Grund, diese Situation zu bervorzugen.
Einen Datenbank-Alias verwendenUsing a Database Alias
Sie können Aliasnamen anstelle des vollständigen Pfads zur primären Datenbankdatei verwenden.
Aliase sind in der Datei aliases.conf
im folgenden Format definiert:
alias = filepath
Eine Datenbank remote erstellen
Wenn Sie eine Datenbank auf einem Remoteserver erstellen, sollten Sie die Spezifikation des Remoteservers angeben. Die Spezifikation des Remoteservers hängt vom verwendeten Protokoll ab. Wenn Sie das TCP / IP-Protokoll zum Erstellen einer Datenbank verwenden, sollte die primäre Dateispezifikation wie folgt aussehen:
_servername_[/{_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 wie folgt aussehen:
\\servername\{filepath | db_alias}
Optionale Parameter für CREATE DATABASE
USER
undPASSWORD
-
Klauseln zur Angabe des Benutzernamens bzw. des Passworts eines vorhandenen Benutzers in der Sicherheitsdatenbank
security2.fdb
. Sie müssen den Benutzernamen und das Kennwort nicht angeben, wenn die UmgebungsvariablenISC_USER
undISC_PASSWORD
festgelegt sind. Der Benutzer, der beim Erstellen der Datenbank angegeben wird, wird ihr Eigentümer sein. Dies ist wichtig, wenn Sie Datenbank- und Objektberechtigungen berücksichtigen. PAGE_SIZE
-
Klausel zum Festlegen der Seitengröße der Datenbank. Diese Größe wird für die primäre Datei und alle sekundären Dateien der Datenbank festgelegt. Wenn Sie die Datenbankseitengröße unter 4.096 angeben, wird diese automatisch auf die Standardseitengröße 4.096 geändert. Andere Werte, die nicht 4.096, 8.192 oder 16.384 entsprechen, werden in den nächst kleineren unterstützten Wert geändert. Wenn die Größe der Datenbankseite nicht angegeben ist, wird der Standardwert auf 4.096 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ären und sekundären Dateien die Mindestanzahl an Seiten, die zum Speichern der Systemdaten erforderlich sind, unabhängig vom in der
LENGTH
-Klausel angegebenen Wert. Der Wert derLENGTH
wirkt sich nicht auf die Größe der einzigen (oder zuletzt in einer Datei mit mehreren Dateien) Datei aus. Die Datei wird bei Bedarf automatisch vergrößert. SET NAMES
-
Klausel, die den Zeichensatz der Verbindung angibt, die verfügbar ist, nachdem die Datenbank erfolgreich erstellt wurde. Der Zeichensatz
NONE
wird standardmäßig verwendet. Beachten Sie, dass der Zeichensatz in einem Apostroph-Paar eingeschlossen sein sollte (einfache Anführungszeichen). DEFAULT CHARACTER SET
-
Klausel, die den Standardzeichensatz zum Erstellen von Datenstrukturen von String-Datentypen angibt. Zeichensätze werden auf Datentypen
CHAR
,VARCHAR
undBLOB TEXT
angewendet. Der ZeichensatzNONE
wird standardmäßig verwendet. Es ist auch möglich, den StandardwertCOLLATION
für den Standardzeichensatz festzulegen, wodurch diese Sortierfolge zum Standardwert für den Standardzeichensatz wird. Der Standardwert wird für die gesamte Datenbank verwendet, es sei denn, ein alternativer Zeichensatz mit oder ohne eine angegebene Collation wird explizit für ein Feld, eine Domain, eine Variable, einen Ausdruck usw. verwendet. STARTING AT
-
Klausel, die die Datenbankseitennummer angibt, bei der die nächste sekundäre Datenbankdatei gestartet werden soll. Wenn die vorherige Datei vollständig mit Daten gemäß der angegebenen Seitennummer gefüllt ist, fügt das System neue Daten zur nächsten Datenbankdatei hinzu.
DIFFERENCE FILE
-
Klausel, die den Pfad und den Namen für das Datei-Delta angibt, das Änderungen in der Datenbankdatei speichert, nachdem es durch die Anweisung
ALTER DATABASE BEGIN BACKUP
auf den “kopiersicheren” Modus gestellt wurde. Eine detaillierte Beschreibung dieser Klausel finden Sie unterALTER DATABASE
. SET SQL DIALECT
-
Datenbanken werden standardmäßig in Dialekt 3 erstellt. Damit die Datenbank in SQL-Dialekt 1 erstellt wird, müssen Sie die Anweisung
SET SQL DIALECT 1
aus dem Skript oder der Clientanwendung, z.B. isql, ausführen, noch vor der AnweisungCREATE DATABASE
.
Beispiele zur Verwendung von CREATE DATABASE
-
Erstellen einer Datenbank in Windows auf der Festplatte D mit einer Seitengröße von 8.192. Der Besitzer der Datenbank ist der Benutzer wizard. Die Datenbank befindet sich in Dialekt 1 und verwendet als Standardzeichensatz
WIN1251
.SET SQL DIALECT 1; CREATE DATABASE 'D:\test.fdb' USER 'wizard' PASSWORD 'player' PAGE_SIZE = 8192 DEFAULT CHARACTER SET WIN1251;
-
Erstellen einer Datenbank im Linux-Betriebssystem mit einer Seitengröße von 4.096. Der Besitzer der Datenbank ist der Benutzer wizard. Die Datenbank befindet sich in Dialekt 3 und verwendet
UTF8
als Standardzeichensatz, wobeiUNICODE_CI_AI
als Standardsortierung verwendet wird.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 angegebenen Alias “test”, der zuvor in der Datei
aliases.conf
definiert wurde. Das TCP / IP-Protokoll wird verwendet. Der Besitzer der Datenbank wird der Benutzer wizard sein. Die Datenbank befindet sich in Dialekt 3 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äre Datei enthält bis zu 10.000 Seiten mit einer Seitengröße von 8.192. Sobald die primäre Datei die maximale Anzahl an Seiten erreicht hat, beginnt Firebird, Seiten der sekundären Dateitest.fdb2
zuzuweisen. Wenn diese Datei ebenfalls maximal gefüllt ist, wirdtest.fdb3
zum Empfänger aller neuen Seitenzuweisungen. Als letzte Datei hat Firebird kein Seitenlimit. Neue Zuweisungen werden so lange fortgesetzt, wie es das Dateisystem zulässt, oder bis das Speichergerät keinen freien Speicher mehr hat. Wenn für diese letzte Datei einLENGTH
-Parameter angegeben wurde, wird dieser 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äre Datei 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 oder um diese in den “kopiersicheren” Modus zu setzen
DSQL — beide Funktionen. ESQL — nur Dateireorganisation
ALTER {DATABASE | SCHEMA} [<add_sec_clause> [<add_sec_clause> ...]] [ADD DIFFERENCE FILE 'diff_file' | DROP DIFFERENCE FILE] [{BEGIN | END} BACKUP] <add_sec_clause> ::= ADD <sec_file> [<sec_file> ...] <sec_file> ::= FILE 'filepath' [STARTING [AT [PAGE]] pagenum] [LENGTH [=] num [PAGE[S]]
Mehrere Dateien können mit einer ADD-Klausel hinzugefügt werden:
Mehrere |
Parameter | Beschreibung |
---|---|
add_sec_clause |
Hinzufügen einer sekundären Datenbankdatei |
sec_file |
Dateispezifikation für sekundäre Datei |
filepath |
Vollständiger Pfad und Dateiname der Delta-Datei oder der sekundären Datenbankdatei |
pagenum |
Seitennummer, von der aus die sekundäre Datenbankdatei gestartet werden soll |
num |
Maximale Größe der sekundären Datei in Seiten |
diff_file |
Dateipfad und Name der .delta-Datei (Differenzdatei) |
Das Statement ALTER DATABASE
kann
-
sekundäre Dateien zu einer Datenbank hinzufügen
-
eine Ein-Datei-Datenbank in und aus dem Modus “kopiersicher” schalten (nur DSQL)
-
festlegen und entfernen des Pfades und der Namen der Delta-Dateien für physikalische Backups (nur DSQL)
Nur Administratoren haben die Berechtigung das Statement ALTER DATABASE
auszuführen.
Parameter für ALTER DATABASE
ADD FILE
-
Die
ADD FILE
-Klausel fügt eine sekundäre Datei zur Datenbank hinzu. Es ist erforderlich, den vollständigen Pfad zur Datei und den Namen der sekundären Datei anzugeben. Die Beschreibung für die sekundäre Datei ähnelt der für die AnweisungCREATE DATABASE
. ADD DIFFERENCE FILE
-
Die
ADD DIFFERENCE FILE
-Klausel gibt den Pfad und den Namen der Delta-Datei an, die Änderungen in der Datenbank speichert, wenn diese auf den Modus “kopiersicher” umgestellt wird. Diese Klausel fügt tatsächlich keine Datei hinzu. Sie überschreibt nur den Standardnamen und den Standardpfad der Delta-Datei. Um die vorhandenen Einstellungen zu ändern, sollten Sie die vorher angegebene Beschreibung der .delta-Datei mit derDROP DIFFERENCE FILE
-Klausel vor der Angabe der neuen Beschreibung der Deltadatei löschen. Wenn Pfad und Name der .delta-Datei nicht überschrieben werden, hat die Datei denselben Pfad und denselben Namen wie die Datenbank, jedoch mit der Dateierweiterung.delta
.Wenn nur ein Dateiname angegeben ist, wird die .delta-Datei im aktuellen Verzeichnis des Servers erstellt. Unter Windows wird dies das Systemverzeichnis sein — ein sehr unkluger Speicherort für flüchtige Benutzerdateien und entgegen den Windows-Dateisystemregeln.
DROP DIFFERENCE FILE
-
Dies ist die Klausel, die die Beschreibung (Pfad und Name) der zuvor in der
ADD DIFFERENCE FILE
-Klausel angegebenen Deltadatei löscht. Die Datei wird nicht gelöscht.DROP DIFFERENCE FILE
löscht den Pfad und den Namen der .delta-Datei aus dem Datenbank-Header. Beim nächsten Umschalten der Datenbank auf den “kopiersicheren” Modus werden die Standardwerte verwendet (d.h. derselbe Pfad und Name wie die Datenbank, jedoch mit der Erweiterung .delta). BEGIN BACKUP
-
Dies ist die Klausel, die die Datenbank in den “kopiersicheren” Modus umschaltet.
ALTER DATABASE
friert mit dieser Klausel die Hauptdatenbankdatei ein und ermöglicht die sichere Sicherung mithilfe von Dateisystemtools, 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 seiner Syntax startet eine Anweisung mit der Klausel
BEGIN BACKUP
keinen Sicherungsprozess, sondern erstellt lediglich die Bedingungen für die Ausführung einer Aufgabe, für die die Datenbankdatei nur vorübergehend schreibgeschützt sein muss. END BACKUP
-
END BACKUP
ist die Klausel, mit der die Datenbank vom “kopiersicheren” Modus in den normalen Modus umgeschaltet wird. Eine Anweisung mit dieser Klausel fügt die .delta-Datei mit der Hauptdatenbankdatei zusammen und stellt den normalen Betrieb der Datenbank wieder her. Sobald derEND BACKUP
-Prozess gestartet wird, sind die Bedingungen für das Erstellen sicherer Backups mit Dateisystemtools nicht mehr vorhanden.
Die Verwendung von Ein sicheres Backup mit dem Dienstprogramm gbak ist jederzeit möglich, wenn auch nicht empfohlen, solange sich die Datenbank im Zustand LOCKED oder MERGE befindet. |
Beispiele zur Verwendung von ALTER DATABASE
-
Hinzufügen einer sekundären Datei zur Datenbank. Sobald in der vorherigen primären oder sekundären Datei 30000 Seiten gefüllt sind, fügt die Firebird-Engine Daten zur sekundären Datei
test4.fdb
hinzu.ALTER DATABASE ADD FILE 'D:\test4.fdb' STARTING AT PAGE 30001;
-
Pfad und Name der Delta-Datei angeben:
ALTER DATABASE ADD DIFFERENCE FILE 'D:\test.diff';
-
Beschreibung der Delta-Datei löschen:
ALTER DATABASE DROP DIFFERENCE FILE;
-
Wechseln der Datenbank in den “kopiersicheren” Modus:
ALTER DATABASE BEGIN BACKUP;
-
Umschalten der Datenbank vom “kopiersicheren” Modus in den normalen Betriebsmodus:
ALTER DATABASE END BACKUP;
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 eine Verbindung herstellen.
Die Anweisung löscht die primäre Datei, alle sekundären Dateien und alle Schattendateien.
Nur Administratoren haben die notwendigen Rechte zum Ausführen der Anweisung DROP DATABASE
.
Löschen der Datenbank, mit der der Client verbunden ist.
DROP DATABASE;
5.2. SHADOW
Ein shadow ist eine exakte Seite-für-Seite-Kopie einer Datenbank. Sobald ein Shadow erstellt wurde, spiegeln sich alle Änderungen in der Datenbank sofort im Shadow wider. Wenn die primäre Datenbankdatei aus irgendeinem Grund nicht verfügbar ist, wechselt das DBMS auf den Shadow.
In diesem Abschnitt wird beschrieben, wie Sie Schattendateien erstellen und löschen.
5.2.1. CREATE SHADOW
Erstellen eines Shadows für die aktuelle Datenbank
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 dazu in Übereinstimmung mit den Regeln des Betriebssystems |
num |
Maximale Schattengröße in Seiten |
secondary_file |
Sekundäre Dateispezifikation |
page_num |
Die Nummer der Seite, auf der die sekundäre Schattendatei gestartet werden soll |
Die Anweisung CREATE SHADOW
erstellt einen neuen Shadow.
Der Schatten beginnt mit dem Duplizieren der Datenbank in dem Moment, in dem sie erstellt wird.
Es ist für einen Benutzer nicht möglich, eine Verbindung zu einem Schatten herzustellen.
Wie bei einer Datenbank kann ein Shadow eine Mehrfachdatei sein. Die Anzahl und Größe der Dateien eines Schattens hängt nicht mit der Anzahl und Größe der Dateien der Datenbank, die es beschattet, zusammen.
Die Seitengröße für Schattendateien wird auf die Größe der Datenbankseite festgelegt und kann nicht geändert werden.
Wenn ein Unglück mit der ursprünglichen Datenbank auftritt, konvertiert das System den Schatten in eine Kopie der Datenbank und wechselt zu dieser.
Der Schatten ist dann nicht verfügbar.
Was als nächstes passiert, hängt von der Option MODE
ab.
AUTO | MANUAL
Modes
Wenn ein Schatten in eine Datenbank konvertiert wird, ist er nicht mehr verfügbar. Ein Schatten kann auch unverfügbar werden, weil jemand versehentlich seine Datei löscht oder der Speicherplatz, auf dem die Schatten-Dateien gespeichert sind, erschöpft ist oder selbst beschädigt ist.
-
Wenn der AUTO-Modus ausgewählt ist (Standardwert), wird die Spiegelung automatisch beendet, alle Referenzen werden aus dem Datenbank-Header gelöscht, und die Datenbank arbeitet normal weiter.
Wenn die Option
CONDITIONAL
festgelegt wurde, versucht das System, einen neuen Schatten zu erstellen, um den verlorenen zu ersetzen. Es ist jedoch nicht immer erfolgreich und ein neuer muss möglicherweise manuell erstellt werden. -
Ist das MANUAL-Modus-Attribut gesetzt, wenn der Schatten nicht mehr verfügbar ist, werden alle Versuche, eine Verbindung zur Datenbank herzustellen und diese abzufragen, Fehlermeldungen erzeugen. Die Datenbank bleibt so lange unzugänglich, bis entweder der Schatten wieder verfügbar ist oder der Datenbankadministrator sie mithilfe der Anweisung
DROP SHADOW
löscht. MANUAL sollte ausgewählt werden, wenn kontinuierliches Shadowing wichtiger ist als der unterbrechungsfreie Betrieb der Datenbank.
Optionen für CREATE SHADOW
LENGTH
-
Klausel, die die maximale Größe der primären oder sekundären Schattendatei in Seiten angibt. Der Wert
LENGTH
wirkt sich nicht auf die Größe der einzigen Schattendatei aus, noch auf die letzte, wenn es sich um eine Gruppe handelt. Die letzte (oder einzige) Datei wird automatisch so lange vergrößert, wie es nötig ist. STARTING AT
-
Klausel, die die Schattenseitennummer angibt, bei der die nächste Schattendatei gestartet werden soll. Das System fügt neue Daten zur nächsten Schattendatei hinzu, wenn die vorherige Datei bis zur angegebenen Seitenzahl mit Daten gefüllt ist.
Nur Administratoren haben die notwendigen Rechte die Anweisung CREATE SHADOW
auszuführen.
Sie können die Größen, Namen und den Speicherort der Schattendateien überprüfen, indem Sie mit isql eine Verbindung zur Datenbank herstellen und den Befehl |
Beispiele für die Verwendung von CREATE SHADOW
-
Erstellen eines Schattens für die aktuelle Datenbank als “shadow number 1”:
CREATE SHADOW 1 'g:\data\test.shd';
-
Erstellen eines Mehrdatei-Schattens für die aktuelle Datenbank als “shadow number 2”:
CREATE SHADOW 2 'g:\data\test.sh1' LENGTH 8000 PAGES FILE 'g:\data\test.sh2';
5.2.2. DROP SHADOW
Deleting a shadow from the current database
DSQL, ESQL
DROP SHADOW sh_num
Parameter | Beschreibung |
---|---|
sh_num |
Schattennummer — eine positive Zahl, die den Schattensatz identifiziert |
Die Anweisung DROP SHADOW
löscht den angegebenen Schatten für die Datenbank, mit der eine Verbindung besteht.
Wenn ein Schatten gelöscht wird, werden alle zugehörigen Dateien gelöscht und Schatten auf die angegebene sh_num werden beendet.
Nur Administratoren haben die notwendigen Rechte die Anweisung DROP SHADOW
auszuführen.
Löschen von “shadow Nummer 1”.
DROP SHADOW 1;
5.3. DOMAIN
Domain
ist eine Objektart innerhalb einer relationalen Datenbank.
Eine Domain wird als ein bestimmter Datentyp mit einigen Attributen erstellt.
Sobald 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 ist.
In diesem Abschnitt wird die Syntax von Anweisungen beschrieben, mit denen Domains erstellt, geändert und gelöscht werden. Eine detaillierte Beschreibung von Domains und deren 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> ::= {SMALLINT | INTEGER | BIGINT} [<array_dim>] | {FLOAT | DOUBLE PRECISION} [<array_dim>] | {DATE | TIME | TIMESTAMP} [<array_dim>] | {DECIMAL | NUMERIC} [(precision [, scale])] [<array_dim>] | {{CHAR | CHARACTER} [VARYING] | VARCHAR} [(size)] [<array_dim>] [CHARACTER SET charset_name] | {NCHAR | NATIONAL {CHARACTER | CHAR}} [VARYING] [(size)] [<array_dim>] | BLOB [SUB_TYPE {subtype_num | subtype_name}] [SEGMENT SIZE seglen] [CHARACTER SET charset_name] | BLOB [(seglen [, subtype_num])] <array_dim> ::= '[' [m:]n [,[m:]n ...] ']' <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 <datatype>) | (<select_one>) | func([<val> [, <val> ...]])
Parameter | Beschreibung |
---|---|
name |
Domainname aus maximal 31 Zeichen |
datatype |
SQL-Datentyp |
literal |
Ein literaler Wert, der kompatibel zu datatype ist |
context_var |
Jede Kontextvariable, deren Typ kompatibel ist mit datatype |
dom_condition |
Domain-Bedingung |
collation_name |
Name einer Collation, die für charset_name gültig ist, sofern dieser mit datatype übergeben wird, oder andernfalls für den Standardzeichensatz der Datenbank |
array_dim |
Array-Dimensionen |
m, n |
|
precision |
Die Gesamtzahl der signifikanten Ziffern, die ein Wert von datatype aufnehmen kann (1..18) |
scale |
Die Anzahl der Stellen nach dem Dezimalpunkt (0..precision) |
size |
Die maximale Anzahl einer Zeichenkette in Zeichen |
charset_name |
Der Name eines gültigen Zeichensatzes, falls sich der Zeichensatz der Domain vom Standardzeichensatz der Datenbank unterscheidet |
subtype_num |
|
subtype_name |
|
seglen |
Segmentgröße (max. 65535) |
select_one |
Eine skalare |
select_list |
Eine |
select_expr |
Eine |
expression |
Ein Ausdruck, der auf einen Wert auflöst, der mit datatype kompatibel ist |
genname |
Sequenzname (Generatorname) |
func |
Interne Funktion oder UDF |
Die Anweisung CREATE DOMAIN
erstellt eine neue Domain.
Jeder SQL-Datentyp kann als Domainntyp angegeben werden.
Typenspezifische Details
ARRAY
Typen-
-
Wenn die Domain ein Array sein soll, kann der Basistyp ein SQL-Datentyp mit Ausnahme von
BLOB
undARRAY
sein. -
Die Dimensionen des Arrays werden in eckigen Klammern angegeben. (Im Syntaxblock werden diese Klammern fett dargestellt, 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:
-
Standardmäßig sind Arrays 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 optional ein Leerraum, der zweite ist größer als der 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 ein optionales Leerzeichen getrennt werden.
-
Indizes werden nur validiert, wenn ein Array tatsächlich existiert. Dies bedeutet, dass keine Fehlermeldungen bezüglich ungültiger Subskripte zurückgegeben werden, wenn ein bestimmtes Element nichts zurückgibt oder wenn ein Array-Feld
NULL
ist.
-
- CHARACTER Typen
-
Sie können die
CHARACTER SET
-Klausel nutzen, um den Zeichensatz für die DatentypenCHAR
,VARCHAR
undBLOB
(SUB_TYPE TEXT
) zu definieren. Wird der Zeichensatz nicht angegeben, wird der in der Datenbank alsDEFAULT CHARACTER SET
Zeichensatz verwendet. Ist auch dieser nicht festgelegt, wird der ZeichensatzNONE
als Standard für die Anlage von Domains verwendet.Bei Zeichensatz
NONE
werden Zeichendaten gespeichert und abgerufen, wie sie übermittelt wurden. Daten in einer beliebigen Codierung können zu einer Spalte auf der Grundlage einer solchen Domain hinzugefügt werden. Es ist jedoch nicht möglich, diese Daten zu einer Spalte mit einer anderen Codierung hinzuzufügen. Da zwischen Quell- und Zielcodierung keine Transkription durchgeführt wird, können Fehler auftreten. DEFAULT
-Klausel-
Mit der optionalen
DEFAULT
-Klausel können Sie einen Standardwert für die Domain angeben. Dieser Wert wird der Tabellenspalte hinzugefügt, die diese Domain erbt, wenn die AnweisungINSERT
ausgeführt wird, wenn in der DML-Anweisung kein Wert dafür 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 basierend auf einer Domain mit der
NOT NULL
-Beschränkung werden daran gehindert, alsNULL
geschrieben zu werden, d.h. ein Wert ist erforderlich.Achten Sie beim Anlegen einer Domain darauf, keine Einschränkungen zu spezifizieren, die einander widersprechen würden. Zum Beispiel sind NOT NULL und DEFAULT NULL widersprüchlich.
CHECK
-Constraint(s)-
Die optionale Klausel
CHECK
gibt Einschränkungen für die Domain an. Eine Domainnbeschrä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” (entsprichtNULL
) zurückgibt. Wenn das PrädikatFALSE
zurückgibt, ist die Bedingung für die Annahme nicht erfüllt. VALUE
-Schlüsselwort-
Das Schlüsselwort
VALUE
in einer Domainbeschränkung ersetzt die Tabellenspalte, die auf dieser Domain oder einer Variablen in einem PSQL-Modul basiert. Es enthält den Wert, der der Variablen oder der Tabellenspalte zugewiesen ist.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 Sortierreihenfolge (Collation) angeben, wenn die Domain auf einem der String-Datentypen basiert, einschließlichBLOB
s mit Textsubtypen. Wenn keine Sortierreihenfolge angegeben ist, ist die Sortierreihenfolge diejenige, die für den angegebenen Zeichensatz zum Zeitpunkt der Erstellung der Domain voreingestellt ist.
Jeder Benutzer, der mit der Datenbank verbunden ist, kann eine Domain erstellen.
Beispiele für CREATE DOMAIN
-
Erstellen einer Domain mit Werten von mehr als 1.000 und 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 Standardzeichensatz annehmen kann, der während der Erstellung der Datenbank angegeben wurde.
CREATE DOMAIN D_BOOLEAN AS CHAR(3) CHECK (VALUE IN ('Yes', 'No'));
-
Erstellen einer Domain mit dem Zeichensatz
UTF8
und der Sortierreihenfolge (Collation)UNICODE_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 ein Array aus zwei Elementen des Typs
NUMERIC(18, 3)
definiert ist. Der Start-Array-Index ist 1.CREATE DOMAIN D_POINT AS NUMERIC(18, 3) [2];
Über einen Array-Typ definierte Domainn dürfen nur zum Definieren von Tabellenspalten verwendet werden. Sie können keine Array-Domainn verwenden, um lokale Variablen in PSQL-Modulen zu definieren.
-
Erstellen einer Domain, deren Elemente nur in der Tabelle COUNTRY definierte Ländercodes sein können.
CREATE DOMAIN D_COUNTRYCODE AS CHAR(3) CHECK (EXISTS(SELECT * FROM COUNTRY WHERE COUNTRYCODE = VALUE));
Das Beispiel zeigt nur die Möglichkeit, Prädikate mit Abfragen in der Domainntestbedingung zu verwenden. Es wird nicht empfohlen, diesen Stil der Domain in der Praxis zu verwenden 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] [ADD [CONSTRAINT] CHECK (<dom_condition>) | DROP CONSTRAINT] <datatype> ::= {SMALLINT | INTEGER | BIGINT} | {FLOAT | DOUBLE PRECISION} | {DATE | TIME | TIMESTAMP} | {DECIMAL | NUMERIC} [(precision [, scale])] | {CHAR | CHARACTER} [VARYING] | VARCHAR} [(size)] [CHARACTER SET charset_name] | {NCHAR | NATIONAL {CHARACTER | CHAR}} [VARYING] [(size)] | BLOB [SUB_TYPE {subtype_num | subtype_name}] [SEGMENT SIZE seglen] [CHARACTER SET charset_name] | BLOB [(seglen [, subtype_num])] <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 <datatype>) | (<select_one>) | func([<val> [, <val> ...]])
Parameter | Beschreibung |
---|---|
new_name |
Neuer Domainname, bestehend aus maximal 31 Zeichen |
datatype |
SQL-Datentyp |
literal |
Ein literaler Wert, der kompatibel zu datatype ist |
context_var |
Jede Kontextvariable, deren Typ kompatibel ist mit datatype |
precision |
Die Gesamtzahl der signifikanten Ziffern, die ein Wert des Datentyps aufnehmen kann (1..18) |
scale |
Die Anzahl der Stellen nach dem Dezimalkomma (0..precision) |
size |
Die maximale Größe einer Zeichenkette in Zeichen |
charset_name |
Der Name eines gültigen Zeichensatzes, falls sich der Zeichensatz der Domain vom Standardzeichensatz der Datenbank unterscheidet |
subtype_num |
|
subtype_name |
|
seglen |
Segmentgröße (max. 65535) |
select_one |
Eine skalare |
select_list |
Eine |
select_expr |
Eine |
expression |
Ein Ausdruck, der auf einen Wert auflöst, der mit datatype kompatibel ist |
genname |
Sequenzname (Generatorname) |
func |
Interne Funktion oder UDF |
Die Anweisung ALTER DOMAIN
ermöglicht Änderungen an den aktuellen Attributen einer Domain einschließlich ihres Namens.
Sie können beliebig viele Domainnänderungen in einer ALTER DOMAIN
-Anweisung vornehmen.
TO name
-
Verwenden Sie die
TO
-Klausel, um die Domain umzubenennen, solange keine Abhängigkeiten von der Domain vorhanden sind, z.B. 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 sie nicht zuerst gelöscht werden, sondern wird durch die neue ersetzt. DROP DEFAULT
-
Mit dieser Klausel löschen Sie einen zuvor festgelegten Standardwert und ersetzen ihn durch
NULL
. ADD CONSTRAINT CHECK
-
Verwenden Sie die Klausel
ADD CONSTRAINT CHECK
, um einenCHECK
-Constraint zur Domain hinzuzufügen. Existiert bereits einCHECK
-Constraint für die Domain, muss dieser zunächst gelöscht werden. Nutzen Sie dazu einALTER DOMAIN
-Statement, das eineDROP CONSTRAINT
-Klausel beinhaltet. TYPE
-
Die
TYPE
-Klausel wird verwendet, um den Datentyp der Domain in eine andere, kompatible zu ändern. Das System verbietet jede Änderung des Typs, der zu Datenverlust führen könnte. Ein Beispiel wäre, wenn die Anzahl der Zeichen im neuen Typ kleiner als im vorhandenen Typ wäre.
Wenn Sie die Attribute einer Domain ändern, kann der vorhandene PSQL-Code ungültig werden. Für Informationen zur Erkennung lesen Sie bitte den Artikel Das RDB$VALID_BLR Feld in Anhang A. |
Jeder Benutzer, der mit der Datenbank verbunden ist, kann eine Domain ändern, sofern sie nicht durch Abhängigkeiten von Objekten verhindert wird, für die dieser Benutzer nicht über ausreichende Berechtigungen verfügt.
Was die Ausführung von ALTER DOMAIN
verhindert
-
Wenn die Domain als ein Array deklariert wurde, ist es nicht möglich, ihren Typ oder ihre Dimensionen zu ändern. Es kann auch kein anderer Typ in einen
ARRAY
-Typ geändert werden. -
In Firebird 2.5 und niedriger darf die Einschränkung
NOT NULL
weder für eine Domain aktiviert noch deaktiviert werden. -
Es gibt keine Möglichkeit, die Standardkollation zu ändern, ohne die Domain zu löschen und sie mit den gewünschten Attributen neu zu erstellen.
Beispiele für ALTER DOMAIN
-
Ändern des Datentyps in
INTEGER
und festlegen oder ändern des Standardwerts auf 2.000:ALTER DOMAIN CUSTNO TYPE INTEGER SET DEFAULT 2000;
-
Umbenennen einer Domain.
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 des
CHECK
-Constraints: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 Anzahl von Zeichen zu erhöhen:
ALTER DOMAIN FIRSTNAME TYPE VARCHAR(50) CHARACTER SET UTF8;
5.3.3. DROP DOMAIN
Eine bestehende Domain löschen
DSQL, ESQL
DROP DOMAIN domain_name
Die Anweisung DROP DOMAIN
löscht eine Domain, die in der Datenbank vorhanden ist.
Es ist nicht möglich, eine Domain zu löschen, wenn sie von Spalten der Datenbanktabellen referenziert oder in einem PSQL-Modul verwendet wird.
Um eine Domain zu löschen, die verwendet wird, müssen alle Spalten in allen Tabellen, die auf die Domain verweisen, gelöscht werden und alle Verweise auf die Domain müssen aus PSQL-Modulen entfernt werden.
Jeder Benutzer, der mit der Datenbank verbunden ist, kann eine Domain löschen.
Beispiele
Löschen der COUNTRYNAME-Domain:
DROP DOMAIN COUNTRYNAME;
5.4. TABLE
Als relationales DBMS speichert Firebird Daten in Tabellen. Eine Tabelle ist eine flache, zweidimensionale Struktur, die eine beliebige Anzahl von Zeilen enthält. Tabellenzeilen werden oft als Datensätze bezeichnet.
Alle Zeilen in einer Tabelle haben die gleiche Struktur und bestehen aus Spalten. Tabellenspalten werden oft als Felder 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> <regular_col_def> ::= colname {<datatype> | domainname} [DEFAULT {<literal> | NULL | <context_var>}] [NOT NULL] [<col_constraint>] [COLLATE collation_name] <computed_col_def> ::= colname [<datatype>] {COMPUTED [BY] | GENERATED ALWAYS AS} (<expression>) <datatype> ::= {SMALLINT | INTEGER | BIGINT} [<array_dim>] | {FLOAT | DOUBLE PRECISION} [<array_dim>] | {DATE | TIME | TIMESTAMP} [<array_dim>] | {DECIMAL | NUMERIC} [(precision [, scale])] [<array_dim>] | {CHAR | CHARACTER} [VARYING] | VARCHAR} [(size)] [<array_dim>] [CHARACTER SET charset_name] | {NCHAR | NATIONAL {CHARACTER | CHAR}} [VARYING] [(size)] [<array_dim>] | BLOB [SUB_TYPE {subtype_num | subtype_name}] [SEGMENT SIZE seglen] [CHARACTER SET charset_name] | BLOB [(seglen [, subtype_num])] <array_dim> ::= '[' [m:]n [, [m:]n ...] ']' <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>) } <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 <datatype>) | (<select_one>) | func([<val> [, <val> ...]])
Parameter | Beschreibung |
---|---|
tablename |
Name (Kennung) für die Tabelle. Sie kann aus bis zu 31 Zeichen bestehen und in der Datenbank eindeutig sein. |
filespec |
Dateispezifikation (nur für externe Tabellen). Vollständiger Dateiname und Pfad, der in einfache Anführungszeichen eingeschlossen ist, unter Berücksichtigung der Regeln des lokalen Dateisystems. Die Datei muss physisch mit dem Host-Computer von Firebird verbunden sein. |
colname |
Name (Bezeichner) für eine Spalte in der Tabelle. Kann aus bis zu 31 Zeichen bestehen und in der Tabelle eindeutig sein. |
datatype |
SQL-Datentyp |
col_constraint |
Spalten-Constraint |
tconstraint |
Tabellen-Constraint |
constr_name |
Der Name (Bezeichner) einer Einschränkung. Darf aus bis zu 31 Zeichen bestehen. |
other_table |
Der Name der Tabelle, auf die die Constraint verweist |
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 |
Beliebige Kontextvariable, deren Datentyp im angegebenen Kontext zulässig ist |
check_condition |
Die Bedingung, die auf eine CHECK-Einschränkung angewendet wird, die als wahr, false oder |
collation |
Collation |
array_dim |
Array-Dimensionen |
m, n |
INTEGER-Ganzzahlen die den Bereich der Array-Dimensionen angeben |
precision |
Die Gesamtzahl der signifikanten Ziffern, die ein Wert des Datentyps halten kann (1..18) |
scale |
Die Anzahl Stellen nach dem Dezimalkomma (0..precision) |
size |
Die maximale Größe eines Strings in Zeichen |
charset_name |
Der Name eines gültigen Zeichensatzes, falls der Zeichensatz der Spalte vom Standardzeichensatz der Datenbank abweichen soll |
subtype_num |
|
subtype_name |
|
seglen |
Segmentgröße (max. 65535) |
select_one |
Eine skalare |
select_list |
Eine |
select_expr |
Eine |
expression |
Ein Ausdruck, der auf einen Wert auflöst, der im angegebenen Kontext zulässig ist |
genname |
Sequenzname (Generatorname) |
func |
Interne Funktion oder UDF |
Die Anweisung CREATE TABLE
erstellt eine neue Tabelle.
Jeder Benutzer kann sie erstellen und ihr Name muss unter den Namen aller Tabellen, Ansichten und gespeicherten Prozeduren in der Datenbank eindeutig sein.
Eine Tabelle muss mindestens eine Spalte enthalten, die nicht berechnet wird, und die Namen der Spalten müssen in der Tabelle eindeutig sein.
Eine Spalte muss entweder einen expliziten SQL-Datentyp, den Namen einer Domain, dessen Attribute für die Spalte kopiert werden oder als COMPUTED BY
-Ausdruck (ein berechnetes Feld).
Eine Tabelle kann eine beliebige Anzahl von Tabelleneinschränkungen haben, einschließlich keiner.
Eine Spalte nicht nullbar machen
In Firebird sind Spalten standardmäßig nullwertig.
Die optionale NOT NULL
-Klausel gibt an, dass die Spalte NULL
anstelle eines Wertes nicht verwenden darf.
Zeichen-Spalten
Sie können die CHARACTER SET
-Klausel verwenden, um den Zeichensatz für die Typen CHAR
, VARCHAR
und BLOB (SUB_TYPE TEXT)
anzugeben.
Wenn der Zeichensatz nicht angegeben ist, wird standardmäßig der während der Erstellung der Datenbank angegebene Zeichensatz verwendet.
Wurde während der Erstellung der Datenbank kein Zeichensatz angegeben, wird standardmäßig der Zeichensatz NONE
übernommen.
In diesem Fall werden Daten gespeichert und abgerufen, wie sie übermittelt wurden.
Daten in einer beliebigen Kodierung können zu einer solchen Spalte hinzugefügt werden, aber es ist nicht möglich, diese Daten zu einer Spalte mit einer anderen Kodierung hinzuzufügen.
Keine Transliteration wird zwischen den Quell- und Zielcodierungen, das dies zu Fehlern führen kann.
Mit der optionalen COLLATE
-Klausel können Sie die Sortierreihenfolge für Zeichendatentypen angeben, einschließlich BLOB SUB_TYPE TEXT
.
Wenn keine Sortierreihenfolge angegeben ist, wird standardmäßig die Sortierreihenfolge angewendet, die für den angegebenen Zeichensatz beim Erstellen der Spalte standardmäßig verwendet wird.
Angabe eines DEFAULT
-Wertes
Die optionale DEFAULT
-Klausel erlaubt Ihnen, den Standardwert für eine Tabellenspalte festzulegen.
Dieser Wert wird der Spalte während der Ausführung eines INSERT
-Statements zugewiesen, sofern kein anderer Wert festgelegt wurde und diese Spalte von der INSERT
-Anweisung ausgelassen 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 ist, wird NULL impliziert.
Ein Ausdruck kann nicht als Standardwert verwendet werden.
Domain-basierte Spalten
Um eine Spalte zu definieren, können Sie eine zuvor definierte Domain verwenden.
Wenn die Definition einer Spalte auf einer Domain basiert, enthält sie möglicherweise einen neuen Standardwert, zusätzliche CHECK
-Einschränkungen und eine COLLATE
-Klausel, die die in der Domain angegebenen Werte überschreibt.
Die Definition einer solchen Spalte kann zusätzliche Spaltenbeschränkungen enthalten (z.B. NOT NULL
), wenn die Domain diese nicht besitzt.
Es ist nicht möglich, eine Domain-basierte Spalte zu definieren, die nullbar ist, wenn die Domain mit dem Attribut |
Berechnete Felder
Berechnete Felder können in der Datenbank mittels COMPUTED [BY]
oder GENERATED ALWAYS AS
(gemäß SQL:2003 Standard) definiert werden.
Sie meinen dasselbe.
Das Beschreiben des Datentyps ist für berechnete Felder nicht erforderlich (aber möglich), da das DBMS den entsprechenden Typ als Ergebnis der Ausdrucksanalyse berechnet und speichert.
Entsprechende Operationen für die in einem Ausdruck enthaltenen Datentypen müssen genau angegeben werden.
Wenn der Datentyp explizit für ein berechnetes Feld angegeben wird, wird das Berechnungsergebnis in den angegebenen Typ konvertiert. Dies bedeutet zum Beispiel, dass das Ergebnis eines numerischen Ausdrucks als String dargestellt werden kann.
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. Es kann die Leistung des Einfügens / Aktualisierens von Datensätzen verringern, aber es wird die Leistung der Datenabfrage erhöhen. |
Definieren einer ARRAY
-Spalte
-
Wenn die Spalte ein Array sein soll, kann der Basistyp ein beliebiger SQL-Datentyp sein, mit Ausnahme von
BLOB
undARRAY
. -
Die Grenzen des Arrays werden in eckigen Klammern angegeben. (Im Syntaxblock werden diese Klammern fett dargestellt, um sie von 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:
-
Standardmäßig sind Arrays 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 optionaler Leerraum, der zweite ist größer als der 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 ein Array tatsächlich existiert. Dies bedeutet, dass keine Fehlermeldungen bezüglich ungültiger Subskripte zurückgegeben werden, wenn ein bestimmtes Element nichts zurückgibt oder wenn ein Array-Feld
NULL
ist.
Constraints
Es gibt vier Constraint-Typen. Diese sind:
-
Primärschlüssel (
PRIMARY KEY
) -
Eindeutigkeitsschlüssel (
UNIQUE
) -
Fremdschlüssel (
REFERENCES
) -
CHECK
-Constraint (CHECK
)
Constraints können auf Spaltenebene (“Spaltenbeschränkungen”) oder auf Tabellenebene (“Tabellenbeschränkungen”) angegeben werden.
Einschränkungen auf Tabellenebene sind erforderlich, wenn Schlüssel (Eindeutigkeitsbeschränkung, Primärschlüssel, Fremdschlüssel) über mehrere Spalten hinweg gebildet werden sollen und wenn eine CHECK
-Einschränkung neben der definierten Spalte andere Spalten in der Zeile einbezieht.
Die Syntax für einige Constraint-Typen kann je nachdem, ob die Constraint auf Spalten- oder Tabellenebene definiert wird, leicht unterschiedlich sein.
-
Eine Spaltenbeschränkung wird während einer Spaltendefinition angegeben, nachdem alle Spaltenattribute, mit Ausnahme von
COLLATION
, angegeben wurden und nur die in dieser Definition angegebene Spalte enthalten ist -
Einschränkungen auf Tabellenebene werden nach allen Spaltendefinitionen angegeben. Sie sind ein flexiblerer Weg, um Einschränkungen festzulegen, da sie Einschränkungen für mehrere Spalten berücksichtigen können
-
Sie können Einschränkungen auf Tabellen- und Spaltenebene im gleichen
CREATE TABLE
-Statement 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
ist eine Einschränkung auf Spaltenebene, FOREIGN KEY REFERENCES
eine auf Tabellenebene).
Name für Constraints und ihre Indizes
Spaltenbeschränkungen und ihre Indizes werden automatisch benannt:
-
Der Constraint-Name besitzt die Form
INTEG_n
, wobei n ein oder mehrere Ziffern repräsentiert -
Der Indexname besitzt die Form
RDB$PRIMARYn
(für einen Primärschlüsselindex),RDB$FOREIGNn
(für einen Fremdschlüsselindex) oderRDB$n
(für einen Eindeutigkeitsindex). Auch hier repräsentiert n eine oder mehrere Ziffern.
Die automatische Benennung von Einschränkungen auf Tabellenebene und deren Indizes folgt dem gleichen Muster, es sei denn, die Namen werden explizit angegeben.
Ein Constraint kann explizit benannt werden, wenn für ihre Definition die CONSTRAINT
-Klausel verwendet wird.
Die CONSTRAINT
-Klausel ist optional für die Definition von Spaltentypen auf Spaltenebene.
Sie ist jedoch obligatorisch für Tabellenstufen.
Standardmäßig hat der Constraint-Index denselben Namen wie die Constraint.
Wenn für den Constraint-Index ein anderer Name gewünscht wird, kann eine USING
-Klausel enthalten sein.
PRIMARY KEY
Der PRIMARY KEY
-Constraint wird auf einer oder mehr Schlüsselspalten gebildet, wobei jede Spalte mit einem NOT NULL
-Constraint definiert wurde.
Die Werte in den Schlüsselspalten einer Zeile müssen eindeutig sein.
Eine Tabelle kann nur einen Primärschlüssel enthalten.
-
Ein einspaltiger Primärschlüssel kann als Spalten- oder Tabellenebene definiert werden
-
Ein mehrspaltiger Primärschlüssel muss als Einschränkung auf Tabellenebene angegeben werden.
Der UNIQUE
-Constraint
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 von eindeutigen Schlüsseleinschränkungen enthalten.
Wie beim Primärschlüssel kann die eindeutige Einschränkung mehrspaltig sein. Ist dies der Fall, muss es als Einschränkung auf Tabellenebene angegeben werden.
NULL
in EindeutigkeitsschlüsselnDie SQL-99-konformen Regeln für UNIQUE
-Constraints erlauben einen oder mehrere NULL
s in einer Spalte mit einem UNIQUE
-Constraint.
Dadurch ist es möglich, eine UNIQUE
-Beschränkung für eine Spalte zu definieren, die nicht die Einschränkung NOT NULL
hat.
Bei UNIQUE
-Schlüsseln, die mehrere Spalten umfassen, ist die Logik ein wenig kompliziert:
-
Mehrere Zeilen mit Null in allen Spalten des Schlüssels sind erlaubt
-
Mehrere Zeilen mit Schlüsseln mit verschiedenen Kombinationen von Nullen und Nicht-Null-Werten sind erlaubt
-
Mehrere Zeilen mit denselben Schlüsselspalten null und der Rest mit Werten ungleich null sind zulässig, sofern sich die Werte in mindestens einer Spalte unterscheiden
-
Mehrere Zeilen mit denselben Schlüsselspalten null und der Rest mit Nicht-Nullwerten, die in jeder Spalte gleich sind, verletzen die Bedingung
Die Regeln für die Eindeutigkeit lassen sich so zusammenfassen:
Im Prinzip werden alle Nullen als eindeutig betrachtet.
Wenn jedoch zwei Zeilen exakt die gleichen Schlüsselspalten aufweisen, die mit Werten gefüllt sind, die nicht Null sind, werden die NULL
-Spalten ignoriert und die Eindeutigkeit wird für Spalten ohne Null festgelegt, 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 ); -- Erlaubt
INSERT INTO t values( NULL, NULL, 1 ); -- Nicht erlaubt
FOREIGN KEY
Ein Fremdschlüssel stellt sicher, dass die teilnehmenden Spalten nur Werte enthalten dürfen, die auch in der referenzierten Spalte (n) in der Mastertabelle vorhanden sind.
Diese referenzierten Spalten werden oft als Zielspalten bezeichnet.
Sie müssen der Primärschlüssel oder ein eindeutiger Schlüssel in der Zieltabelle sein.
Sie müssen keine NOT NULL
-Einschränkung definiert haben, 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 definiert werden, wobei das Schlüsselwort REFERENCES
verwendet wird:
... ,
ARTIFACT_ID INTEGER REFERENCES COLLECTION (ARTIFACT_ID),
Die Spalte ARTIFACT_ID
im Beispiel verweist auf eine gleichnamige Spalte in der Tabelle COLLECTIONS
.
Sowohl einspaltige als auch mehrspaltige Fremdschlüssel können auf der Tabellenebene definiert werden. Bei einem mehrspaltigen Fremdschlüssel ist die Deklaration auf Tabellenebene die einzige Option. Diese Methode ermöglicht auch die Angabe 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”) möglicherweise von denen im Fremdschlüssel unterscheiden.
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 betroffenen Fremdschlüsselspalte anzugeben, wenn referenzierte Werte in der Mastertabelle geändert werden:
NO ACTION
-
(der Standard) - nichts wird getan
CASCADE
-
Die Änderung in der Mastertabelle wird an die entsprechende(n) Zeile(n) in der untergeordneten 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 Master-Zeile gelöscht wird, werden die untergeordneten Datensätze gelöscht.
SET DEFAULT
-
Die Fremdschlüsselspalten in den betroffenen Zeilen werden wie bei der Definition der Fremdschlüsselbeschränkung auf ihre Standardwerte gesetzt.
SET NULL
-
Die Fremdschlüssel-Spalten in den betroffenen Zeilen werden auf NULL gesetzt.
Die angegebene Aktion oder die Standardeinstellung NO ACTION
könnte dazu führen, dass eine Fremdschlüsselspalte ungültig wird.
Beispielsweise könnte er 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 Master-Tabelle mit einer Fehlermeldung fehlschlägt.
...
CONSTRAINT FK_ORDERS_CUST
FOREIGN KEY (CUSTOMER) REFERENCES CUSTOMERS (ID)
ON UPDATE CASCADE ON DELETE SET NULL
CHECK
-Constraint
Die Bedingung 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 Anweisung INSERT
) und zum Aktualisieren des vorhandenen Werts der Tabellenspalte (die Anweisung UPDATE
wo eine dieser Aktionen stattfinden kann (UPDATE OR INSERT
, MERGE
).
Eine |
CHECK
-Bedingungen — ob auf Tabellen- oder Spaltenebene definiert — beziehen sich auf Tabellenspalten durch ihren Namen.
Die Verwendung des Schlüsselworts VALUE
als Platzhalter wie auch in der CHECK
-Bedingung für Domains ist im Kontext der Definition von Spalteneinschränkungen nicht zulässig.
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)
);
Global Temporary Tables (GTT)
Global Temporary Tables haben persistente Metadaten, aber ihre Inhalte sind transaktions- (Standard) oder verbindungsgebunden.
Jede Transaktion oder Verbindung hat eine eigene private Instanz eines GTT, isoliert von allen anderen.
Instanzen werden nur dann erstellt, wenn auf das GTT verwiesen wird.
Sie werden beim Beenden der Transaktion oder beim Trennen zerstört.
Die Metadaten eines 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]
Hinweise zur Syntax
|
Einschränkungen für GTTs
GTTs können mit allen Features und Utensilien gewöhnlicher Tabellen (Schlüssel, Referenzen, Indizes, Trigger usw.) “ausgestattet werden”, aber es gibt einige Einschränkungen:
-
GTTs und reguläre Tabellen können sich nicht gegenseitig referenzieren
-
Eine verbindungsgebundene (“PRESERVE ROWS”) GTT kann nicht auf eine transaktionsgebundene (“DELETE ROWS”) GTT verweisen
-
Domain-Constraints können nicht auf GTTs verweisen
-
Die Zerstörung einer GTT-Instanz am Ende ihres Lebenszyklus löst keine
BEFORE
/AFTER
-Löschtrigger aus
In einer vorhandenen Datenbank ist es nicht immer einfach, eine reguläre Tabelle von einer GTT oder einer GTT auf Transaktions- 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 Beziehungen in der Datenbank:
Das Feld |
Externe Tabellen
Die optionale Klausel EXTERNAL [FILE]
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 von einem beliebigen Typ außer BLOB
oder ARRAY
sein.
Alles, was Sie mit einer in einer externen Datei gespeicherten Tabelle tun können, ist, neue Zeilen einzufügen (INSERT
) und die Daten abzufragen).
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 ausgeführt wird.
Wenn der Parameter ExternalFileAccess in der Konfigurationsdatei firebird.conf
auf Restrict
lautet, muss in einem der dort aufgeführten Verzeichnisse das Argument für Restrict
stehen.
Wenn die Datei nicht schon existiert, wird Firebird diese beim ersten Zugriff erstellen.
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 “Zeilen”-Format einer externen Tabelle besteht aus fester Länge. Es gibt keine Feldbegrenzer: Sowohl Feld- als auch Zeilengrenzen werden durch die maximale Größe der Felddefinitionen in Bytes bestimmt. Dies ist sowohl beim Definieren der Struktur der externen Tabelle als auch beim Entwerfen einer Eingabedatei für eine externe Tabelle wichtig, die Daten aus einer anderen Anwendung importieren soll. Das ubiquitäre “.csv”-Format ist z.B. nicht nützlich als Eingabedatei 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 mit geeigneten Längen für die Daten, die sie tragen sollen.
Datums- und Zahlentypen können problemlos zu und von Zeichenketten umgewandelt werden, solange die Dateien nicht von einer anderen Firebird-Datenbank gelesen werden sollen, werden die nativen Datentypen externen Anwendungen als unparsbar “alphabetti” angezeigt.
Natürlich gibt es Möglichkeiten, typisierte Daten zu manipulieren, um Ausgabedateien von Firebird zu generieren, die als Eingabedateien für andere Anwendungen mithilfe von gespeicherten Prozeduren mit oder ohne externe Tabellen gelesen werden können. Solche Techniken liegen außerhalb des Bereichs einer Sprachreferenz. Hier finden Sie einige Richtlinien und Tipps zum Erstellen und Arbeiten mit einfachen Textdateien, da die externe Tabellenfunktion häufig als einfache Möglichkeit zum Erstellen oder Lesen von transaktionsunabhängigen Protokollen verwendet wird, die offline in einem Texteditor oder in einem 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 Leseanwendungen auf der vorgesehenen Plattform erkannt wird.
Für die meisten Kontexte unter Windows ist dies die Zwei-Byte-CRLF-Sequenz, Wagenrücklauf (ASCII-Code dezimal 13) und Zeilenvorschub (ASCII-Code dezimal 10).
Auf POSIX ist LF für sich allein üblich;
Bei einigen MacOSX-Anwendungen kann es sich um LFCR handeln.
Es gibt verschiedene Möglichkeiten, diese Begrenzerspalte zu füllen.
In unserem Beispiel unten geschieht dies mittel 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 kann. Die externe Tabelle wird ausgewählt, da die Nachrichten aus den behandelten Ausnahmen im Protokoll beibehalten werden, selbst wenn die Transaktion, die den Prozess gestartet hat, aufgrund einer anderen, nicht behandelten Ausnahme schließlich zurückgesetzt wird. Zu Demonstrationszwecken gibt es nur zwei Datenspalten, einen Zeitstempel und eine Nachricht. Die dritte Spalte speichert den Zeilenbegrenzer:
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 noch einen Trigger erstellen, der Zeitstempel und Zeilenbegrenzer festlegt, sobald 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 von Datensätzen (die von einem Exception-Handler oder Shakespeare-Fan gemacht worden sein könnten):
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
Beispiele für CREATE TABLE
-
Erstellen der Tabelle
COUNTRY
mit dem als Spaltenbeschränkung angegebenen Primärschlüssel.CREATE TABLE COUNTRY ( COUNTRY COUNTRYNAME NOT NULL PRIMARY KEY, CURRENCY VARCHAR(10) NOT NULL );
-
Erstellen der
STOCK
-Tabelle mit dem auf der Spaltenebene angegebenen benannten Primärschlüssel und dem auf der Tabellenebene angegebenen benannten eindeutigen Schlüssel.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
JOB
-Tabelle mit einer Primärschlüsselbeschränkung, die zwei Spalten umfasst, eine Fremdschlüsselbeschränkung für die TabelleCOUNTRY
und eineCHECK
-Einschränkung auf Tabellenebene. Die Tabelle enthält außerdem eine Reihe 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
PROJECT
-Tabelle mit Primär-, Fremd- und eindeutigen Schlüsseleinschränkungen mit benutzerdefinierten Indexnamen, die mit derUSING
-Klausel angegeben wurden.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 der Tabelle
SALARY_HISTORY
mit zwei berechneten Feldern. Der erste wird gemäß dem Standard SQL:2003 deklariert, während der zweite gemäß der traditionellen Deklaration der berechneten Felder 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) );
-
Erstellen einer verbindungsabhängigen Global Temporary Table.
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 globale globale temporäre Tabelle mit Verbindungsbereich zu verweisen. Die
ON COMMIT
-Unterklausel ist optional, daDELETE ROWS
die Standardeinstellung 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;
5.4.2. ALTER TABLE
Ändern der Tabellenstruktur.
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> <regular_col_def> ::= colname {<datatype> | domainname} [DEFAULT {<literal> | NULL | <context_var>}] [NOT NULL] [<col_constraint>] [COLLATE collation_name] <computed_col_def> ::= colname [<datatype>] {COMPUTED [BY] | GENERATED ALWAYS AS} (<expression>) <col_mod> ::= <regular_col_mod> | <computed_col_mod> <regular_col_mod> ::= TO newname | POSITION newpos | TYPE {<datatype> | domainname} | SET DEFAULT {<literal> | NULL | <context_var>} | DROP DEFAULT <computed_col_mod> ::= TO newname | POSITION newpos | [TYPE <datatype>] {COMPUTED [BY] | GENERATED ALWAYS AS} (<expression>) <datatype> ::= {SMALLINT | INTEGER | BIGINT} [<array_dim>] | {FLOAT | DOUBLE PRECISION} [<array_dim>] | {DATE | TIME | TIMESTAMP} [<array_dim>] | {DECIMAL | NUMERIC} [(precision [, scale])] [<array_dim>] | {CHAR | CHARACTER} [VARYING] | VARCHAR} [(size)] [<array_dim>] [CHARACTER SET charset_name] | {NCHAR | NATIONAL {CHARACTER | CHAR}} [VARYING] [(size)] [<array_dim>] | BLOB [SUB_TYPE {subtype_num | subtype_name}] [SEGMENT SIZE seglen] [CHARACTER SET charset_name] | BLOB [(seglen [, subtype_num])] <array_dim> ::= '[' [m:]n [,[m:]n ...] ']' <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>) } <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>) | (<search_condition>) | NOT <search_condition> | <search_condition> OR <search_condition> | <search_condition> AND <search_condition> <operator> ::= <> | != | ^= | ~= | = | < | > | <= | >= | !< | ^< | ~< | !> | ^> | ~> <val> ::= colname ['['array_idx [, array_idx ...]']'] | <literal> | <context_var> | <expression> | NULL | NEXT VALUE FOR genname | GEN_ID(genname, <val>) | CAST(<val> AS <datatype>) | (<select_one>) | func([<val> [, <val> ...]])
Parameter | Beschreibung |
---|---|
tablename |
Name (Kennung) für die Tabelle. Sie kann aus bis zu 31 Zeichen bestehen und in der Datenbank eindeutig sein. |
operation |
Eine der verfügbaren Operationen, die die Struktur der Tabelle verändert |
colname |
Name (Bezeichner) für eine Spalte in der Tabelle, max. 31 Zeichen. Muss in der Tabelle eindeutig sein. |
newname |
Neuer Name (Bezeichner) für die Spalte, max. 31 Zeichen. Muss in der Tabelle eindeutig sein. |
newpos |
Die neue Spaltenposition (eine Ganzzahl zwischen 1 und der Anzahl der Spalten in der Tabelle) |
col_constraint |
Spalten-Constraint |
tconstraint |
Tabellen-Constraint |
constr_name |
Der Name (Bezeichner) einer Einschränkung. Darf aus bis zu 31 Zeichen bestehen. |
other_table |
Der Name der Tabelle, auf die die Constraint verweist |
literal |
Ein Literalwert, der im angegebenen Kontext zulässig ist |
context_var |
Beliebige Kontextvariable, deren Datentyp im angegebenen Kontext zulässig ist |
check_condition |
Die Bedingung, die auf eine |
collation |
Collation |
array_dim |
Array-Dimensionen |
m, n |
INTEGER-Ganzzahlen die den Bereich der Array-Dimensionen angeben |
precision |
Die Gesamtzahl der signifikanten Ziffern, die ein Wert des Datentyps halten kann (1..18) |
scale |
Die Anzahl Stellen nach dem Dezimalkomma (0..precision) |
size |
Die maximale Größe eines Strings in Zeichen |
charset_name |
Der Name eines gültigen Zeichensatzes, falls der Zeichensatz der Spalte vom Standardzeichensatz der Datenbank abweichen soll |
subtype_num |
|
subtype_name |
|
seglen |
Segmentgröße (max. 65535) |
select_one |
Eine skalare |
select_list |
Eine |
select_expr |
Eine |
expression |
Ein Ausdruck, der auf einen Wert auflöst, der im angegebenen Kontext zulässig ist |
genname |
Sequenzname (Generatorname) |
func |
Interne Funktion oder UDF |
Die ALTER TABLE
-Anweisung ändert die Struktur einer vorhandenen Tabelle.
Mit einer Anweisung ALTER TABLE
können Sie mehrere Operationen ausführen, Spalten und Einschränkungen hinzufügen und löschen sowie Spaltenangaben ändern.
Mehrere Operationen in einer Anweisung ALTER TABLE
werden durch Kommata getrennt.
Zählung von Versionsinkrementen
Einige Änderungen in der Struktur einer Tabelle erhöhen den Metadatenänderungszähler (“Versionszähler”), der jeder Tabelle zugewiesen ist. Die Anzahl der Metadatenänderungen ist für jede Tabelle auf 255 beschränkt. 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 Tabellenbeschränkung hinzufügen.
Die Syntax zum Definieren der Spalte und die Syntax zum Definieren der Tabellenbeschränkung entsprechen denen, die für die Anweisung CREATE TABLE
beschrieben wurden.
-
Jedes Mal, wenn eine neue Spalte hinzugefügt wird, wächst der Metadatenänderungszähler um eins
-
Das Hinzufügen einer neuen Tabellenbeschränkung erhöht den Metadatenänderungszähler nicht
Zu beachtende Punkte
|
Die DROP
-Klausel
Die Klausel DROP Spaltenname
löscht die angegebene Spalte aus der Tabelle.
Ein Versuch, eine Spalte zu löschen, schlägt fehl, wenn irgendetwas darauf verweist.
Betrachten Sie die folgenden Elemente als Quellen potenzieller Abhängigkeiten:
-
Spalten- oder Tabellenbeschränkungen
-
Indizes
-
Stored Procedures und Trigger
-
Ansichten (Views)
Jedes Mal, wenn eine Spalte gelöscht wird, wird der Metadatenänderungszähler der Tabelle um eins erhöht.
Die DROP CONSTRAINT
-Klausel
Die DROP CONSTRAINT
-Klausel löscht die angegebene Spalten- oder Tabellenebenenbeschränkung.
Eine PRIMARY KEY
- oder UNIQUE
-Beschränkung kann nicht gelöscht werden, wenn sie in einer anderen Tabelle durch eine FOREIGN KEY
-Constraint referenziert wird.
Es ist notwendig, die FOREIGN KEY
-Beschränkung zu löschen, bevor Sie versuchen, die hierauf verweisenden PRIMARY KEY
-Constraints oder UNIQUE
-Constraints zu löschen.
Das Löschen einer Spaltenbeschränkung oder einer Tabellenbeschränkung erhöht den Metadatenänderungszähler nicht.
Die ALTER [COLUMN]
-Klausel
Mit der ALTER [COLUMN]
-Klausel können Attribute bestehender Spalten geändert werden, ohne dass die Spalte gelöscht oder neu hinzugefügt werden muss.
Erlaubte Änderungen sind:
-
Ändern des Namens (wirkt sich nicht auf den Metadatenversionszähler aus)
-
Ändern des Datentyps (erhöht den Metadatenversionszähler um eins)
-
Ändern der Spaltenposition in der Spaltenliste der Tabelle (wirkt sich nicht auf den Metadatenversionszähler aus)
-
Löschen des Standardspaltenwert (wirkt sich nicht auf den Metadatenversionszähler aus)
-
Festlegen des Standardspaltenwertes oder den vorhandenen Standardwert ändern (hat keinen Einfluss auf den Metadatenversionszähler)
-
Ändern des Typs und des Ausdrucks für eine berechnete Spalte (wirkt sich nicht auf den Metadatenversionszähler aus)
Umbenennen einer Spalte: das TO
-Schlüsselwort
Das TO
-Schlüsselwort mit einer neuen Kennung benennt eine vorhandene Spalte um.
Die Tabelle darf keine existierende Spalte haben, die den gleichen Bezeichner hat.
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 der CHECK
-Constraint einer Tabelle.
Das Umbenennen einer Spalte ist ebenfalls nicht zulässig, wenn die Spalte in einem Trigger, einer gespeicherten Prozedur oder einer Ansicht (View) verwendet wird.
Ändern des Datentyps einer Spalte: Das TYPE
-Schlüsselwort
Das Schlüsselwort TYPE
ändert den Datentyp einer vorhandenen Spalte in einen anderen, zulässigen Typ.
Eine Typänderung, die zu einem Datenverlust führen kann, wird nicht zugelassen.
Beispielsweise kann die Anzahl der Zeichen im neuen Typ für eine CHAR
- oder VARCHAR
-Spalte nicht kleiner sein als die bestehende Spezifikation dafür.
Wenn die Spalte als Array deklariert wurde, ist keine Änderung des Typs oder der Anzahl der Dimensionen zulässig.
Der Datentyp einer Spalte, die an einem Fremdschlüssel, einem Primärschlüssel oder einer eindeutigen Einschränkung beteiligt ist, kann überhaupt nicht geändert werden.
Ändern der Position einer Spalte: Das POSITION
-Schlüsselwort
Das POSITION
-Schlüsselwort ä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 ist, wird eine Fehlermeldung zurückgegeben
-
Wenn eine Positionsnummer größer als die Anzahl der Spalten in der Tabelle ist, wird die neue Position automatisch an die Anzahl der Spalten angepasst.
Die DROP DEFAULT
- und SET DEFAULT
-Klauseln
Die optionale DROP DEFAULT
-Klausel löscht den Standardwert für die Spalte, wenn sie zuvor durch eine CREATE TABLE
- oder ALTER TABLE
-Anweisung dort gesetzt wurde.
-
Wenn die Spalte auf einer Domäne mit einem Standardwert basiert, wird der Standardwert auf die Standarddomain 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 Domain-basiert ist
Die optionale SET DEFAULT
-Klausel setzt einen Standardwert für die Spalte.
Wenn die Spalte bereits einen Standardwert hat, wird sie durch die neue ersetzt.
Der Standardwert, der auf eine Spalte angewendet wird, überschreibt immer einen von einer Domain geerbten Wert.
Die COMPUTED [BY]
- oder GENERATED ALWAYS AS
-Klauseln
Der Datentyp und der Ausdruck, die einer berechneten Spalte zugrunde liegen, können mit der ALTER TABLE ALTER [COLUMN]
-Anweisung angepasst werden.
Die Umwandlung einer regulären Spalte in eine berechnete und umgekehrt ist nicht zulässig.
Attribute, die nicht geändert werden können
Folgende Änderungen werden nicht unterstützt:
-
Aktivieren oder Deaktivieren der Einschränkung
NOT NULL
für eine Spalte -
Ändern der Standardkollation für eine Zeichentypspalte
Nur der Tabelleneigentümer und Administratoren haben die Berechtigung für ALTER TABLE
.
Beispiele für die Verwendung von ALTER TABLE
-
Hinzufügen der Spalte
CAPITAL
zur TabelleCOUNTRY
.ALTER TABLE COUNTRY ADD CAPITAL VARCHAR(25);
-
Hinzufügen der
CAPITAL
-Spalte mit derUNIQUE
-Einschränkung und Löschen derCURRENCY
-Spalte.ALTER TABLE COUNTRY ADD CAPITAL VARCHAR(25) NOT NULL UNIQUE, DROP CURRENCY;
-
Hinzufügen der
CHK_SALARY
-Prüfbedingung und eines Fremdschlüssels zurJOB
-Tabelle.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 derITEMID
-Spalte und umbenennen der SpalteMODELNAME
.ALTER TABLE STOCK ALTER COLUMN MODEL SET DEFAULT 1, ALTER COLUMN ITEMID TYPE BIGINT, ALTER COLUMN MODELNAME TO NAME;
-
Ä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 einer Tabelle
DSQL, ESQL
DROP TABLE tablename
Parameter | Beschreibung |
---|---|
tablename |
Name (Kennung) der Tabelle |
Die Anweisung DROP TABLE
löscht eine vorhandene Tabelle.
Wenn die Tabelle Abhängigkeiten hat, schlägt die Anweisung DROP TABLE
mit einem Ausführungsfehler fehl.
Wenn eine Tabelle gelöscht wird, werden auch alle Trigger für ihre Ereignisse und Indizes, die für ihre Felder erstellt wurden, gelöscht.
Nur der Tabelleneigentümer und Administratoren haben die Berechtigung, DROP TABLE
zu verwenden.
Die Tabelle COUNTRY
löschen.
DROP TABLE COUNTRY;
5.4.4. RECREATE TABLE
Erstellen einer neuen Tabelle (Relation) oder Wiederherstellen einer bestehenden
DSQL
RECREATE [GLOBAL TEMPORARY] TABLE tablename [EXTERNAL [FILE] 'filespec'] (<col_def> [, {<col_def> | <tconstraint>} ...]) [ON COMMIT {DELETE | PRESERVE} ROWS]
Vergleichen Sie den CREATE TABLE
-Abschnitt für die vollständige Syntax für CREATE TABLE
und beachten Sie die Beschreibungen zum Definieren von Tabellen, Spalten und Constraints.
RECREATE TABLE
erstellt eine Tabelle neu oder erneut.
Wenn bereits eine Tabelle mit diesem Namen vorhanden ist, versucht die Anweisung RECREATE TABLE
, diese zu löschen und eine neue zu erstellen.
Bestehende Abhängigkeiten verhindern die Ausführung der Anweisung.
Erstellen oder Wiederherstellen der Tabelle COUNTRY
.
RECREATE TABLE COUNTRY (
COUNTRY COUNTRYNAME NOT NULL PRIMARY KEY,
CURRENCY VARCHAR(10) NOT NULL
);
5.5. INDEX
Ein Index ist ein Datenbankobjekt, das für eine schnellere Datenabfrage aus einer Tabelle oder zur Beschleunigung der Sortierung der Abfrage verwendet wird.
Indizes werden auch verwendet, um die referenziellen Integritätsbedingungen PRIMARY KEY
, FOREIGN KEY
und UNIQUE
sicherzustellen.
In diesem Abschnitt wird beschrieben, wie Sie Indizes erstellen, aktivieren und deaktivieren, löschen und Statistiken sammeln (Selektivität neu berechnen).
5.5.1. CREATE INDEX
Einen Index für eine Tabelle erstellen
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 bekannt als “Ausdrucksindex” |
Die Anweisung CREATE INDEX
erstellt einen Index für eine Tabelle, mit der das Suchen, Sortieren und Gruppieren beschleunigt werden kann.
Indizes werden automatisch beim Definieren von Constraints wie Primärschlüssel, Fremdschlüssel oder eindeutigen Constraints erstellt.
Ein Index kann auf den Inhalt von Spalten eines beliebigen Datentyps mit Ausnahme von BLOB
und Arrays aufgebaut werden.
Der Name (Bezeichner) eines Index muss unter allen Indexnamen eindeutig sein.
Schlüssel-Indizes
Wenn ein Primärschlüssel, ein Fremdschlüssel oder eine eindeutige Einschränkung zu einer Tabelle oder Spalte hinzugefügt wird, wird automatisch ein Index mit demselben Namen erstellt, ohne explizite Anweisung vom Designer.
Beispielsweise wird der
|
Unique-Indizes
Wenn Sie in der Indexerstellungsanweisung das Schlüsselwort UNIQUE
angeben, wird ein Index erstellt, in dem die Eindeutigkeit in der gesamten Tabelle durchgesetzt wird.
Der Index wird als “eindeutiger Index” bezeichnet.
Ein eindeutiger Index ist keine Einschränkung.
Eindeutige Indizes dürfen keine doppelten Schlüsselwerte (oder Duplikatschlüsselwertkombinationen im Falle von berechnet Indizes oder multi-column oder multi-segment) enthalten.
Duplizierte NULL
s sind gemäß dem SQL: 99-Standard sowohl in Indizes mit einem einzelnen Segment als auch mit mehreren Segmenten zulässig.
Index-Sortierung
Alle Indizes in Firebird sind unidirektional.
Ein Index kann vom niedrigsten Wert zum höchsten (aufsteigend) oder vom höchsten zum niedrigsten (absteigend) erstellt 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 sinnvoll, sowohl einen auf- 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 Suchanfragen auf den hohen Werten unterzogen wird (“neuestes”, Maximum usw.) |
Berechnete (Ausdrucks-) Indizes
Beim Erstellen eines Index können Sie die COMPUTED BY
-Klausel verwenden, um anstelle einer oder mehrerer Spalten einen Ausdruck anzugeben.
Berechnete Indizes werden in Abfragen verwendet, bei denen die Bedingung in einer WHERE
, ORDER BY
oder GROUP BY
-Klausel exakt dem Ausdruck in der Indexdefinition entspricht.
Der Ausdruck in einem berechneten Index kann mehrere Spalten in der Tabelle enthalten.
Sie können tatsächlich einen berechneten Index für ein berechnetes Feld erstellen, der Index wird jedoch nie verwendet. |
Indexgrenzen
Bestimmte Beschränkungen gelten für Indizes.
Die maximale Länge eines Schlüssels in einem Index ist auf ¼ der Seitengröße.
Maximale Anzahl 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 |
Anzahl der Indizes in Abhängigkeit von der Spaltenanzahl |
||
---|---|---|---|
einspaltig |
zweispaltig |
dreispaltig |
|
4096 |
203 |
145 |
113 |
8192 |
408 |
291 |
227 |
16384 |
818 |
584 |
454 |
Zeichenindexgrenzen
Die maximale Länge der indizierten Zeichenfolgen beträgt 9 Byte weniger als die maximale Schlüssellänge. Die maximale indexierbare Zeichenfolgenlänge hängt von der Seitengröße und dem Zeichensatz ab.
Seitengröße |
Maximale indexierbare Zeichenfolgenlänge nach Zeichensatz |
|||
---|---|---|---|---|
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 |
Nur der Tabelleneigentümer und Administratoren besitzen die notwendigen Rechte für die Verwendung von CREATE INDEX
.
Beispiel für die Verwendung von CREATE INDEX
-
Erstellen eines Index für die
UPDATER_ID
in der TabelleSALARY_HISTORY
CREATE INDEX IDX_UPDATER ON SALARY_HISTORY (UPDATER_ID);
-
Erstellen eines Index mit Schlüsseln in absteigender Reihenfolge für die
CHANGE_DATE
-Spalte in der TabelleSALARY_HISTORY
.CREATE DESCENDING INDEX IDX_CHANGE ON SALARY_HISTORY (CHANGE_DATE);
-
Erstellen eines Multisegment-Index für die Spalten
ORDER_STATUS
sowiePAID
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
PERSONS
-TabelleCREATE INDEX IDX_NAME_UPPER ON PERSONS COMPUTED BY (UPPER (NAME));
Ein solcher Index kann für eine Groß- / Kleinschreibungs-sensitive Suche verwendet werden:
SELECT * FROM PERSONS WHERE UPPER(NAME) STARTING WITH UPPER('Iv');
5.5.2. ALTER INDEX
Aktivieren oder Deaktivieren eines Indexes; einen Index neu aufbauen
DSQL, ESQL
ALTER INDEX indexname {ACTIVE | INACTIVE}
Parameter | Beschreibung |
---|---|
indexname |
Indexname |
Die ALTER INDEX
-Anweisung aktiviert oder deaktiviert einen Index.
In dieser Anweisung gibt es keine Möglichkeit, Attribute des Index zu ändern.
-
Mit der Option
INACTIVE
wird der Index vom aktiven in den inaktiven Zustand geschaltet. Der Effekt ähnelt der AnweisungDROP INDEX
, mit der Ausnahme, dass die Indexdefinition in der Datenbank verbleibt. Das Ändern eines Beschränkungsindex in den inaktiven Zustand ist nicht zulässig.Ein aktiver Index kann deaktiviert werden, wenn keine Abfragen mit diesem Index vorhanden sind. Andernfalls wird ein “Objekt in Verwendung”-Fehler zurückgegeben.
Die Aktivierung eines inaktiven Index ist ebenfalls sicher. Wenn jedoch aktive Transaktionen die Tabelle ändern, schlägt die Transaktion mit der Anweisung
ALTER INDEX
fehl, wenn das AttributNOWAIT
vorhanden ist. Wenn sich die Transaktion im ModusWAIT
befindet, wartet sie auf den Abschluss der gleichzeitigen Transaktionen.Auf der anderen Seite der Münze werden andere Transaktionen, die diese Tabelle modifizieren den Index nach einem
COMMIT
neu erstellen oder fehlschlagen je nach Status derWAIT
/NO WAIT
-Attribute. Die Situation ist genau dieselbe fürCREATE INDEX
.Wie sinnvoll ist dies?Es kann sinnvoll sein, einen Index in den inaktiven Zustand zu wechseln, während Sie einen großen Stapel von Datensätzen in der Tabelle, in der sich der Index befindet, einfügen, aktualisieren oder löschen.
-
Wenn sich der Index im Status
INACTIVE
befindet, wird es mit der OptionACTIVE
in den aktiven Status umgeschaltet, und das System erstellt den Index neu.Wie sinnvoll ist dies?Auch wenn der Index active ist, wenn
ALTER INDEX … ACTIVE
ausgeführt wird, wird der Index neu erstellt. Die Wiederherstellung von Indizes kann eine nützliche Haushaltshilfe sein, gelegentlich auch für die Indizes einer großen Tabelle in einer Datenbank, die häufige Neuaufnahmen Aktualisierungen oder Löschungen aufweist, aber selten wiederhergestellt wird.
Verwendung von ALTER INDEX
in einem Constraint-Index
Das Ändern des Erzwingungsindex für eine PRIMARY KEY
-, FOREIGN KEY
- oder UNIQUE
-Einschränkung auf INACTIVE
ist nicht zulässig.
Jedoch funktionert ALTER INDEX … ACTIVE
genauso gut wie andere als Indexwiederherstellungstool.
Nur der Tabelleneigentümer und Administratoren haben die Berechtigungen für die Anweisung ALTER INDEX
.
5.5.3. DROP INDEX
Deleting an index
DSQL, ESQL
DROP INDEX indexname
Parameter | Beschreibung |
---|---|
indexname |
Indexname |
Das Statement DROP INDEX
löscht den angegebenen Index aus der Datenbank.
Ein Constraint-Index kann nicht mittels |
Nur die Tabelleneigentümer und Administratoren besitzen die Berechtigungen die Anweisung DROP INDEX
auszuführen.
Löschen des Index IDX_UPDATER
DROP INDEX IDX_UPDATER;
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.
Index-Selektivität
Die Selektivität eines Index ergibt sich aus der Auswertung der Anzahl der Zeilen, die bei einer Suche für jeden Indexwert ausgewählt werden können. Ein eindeutiger Index hat die maximale Selektivität, da es nicht mö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 Wahl des Optimierers bei der Suche nach dem optimalen Abfrageplan.
Indexstatistiken in Firebird werden nicht automatisch als Reaktion auf große Stapel von Neuaufnahmen, Aktualisierungen oder Löschungen neu berechnet. Es kann vorteilhaft sein, die Selektivität eines Index nach solchen Operationen neu zu berechnen, da die Selektivität dazu neigt, zu veralten.
Die Anweisungen |
Die Selektivität eines Index kann vom Besitzer der Tabelle oder einem Administrator neu berechnet werden.
Es kann unter gleichzeitiger Belastung ohne Korruptionsrisiko durchgeführt werden.
Beachten Sie jedoch, dass die neu berechnete Statistik bei gleichzeitiger Auslastung veraltet sein kann, sobald SET STATISTICS
beendet ist.
5.6. VIEW
Eine Sicht (VIEW
) ist eine virtuelle Tabelle, bei der es sich um eine gespeicherte und benannte SELECT
-Abfrage zum Abrufen von Daten beliebiger Komplexität handelt.
Daten können aus einer oder mehreren Tabellen, aus anderen Ansichten und aus ausgewählten gespeicherten Prozeduren abgerufen werden.
Im Gegensatz zu regulären Tabellen in relationalen Datenbanken ist eine Sicht kein unabhängiger Datensatz, der in der Datenbank gespeichert ist. Das Ergebnis wird dynamisch als Datensatz erstellt, wenn die Ansicht ausgewählt wird.
Die Metadaten einer Sicht sind für den Prozess verfügbar, der den Binärcode für gespeicherte Prozeduren und Trigger generiert, so als wären es konkrete Tabellen, in denen persistente Daten gespeichert werden.
5.6.1. CREATE VIEW
Erstellen einer View
DSQL
CREATE VIEW viewname [<full_column_list>] AS <select_statement> [WITH CHECK OPTION] <full_column_list> ::= (colname [, colname ...])
Parameter | Beschreibung |
---|---|
viewname |
View-Name, maximal 31 Zeichen |
select_statement |
|
full_column_list |
Die Liste der Spalten in der View |
colname |
Spaltenname der View. Doppelte Spaltennamen sind nicht zulässig. |
Die Anweisung CREATE VIEW
erstellt eine neue View Der Bezeichner (Name) einer View muss unter den Namen aller Ansichten, Tabellen und gespeicherten Prozeduren in der Datenbank eindeutig sein.
Dem Namen der neuen Sicht kann die Liste der Spaltennamen folgen, die beim Aufrufen der View an den Aufrufer zurückgegeben werden sollen. Namen in der Liste müssen nicht mit den Namen der Spalten in den Basistabellen verknüpft sein, von denen sie abgeleitet werden.
Wenn die Sichtspaltenliste ausgelassen wird, verwendet das System die Spaltennamen und / oder Aliase aus der Anweisung SELECT
.
Wenn doppelte Namen oder nicht von Alias-Ausdrucken abgeleitete Spalten das Erstellen einer gültigen Liste unmöglich machen, schlägt die Erstellung der View mit einem Fehler fehl.
Die Anzahl der Spalten in der Liste der Ansichten muss genau der Anzahl der Spalten in der Auswahlliste der zugrunde liegenden SELECT
-Anweisung in der Sichtdefinition entsprechen.
Zusätzliche Punkte
|
Aktualisierbare Views
Eine View kann aktualisierbar oder schreibgeschützt sein.
Wenn eine Sicht aktualisierbar ist, können die beim Aufruf dieser Sicht abgerufenen Daten durch die DML-Anweisungen INSERT
, UPDATE
, DELETE
, UPDATE OR INSERT
oder MERGE
.
Änderungen, die in einer aktualisierbaren View vorgenommen wurden, werden auf die zugrunde liegenden Tabelle(n) angewendet.
Eine Nur-Lese-Ansicht kann mit Hilfe von Triggern aktualisiert werden. Nachdem Trigger in einer Sicht definiert wurden, werden Änderungen, die an sie gesendet wurden, niemals automatisch in die zugrunde liegende Tabelle geschrieben, selbst wenn die Ansicht von vornherein aktualisiert werden konnte. Es liegt in der Verantwortung des Programmierers, sicherzustellen, dass die Trigger die Basistabellen nach Bedarf aktualisieren (oder löschen oder einfügen).
Eine View wird automatisch aktualisiert, wenn alle der folgenden Bedingungen erfüllt sind:
-
Die
SELECT
-Anweisung fragt nur eine Tabelle oder eine aktualisierbare Ansicht ab -
Die Anweisung
SELECT
ruft keine gespeicherten Prozeduren auf -
jede Spalte der Basistabelle (oder Basissicht), die in der Sichtdefinition nicht vorhanden ist, wird durch eine der folgenden Bedingungen abgedeckt:
-
sie ist nullbar
-
sie hat einen nicht-
NULL
Standardwert -
sie hat einen Trigger, der einen zulässigen Wert liefert
-
-
die Anweisung
SELECT
enthält keine Felder, die von Unterabfragen oder anderen Ausdrücken abgeleitet wurden -
die
SELECT
-Anweisung enthält keine durch Aggregatfunktionen definierten Felder wieMIN
,MAX
,AVG
,SUM
,COUNT
,LIST
-
Die
SELECT
-Anweisung enthält keine der KlauselnORDER BY
oderGROUP BY
-
Das
SELECT
-Statement enthält weder das SchlüsselwortDISTINCT
noch zeilenbeschränkende Schlüsselwörter wieROWS
,FIRST
,SKIP
WITH CHECK OPTION
Die optionale Klausel WITH CHECK OPTION
erfordert eine aktualisierbare Sicht, um zu überprüfen, ob neue oder aktualisierte Daten die in der WHERE
-Klausel der SELECT
.
Jeder Versuch, einen neuen Datensatz einzufügen oder einen bestehenden zu aktualisieren, wird überprüft, ob der neue oder aktualisierte Datensatz den Kriterien aus WHERE
entspricht.
Wenn die Überprüfung fehlschlägt, wird die Operation nicht ausgeführt und eine entsprechende Fehlermeldung wird 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
-Anweisung einzuschränken.
Eine Fehlermeldung wird ansonsten zurückgegeben.
Bitte beachten:
Wenn Außerdem werden Sichtfelder, die in der Anweisung Für Views, die |
Eigentümer einer View
Der Ersteller einer Sicht wird zu seinem Besitzer.
Um eine Sicht zu erstellen, benötigt ein Benutzer ohne Administratorrechte mindestens SELECT
-Zugriff auf die zugrunde liegenden Tabelle(n) und / oder Ansichten und die Berechtigung EXECUTE
auf abfragbare gespeicherte Prozeduren.
Um Einfügungen, Aktualisierungen und Löschungen durch die Sicht zu ermöglichen, muss der Ersteller / Eigentümer auch die entsprechenden Berechtigungen INSERT
, UPDATE
und DELETE
auf die zugrunde liegenden Objekt(e) besitzen.
Das Freigeben anderer Benutzerprivilegien für die Sicht ist nur möglich, wenn der Sichtbesitzer selbst diese Berechtigungen für die zugrunde liegenden Objekte WITH GRANT OPTION
besitzt.
Dies ist immer dann der Fall, wenn der View-Besitzer auch Eigentümer der zugrunde liegenden Objekte ist.
Beispiele zum Erstellen von Views
-
Creating view returning the
JOB_CODE
andJOB_TITLE
columns only for those jobs whereMAX_SALARY
is less than $15,000.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 diejenigen 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 der WertMAX_SALARY < 15000
Zustand wird überprüft. Wenn die Bedingung nicht wahr ist, wird die Operation zum Einfügen / Aktualisieren zurückgewiesen.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 Aliasnamen 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 existierenden View
DSQL
ALTER VIEW viewname [<full_column_list>] AS <select_statement> [WITH CHECK OPTION] <full_column_list> ::= (colname [, colname ...])
Parameter | Beschreibung |
---|---|
viewname |
Name einer existierenden View |
select_statement |
|
full_column_list |
Die Liste der Spalten in der View |
colname |
Spaltenname der View Doppelte Spaltennamen sind nicht zulässig. |
Verwenden Sie die Anweisung ALTER VIEW
, um die Definition einer vorhandenen View zu ändern.
Berechtigungen für Ansichten bleiben erhalten und Abhängigkeiten sind nicht betroffen.
Die Syntax der Anweisung ALTER VIEW
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 Sicht zugreifen, können ungültig werden. Informationen zur Erkennung dieser Art von Problemen in gespeicherten Prozeduren und Triggern finden Sie unter Das RDB$VALID_BLR-Feld im Anhang. |
Nur der View-Eigentümer und Administratoren besitzen die notwendigen Berechtigungen zum Ausführen von ALTER VIEW
.
ALTER VIEW
Ansicht ändern PRICE_WITH_MARKUP
ALTER VIEW PRICE_WITH_MARKUP (
CODE_PRICE,
COST,
COST_WITH_MARKUP
) AS
SELECT
CODE_PRICE,
COST,
COST * 1.15
FROM PRICE;
5.6.3. CREATE OR ALTER VIEW
Creating a new view or altering an existing view.
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 View, die vorhanden oder nicht vorhanden ist |
select_statement |
|
full_column_list |
Die Liste der Spalten in der View |
colname |
Spaltenname der View. Duplikate sind nicht zulässig. |
Verwenden Sie die Anweisung CREATE OR ALTER VIEW
, um die Definition einer vorhandenen 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 Anweisung CREATE OR ALTER VIEW
entspricht vollständig der von CREATE VIEW
.
Erstellen Sie die Ansicht PRICE_WITH_MARKUP
der neuen Ansicht oder ändern Sie sie, falls diese bereits vorhanden ist:
CREATE OR ALTER VIEW PRICE_WITH_MARKUP (
CODE_PRICE,
COST,
COST_WITH_MARKUP
) AS
SELECT
CODE_PRICE,
COST,
COST * 1.15
FROM PRICE;
5.6.4. DROP VIEW
Löschen (dropping) einer View
DSQL
DROP VIEW viewname
Parameter | Beschreibung |
---|---|
viewname |
View-Name |
Die Anweisung DROP VIEW
löscht eine existierende View.
Die Anweisung wird fehlschlagen wenn die View Abhängigkeiten besitzt.
Nur der Eigentümer der View und Administratoren besitzen die notwendigen Berechtigungen zum Ausführen von DROP VIEW
.
Löschen der View PRICE_WITH_MARKUP
.
DROP VIEW PRICE_WITH_MARKUP;
5.6.5. RECREATE VIEW
Erstellen einer neuen Ansicht oder Wiederherstellen einer vorhandenen View
DSQL
RECREATE VIEW viewname [<full_column_list>] AS <select_statement> [WITH CHECK OPTION] <full_column_list> ::= (colname [, colname ...])
Parameter | Beschreibung |
---|---|
viewname |
View-Name, maximal 31 Zeichen |
select_statement |
|
full_column_list |
Die Liste der Spalten in der View |
colname |
Spaltenname der View. Duplikate sind nicht zulässig. |
Erstellt oder erstellt eine Ansicht neu.
Wenn bereits eine Ansicht mit diesem Namen vorhanden ist, versucht die Engine, sie vor dem Erstellen der neuen Instanz zu löschen.
Wenn die vorhandene Sicht aufgrund von Abhängigkeiten oder unzureichenden Rechten nicht gelöscht werden kann, schlägt RECREATE VIEW
mit einem Fehler fehl.
Die neue View PRICE_WITH_MARKUP
erstellen oder neu erstellen, falls diese bereits vorhanden ist.
RECREATE VIEW PRICE_WITH_MARKUP (
CODE_PRICE,
COST,
COST_WITH_MARKUP
) AS
SELECT
CODE_PRICE,
COST,
COST * 1.15
FROM PRICE;
5.7. TRIGGER
Ein Trigger ist ein spezieller Typ einer gespeicherten Prozedur, der nicht direkt aufgerufen wird, sondern ausgeführt wird, wenn ein bestimmtes Ereignis in der zugeordneten Tabelle oder Sicht (View) auftritt. Ein Trigger ist spezifisch für eine und nur eine Relation (Tabelle oder View) und eine Phase im Timing des Ereignisses (BEFORE oder AFTER). Es kann angegeben werden, dass dieser für ein bestimmtes Ereignis (Einfügen, Aktualisieren, Löschen) oder für eine Kombination von zwei oder drei dieser Ereignisse ausgeführt wird.
Eine andere Form eines Triggers — bekannt als ein “Datenbanktrigger” — kann spezifiziert werden, um in Verbindung mit dem Start oder dem Ende einer Benutzersitzung (Verbindung) oder einer Benutzertransaktion zu ausgelöst zu werden.
5.7.1. CREATE TRIGGER
Erstellen eines neuen Triggers
DSQL, ESQL
CREATE TRIGGER trigname { <relation_trigger_legacy> | <relation_trigger_sql2003> | <database_trigger> } AS [<declarations>] BEGIN [<PSQL_statements>] END <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] <mutation_list> ::= <mutation> [OR <mutation> [OR <mutation>]] <mutation> ::= { INSERT | UPDATE | DELETE } <db_event> ::= { CONNECT | DISCONNECT | TRANSACTION START | TRANSACTION COMMIT | TRANSACTION ROLLBACK } <declarations> ::= {<declare_var> | <declare_cursor>}; [{<declare_var> | <declare_cursor>}; …]
Parameter | Beschreibung |
---|---|
trigname |
Triggername bestehend aus bis zu 31 Zeichen. Dieser muss unter allen Triggernamen in der Datenbank eindeutig sein. |
relation_trigger_legacy |
Legacy-Stil der Trigger-Deklaration für einen Relation-Trigger |
relation_trigger_sql2003 |
Relation-Trigger-Deklaration gemäß dem SQL: 2003-Standard |
database_trigger |
Datenbank-Triggerdeklaration |
tablename |
Name der Tabelle, der der Relationstrigger zugeordnet ist |
viewname |
Name der Sicht, der der Relationstrigger zugeordnet ist |
mutation_list |
Liste von Relationsereignissen (Tabelle | Ansicht) |
number |
Position des Triggers in der Zündreihenfolge. Von 0 bis 32.767 |
db_event |
Verbindungs- oder Transaktionsereignis |
declarations |
Abschnitt zum Deklarieren von lokalen Variablen und benannten Cursorn |
declare_var |
Lokale Variablendeklarieren |
declare_cursor |
Benannte Cursor-Deklaration |
PSQL_statements |
Anweisungen in der Programmiersprache von Firebird (PSQL) |
Die Anweisung CREATE TRIGGER
dient zum Erstellen eines neuen Triggers.
Ein Trigger kann entweder für ein Ereignis Relation (Table | View) (oder eine Kombination von Ereignissen) oder für ein Datenbankereignis erstellt werden.
CREATE TRIGGER
, zusammen mit den zugehörigen Assoziaten ALTER TRIGGER
, CREATE OR ALTER TRIGGER
und RECREATE TRIGGER
, ist eine zusammengesetzte Anweisung, bestehend aus einem Header und einem Rumpf.
Der Header spezifiziert den Namen des Triggers, den Namen der Relation (für einen Relations-Trigger), die Phase des Triggers und das Ereignis, für das er gilt.
Der Körper besteht aus optionalen Deklarationen von lokalen 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 mitgelieferte 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 bei der Codierung in diesen Umgebungen zu einem Konflikt mit der PSQL-Syntax. Wenn Sie mit diesem Problem und seiner Lösung nicht vertraut sind, lesen Sie bitte die Details im Kapitel PSQL im Abschnitt Umschalten des Terminators in isql.
Relations-Trigger (auf Tabellen oder Views)
Relation-Trigger werden jedes Mal auf der Zeilen- (Datensatz-) Ebene ausgeführt, wenn sich das Zeilenbild ändert.
Ein Trigger kann entweder ACTIVE
oder INACTIVE
sein.
Nur aktive Trigger werden ausgeführt.
Trigger werden standardmäßig mit ACTIVE
erstellt.
Formen der Deklaration
Firebird unterstützt zwei Arten der Deklaration für Relationstrigger:
-
Die ursprüngliche Legacy-Syntax
-
Das standardmäßige SQL:2003-Formular (empfohlen)
Das standardmäßige SQL:2003-Formular ist das empfohlene Format.
Ein Relationstrigger spezifiziert unter anderem eine Phase und ein oder mehrere Ereignisse.
Phase
Die Phase betrifft das Timing des Triggers in Bezug auf das Zustandswechselereignis in der Datenzeile:
-
Ein
BEFORE
-Trigger wird ausgelöst, bevor die angegebene Datenbankoperation (Einfügen, Aktualisieren oder Löschen) ausgeführt wird. -
Ein
AFTER
-Trigger wird ausgeführt, nachdem die Datenbankoperation abgeschlossen wurde.
Multi-Aktions-Trigger
Eine Relationstriggerdefinition gibt mindestens eine der DML-Operationen INSERT
, UPDATE
und DELETE
an, um ein oder mehrere Ereignisse anzuzeigen, auf die der Trigger ausgelöst werden soll.
Wenn mehrere Operationen angegeben werden, müssen sie durch das Schlüsselwort OR
getrennt werden.
Keine Operation darf mehrmals auftreten.
Ausführungsreihenfolge von Triggern
Das Schlüsselwort POSITION
erlaubt es, eine optionale Ausführungsreihenfolge (“firing order”) für eine Reihe von Triggern anzugeben, die dieselbe Phase und dasselbe Ereignis wie ihr Ziel haben.
Die Standardposition ist 0.
Wenn keine Positionen angegeben werden oder wenn mehrere Trigger eine einzelne Positionsnummer haben, werden die Trigger in der alphabetischen Reihenfolge ihrer Namen ausgeführt.
Variablendeklarationen
Der optionale Deklarationsabschnitt unter dem Schlüsselwort AS
im Header des Triggers dient zum Definieren von Variablen und benannten Cursorn, die lokal zum Trigger gehören.
Weitere Informationen finden Sie unter DECLARE VARIABLE
und DECLARE CURSOR
im Kapitel Prozedurales SQL.
Der Trigger-Body
Die lokalen Deklarationen (falls vorhanden) sind der letzte Teil des Headerabschnitts eines Triggers.
Der Trigger-Body folgt, wobei ein oder mehrere Blöcke von PSQL-Anweisungen in einer Struktur eingeschlossen sind, die mit dem Schlüsselwort BEGIN
beginnt und mit dem Schlüsselwort END
endet.
Nur der Eigentümer der Sicht oder Tabelle und Administratoren haben die Berechtigung, CREATE TRIGGER
zu verwenden.
Beispiele für CREATE TRIGGER für Tabellen und Views
-
Erstellung eines Triggers in “Legacy”-Form. Wird vor dem Einfügen eines neuen Datensatzes in die Tabelle
CUSTOMER
ausgelöst.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 eines Triggers in SQL:2003-konformer Variante, der vor dem Einfügen eines neuen Datensatzes in die Tabelle
CUSTOMER
ausgelöst wird.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
-
Einen Trigger erstellen, der nach dem Einfügen, Aktualisieren oder Löschen eines Datensatzes in der Tabelle
CUSTOMER
ausgeführt 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 definiert werden, um auf “Datenbankereignisse” zu reagieren. Dies können verschiedene Ereignisse sein. Diese können auf den Umfang einer Sitzung (Verbindung) hinweg agieren, aber auch über die Umgebung einer Transaktion wirken:
-
CONNECT
-
DISCONNECT
-
TRANSACTION START
-
TRANSACTION COMMIT
-
TRANSACTION ROLLBACK
Ausführung von Datenbanktriggern und Fehlerbehandlung
CONNECT
- und DISCONNECT
-Trigger werden in einer speziell für diesen Zweck erstellten Transaktion ausgeführt.
Läuft alles glatt, wird die Transaktion committed.
Nicht abgefangene Ausnahmen bewirken, dass die Transaktion zurückgesetzt wird, und
-
für einen
CONNECT
-Trigger wird die Verbindung unterbrochen und die Ausnahme wird an den Client zurückgegeben -
für einen
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 Aktion, die nach einer nicht abgefangenen Ausnahme ausgeführt wird, hängt vom Ereignis ab:
-
In einem
TRANSACTION START
-Trigger wird die Ausnahme an den Client gemeldet und die Transaktion wird zurückgesetzt -
In einem
TRANSACTION COMMIT
-Trigger wird die Ausnahme gemeldet, die bisherigen Aktionen des Triggers werden rückgängig gemacht und das Commit abgebrochen -
In einem
TRANSACTION ROLLBACK
-Trigger wird die Ausnahme nicht gemeldet und die Transaktion wird wie beabsichtigt 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 verursacht und eine Transaktion nicht gestartet werden kann, wenn auch ein TRANSACTION START
-Trigger einen auslöst.
Beide Phänomene sperrt Sie effektiv aus Ihrer Datenbank aus, bis Sie mit unterdrückten Datenbanktriggern zurückkehren und den fehlerhaften Code beheben.
Einige Firebird-Befehlszeilentools wurden mit Switches ausgestattet, mit denen ein Administrator die automatische Auslösung von Datenbanktriggern unterdrücken kann. Bisher sind sie:
gbak -nodbtriggers
isql -nodbtriggers
nbackup -T
In einem zweiphasigen Commit-Szenario löst ein TRANSACTION COMMIT
-Trigger bereits in der Vorbereitungsphase aus und nicht erst beim Commit.
-
Die Verwendung der Anweisung
IN AUTONOMOUS TRANSACTION DO
in den Datenbanktriggern für Transaktionen (TRANSACTION START
,TRANSACTION ROLLBACK
,TRANSACTION COMMIT
) kann dazu führen, dass die autonome Transaktion eine Endlosschleife generiert -
Die Ereignistrigger
DISCONNECT
undTRANSACTION ROLLBACK
werden nicht ausgeführt, wenn Clients über Überwachungstabellen getrennt werden (DELETE FROM MON$ATTACHMENTS
)
Nur der Datenbankbesitzer und Administratoren haben die Berechtigung zum Erstellen von Datenbanktriggern.
Beispiele für CREATE TRIGGER
für “Database Triggers”
-
Einen Trigger für das Ereignis erstellen, bei dem eine Verbindung zur Datenbank hergestellt wird, in der Benutzer protokolliert werden, die sich am System anmelden. Der Trigger wird als inaktiv erstellt.
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
-
Einen Trigger für das Ereignis der Verbindung mit der Datenbank erstellen, das 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
ALTER TRIGGER
, CREATE OR ALTER TRIGGER
, RECREATE TRIGGER
, DROP TRIGGER
5.7.2. ALTER TRIGGER
Ändern und Deaktivieren eines vorhandenen Triggers
DSQL, ESQL
ALTER TRIGGER trigname [ACTIVE | INACTIVE] [{BEFORE | AFTER} <mutation_list> | ON <db_event>] [POSITION number] [ AS [<declarations>] BEGIN [<PSQL_statements>] END ] <mutation_list> ::= <mutation> [OR <mutation> [OR <mutation>]] <mutation> ::= { INSERT | UPDATE | DELETE } <db_event> ::= { CONNECT | DISCONNECT | TRANSACTION START | TRANSACTION COMMIT | TRANSACTION ROLLBACK } <declarations> ::= {<declare_var> | <declare_cursor>}; [{<declare_var> | <declare_cursor>}; …]
Parameter | Beschreibung |
---|---|
trigname |
Name eines vorhandenen Triggers |
mutation_list |
Liste von Relation-Ereignissen (Tabelle | Ansicht) |
number |
Position des Triggers in der Zündreihenfolge. Von 0 bis 32.767 |
declarations |
Abschnitt zum Deklarieren von lokalen Variablen und benannter Cursor |
declare_var |
Lokale Variablendeklaration |
declare_cursor |
Benannte Cursor-Deklaration |
PSQL_statements |
Anweisungen in der Programmiersprache von Firebird (PSQL) |
Die Anweisung ALTER TRIGGER
erlaubt bestimmte Änderungen am Header und am Rumpf eines Triggers.
Zulässige Änderungen an Triggern
-
Status (
ACTIVE | INACTIVE
) -
Phase (
BEFORE | AFTER
) -
Veranstaltungen; aber Relationstriggerereignisse können nicht in Datenbanktriggerereignisse geändert werden, und umgekehrt
-
Position innerhalb der Ausführungsreihenfolge
-
Änderungen am Code im Trigger-Body
Wenn ein Element nicht angegeben wurde, bleibt es unverändert.
Zur Erinnerungen
Das Mehr als ein Beziehungsereignis — Das Schlüsselwort |
Administratoren und folgende Benutzer haben die Berechtigung für die Ausführung von ALTER TRIGGER
:
-
Für Relations-Trigger der Besitzer des Tisches
-
Für Datenbank-Trigger der Eigentümer der Datenbank
Beispiele mit ALTER TRIGGER
-
Deaktivieren des Triggers
set_cust_no
(Umschalten in den inaktiven Status)ALTER TRIGGER set_cust_no INACTIVE;
-
Ändern der Ausführungsreihenfolge des Triggers
set_cust_no
.ALTER TRIGGER set_cust_no POSITION 14;
-
Den Trigger
TR_CUST_LOG
in den inaktiven Status schalten und die Liste der Ereignisse ändern.ALTER TRIGGER TR_CUST_LOG INACTIVE AFTER INSERT OR UPDATE;
-
Den
tr_log_connect
-Trigger in den aktiven Status schalten und 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 vorhandenen Triggers
DSQL
CREATE OR ALTER TRIGGER trigname { <relation_trigger_legacy> | <relation_trigger_sql2003> | <database_trigger> } AS [<declarations>] BEGIN [<PSQL_statements>] END
Für das vollständige Detail der Syntax siehe CREATE TRIGGER
.
Die Anweisung CREATE OR ALTER TRIGGER
erstellt einen neuen Trigger, falls er nicht existiert.
Andernfalls ändert und kompiliert er es erneut, wobei die Privilegien intakt und die Abhängigkeiten unberührt bleiben.
CREATE OR ALTER TRIGGER
Erstellen eines neuen Triggers, falls er nicht existiert oder anpassen, falls vorhanden.
CREATE OR ALTER TRIGGER set_cust_no
ACTIVE BEFORE INSERT POSITION 0 ON customer
AS
BEGIN
IF (NEW.cust_no IS NULL) THEN
NEW.cust_no = GEN_ID(cust_no_gen, 1);
END
5.7.4. DROP TRIGGER
Einen vorhandenen Trigger löschen
DSQL, ESQL
DROP TRIGGER trigname
Parameter | Beschreibung |
---|---|
trigname |
Triggername |
Die Anweisung DROP TRIGGER
löscht einen vorhandenen Trigger.
Administrators und die folgenden Benutzer besitzen die Berechtigung, die Anweisung DROP TRIGGER
auszuführen:
-
Für Relations-Trigger, der Eigentümer der Tabelle
-
Für Datenbank-Trigger, der Eigentümer der Datenbank
DROP TRIGGER
Löschen des Triggers set_cust_no
.
DROP TRIGGER set_cust_no;
5.7.5. RECREATE TRIGGER
Erstellen eines neuen Triggers oder Neuerstellung eines vorhandenen
DSQL
RECREATE TRIGGER trigname { <relation_trigger_legacy> | <relation_trigger_sql2003> | <database_trigger> } AS [<declarations>] BEGIN [<PSQL_statements>] END
Für die detaillierte Syntax, vergleichen Sie CREATE TRIGGER
.
Die Anweisung RECREATE TRIGGER
erzeugt 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 beim COMMIT
fehl, wenn Triggerabhängigkeiten vorliegen.
Beachten Sie, dass Abhängigkeitsfehler erst in der Phase |
RECREATE TRIGGER
Erstellen oder Neuerstellung des Triggers set_cust_no
.
RECREATE TRIGGER set_cust_no
ACTIVE BEFORE INSERT POSITION 0 ON customer
AS
BEGIN
IF (NEW.cust_no IS NULL) THEN
NEW.cust_no = GEN_ID(cust_no_gen, 1);
END
5.8. PROCEDURE
Eine gespeicherte Prozedur (Stored Procedure) ist ein Softwaremodul, das von einem Client, einer anderen Prozedur, einem ausführbaren Block oder einem Trigger aufgerufen werden kann. Gespeicherte Prozeduren, ausführbare Blöcke und Trigger werden in prozeduralem SQL (PSQL) geschrieben. Die meisten SQL-Anweisungen sind auch in PSQL verfügbar, manchmal mit Einschränkungen oder Erweiterungen. Zu den bemerkenswerten Ausnahmen zählen DDL- und Transaktionskontrollanweisungen.
Gespeicherte Prozeduren können viele Eingabe- und Ausgabeparameter haben.
5.8.1. CREATE PROCEDURE
Erstellen einer neuen gespeicherten Prozedur
DSQL, ESQL
CREATE PROCEDURE procname [(<inparam> [, <inparam> ...])] [RETURNS (<outparam> [, <outparam> ...])] AS [<declarations>] BEGIN [<PSQL_statements>] END <inparam> ::= <param_decl> [{= | DEFAULT} <value>] <outparam> ::= <param_decl> <value> ::= {<literal> | NULL | <context_var>} <param_decl> ::= paramname <type> [NOT NULL] [COLLATE collation] <type> ::= <datatype> | [TYPE OF] domain | TYPE OF COLUMN rel.col <datatype> ::= {SMALLINT | INT[EGER] | BIGINT} | {FLOAT | DOUBLE PRECISION} | {DATE | TIME | TIMESTAMP} | {DECIMAL | NUMERIC} [(precision [, scale])] | {CHAR | CHARACTER} [VARYING] | VARCHAR} [(size)] [CHARACTER SET charset] | {NCHAR | NATIONAL {CHARACTER | CHAR}} [VARYING] [(size)] | BLOB [SUB_TYPE {subtype_num | subtype_name}] [SEGMENT SIZE seglen] [CHARACTER SET charset] | BLOB [(seglen [, subtype_num])] <declarations> ::= {<declare_var> | <declare_cursor>}; [{<declare_var> | <declare_cursor>}; …]
Parameter | Beschreibung |
---|---|
procname |
Der Name der gespeicherten Prozedur besteht aus bis zu 31 Zeichen. Muss für alle Tabellen-, View- und Prozedurnamen in der Datenbank eindeutig sein |
inparam |
Beschreibung der Eingabeparameter |
outparam |
Beschreibung der Ausgangsparameter |
declarations |
Abschnitt zum Deklarieren von lokalen Variablen und benannten Cursorn |
declare_var |
Lokale Variablendeklaration |
declare_cursor |
Benannte Cursor-Deklaration |
PSQL_statements |
Prozedurale SQL-Anweisungen |
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. Dieser kann aus bis zu 31 Zeichen bestehen. Der Name des Parameters muss unter den Eingabe- und Ausgabeparametern der Prozedur und ihren lokalen Variablen eindeutig sein |
datatype |
SQL-Datentyp |
collation |
Sortierfolge |
domain |
Domain-Name |
rel |
Tabellen- oder View-Name |
col |
Spaltenname einer Tabelle oder View |
precision |
Die Gesamtanzahl der signifikanten Stellen, die der Parameter halten kann (1..18) |
scale |
Die Anzahl der Stellen nach dem Dezimalpunkt (0..precision) |
size |
Die maximale Größe eines Zeichenfolgentypparameters oder einer Variablen in Zeichen |
charset |
Zeichensatz eines String-Typ-Parameters oder einer Variablen |
subtype_num |
Subtyp-Nummer eines |
subtype_name |
Mnemonischer Name eines |
seglen |
Segmentgröße (max. 65535) |
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 ggf. die Ausgabeparameter, die von der Prozedur zurückgegeben werden sollen.
Der Prozedurhauptteil 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 dem Schlüsselwort END
endet.
Deklarationen und eingebettete Anweisungen werden mit Semikolon (‘;
’) abgeschlossen.
Statement-Terminatoren
Einige SQL-Anweisungseditoren — insbesondere das mit Firebird mitgelieferte 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 bei der Codierung in diesen Umgebungen zu einem Konflikt mit der PSQL-Syntax. Wenn Sie mit diesem Problem und seiner Lösung nicht vertraut sind, lesen Sie bitte die Details im Kapitel PSQL im Abschnitt Umschalten des Terminators in isql.
Parameter
Jeder Parameter hat einen Datentyp, der dafür angegeben ist.
Die Einschränkung NOT NULL
kann auch für jeden beliebigen Parameter angegeben werden, um zu verhindern, dass NULL
übergeben oder zugewiesen wird.
Mit der Klausel COLLATE
kann eine Sortierfolge für Parameter vom Typ String festgelegt werden.
- Eingabeparameter
-
Eingabeparameter werden nach dem Namen der Prozedur in Klammern angezeigt. Sie werden als Werte an die Prozedur übergeben, d.h. alles, was sie innerhalb der Prozedur ändert, hat keine Auswirkungen auf die Parameter im aufrufenden Programm.
Eingabeparameter können Standardwerte haben. Diejenigen, für die Werte angegeben sind, müssen sich am Ende der Parameterliste befinden.
- Ausgabeparameter
-
Die optionale Klausel
RETURNS
dient zur Angabe einer eingeklammerten Liste von Ausgabeparametern für die gespeicherte Prozedur.
Verwendung von Domains in Deklarationen
Ein Domainname kann als Typ eines Parameters angegeben werden. Der Parameter erbt alle Domainattribute. Wenn ein Standardwert für den Parameter angegeben wird, überschreibt dieser den in der Domänendefinition angegebenen Standardwert.
Wenn die Klausel TYPE OF
vor dem Domänennamen hinzugefügt wird, wird nur der Datentyp der Domain verwendet: Alle anderen Attribute der Domain — NOT NULL
-Einschränkung, CHECK
-Bedingung, Standardwert — werden weder geprüft noch verwendet.
Wenn die Domain jedoch aus einem Texttyp besteht, werden immer ihre Zeichensatz und die Sortierreihenfolge verwendet.
Verwendung des Spaltentyps in Deklarationen
Eingabe- und Ausgabeparameter können auch über den Datentyp von Spalten in vorhandenen Tabellen und Ansichten deklariert werden.
Die Klausel TYPE OF COLUMN
wird dafür verwendet, wobei relationname.columnname als Argument angegeben wird.
Wenn TYPE OF COLUMN
verwendet wird, erbt der Parameter nur den Datentyp, bei Zeichenkettentypen den Zeichensatz und die Sortierreihenfolge.
Die Constraints und der Standardwert der Spalte werden ignoriert.
Bugwarnung für Versionen vor Firebird 3:
Für Eingabeparameter wird die Sortierung, die mit dem Typ der Spalte geliefert wird, in Vergleichen ignoriert (z.B. Gleichheitstests). Bei lokalen Variablen variiert das Verhalten. Der Bug wurde für Firebird 3 behoben. |
Variablen- und Cursor-Deklarationen
Der optionale Deklarationsabschnitt, der zuletzt im Headerabschnitt der Prozedurdefinition aufgeführt ist, definiert lokale Variablen für die Prozedur und ihre benannten Cursor.
Lokale Variablendeklarationen folgen denselben Regeln wie Parameter bezüglich der Spezifikation des Datentyps.
Bitte entnehmen Sie Details den Abschnitten PSQL chapter for DECLARE VARIABLE
und DECLARE CURSOR
.
Prozedurhauptteil
Auf den Headerabschnitt folgt der Prozedurhauptteil, 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 Prozedurtext eingebettet werden.
Jeder Benutzer, der mit der Datenbank verbunden ist, kann eine neue gespeicherte Prozedur erstellen. Der Benutzer, der eine gespeicherte Prozedur erstellt, wird zu seinem Besitzer.
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 wä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; -- the statement that sends an output row to the buffer
-- and makes the procedure "selectable"
END
5.8.2. ALTER PROCEDURE
Ändern einer vorhandenen gespeicherten Prozedur
DSQL, ESQL
ALTER PROCEDURE procname [(<inparam> [, <inparam> ...])] [RETURNS (<outparam> [, <outparam> ...])] AS [<declarations>] BEGIN [<PSQL_statements>] END <inparam> ::= <param_decl> [{= | DEFAULT} <value>] <outparam> ::= <param_decl> <param_decl> ::= paramname <type> [NOT NULL] [COLLATE collation] <type> ::= <datatype> | [TYPE OF] domain | TYPE OF COLUMN rel.col <datatype> ::= {SMALLINT | INT[EGER] | BIGINT} | {FLOAT | DOUBLE PRECISSION} | {DATE | TIME | TIMESTAMP} | {DECIMAL | NUMERIC} [(precision [, scale])] | {CHAR | CHARACTER} [VARYING] | VARCHAR} [(size)] [CHARACTER SET charset] | {NCHAR | NATIONAL {CHARACTER | CHAR} [VARYING] [(size)] | BLOB [SUB_TYPE {subtype_num | subtype_name}] [SEGMENT SIZE seglen] [CHARACTER SET charset] | BLOB [(seglen [, subtype_num])] <declarations> ::= {<declare_var> | <declare_cursor>}; [{<declare_var> | <declare_cursor>}; …]
Parameter | Beschreibung |
---|---|
procname |
Der Name einer existierenden gespeicherten Prozedur |
inparam |
Beschreibung der Eingabeparameter |
outparam |
Beschreibung der Ausgangsparameter |
declarations |
Abschnitt zum Deklarieren von lokalen Variablen und benannten Cursorn |
declare_var |
Lokale Variablendeklaration |
declare_cursor |
Benannte Cursor-Deklaration |
PSQL_statements |
Prozedurale SQL-Anweisungen |
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. Dieser kann aus bis zu 31 Zeichen bestehen. Der Name des Parameters muss unter den Eingabe- und Ausgabeparametern der Prozedur und ihren lokalen Variablen eindeutig sein |
datatype |
SQL-Datentyp |
collation |
Sortierfolge |
domain |
Domain-Name |
rel |
Tabellen- oder View-Name |
col |
Spaltenname einer Tabelle oder View |
precision |
Die Gesamtanzahl der signifikanten Stellen, die der Parameter halten kann (1..18) |
scale |
Die Anzahl der Stellen nach dem Dezimalpunkt (0..precision) |
size |
Die maximale Größe eines Zeichenfolgentypparameters oder einer Variablen in Zeichen |
charset |
Zeichensatz eines String-Typ-Parameters oder einer Variablen |
subtype_num |
Subtyp-Nummer eines |
subtype_name |
Mnemonischer Name eines |
seglen |
Segmentgröße (max. 65535) |
Die Anweisung ALTER PROCEDURE
erlaubt folgende Änderungen der Definition für gespeicherte Prozeduren:
-
Satz und die Eigenschaften von Eingangs- und Ausgangsparametern
-
lokale Variablen
-
Code im Hauptteil der gespeicherten Prozedur
Nach der Ausführung von ALTER PROCEDURE
bleiben vorhandene Berechtigungen erhalten und Abhängigkeiten werden nicht beeinflusst.
Achten Sie darauf, die Anzahl und Art der Eingabe- und Ausgabeparameter in gespeicherten Prozeduren zu ändern.
Bestehender Anwendungscode und Prozeduren und Trigger, die ihn aufrufen, könnten ungültig werden, weil die neue Beschreibung der Parameter mit dem alten Aufrufformat nicht kompatibel ist.
Informationen zum Beheben einer solchen Situation finden Sie im Artikel Das Feld |
Der Prozedureigentümer und Administratoren besitzen die Recht zum Ausführen von ALTER PROCEDURE
.
Ändern der gespeicherten Prozedur GET_EMP_PROJ
ALTER PROCEDURE GET_EMP_PROJ (
EMP_NO SMALLINT)
RETURNS (
PROJ_ID VARCHAR(20))
AS
BEGIN
FOR SELECT
PROJ_ID
FROM
EMPLOYEE_PROJECT
WHERE
EMP_NO = :emp_no
INTO :proj_id
DO
SUSPEND;
END
5.8.3. CREATE OR ALTER PROCEDURE
Erstellen einer neuen gespeicherten Prozedur oder Ändern einer vorhandenen
DSQL
CREATE OR ALTER PROCEDURE procname [(<inparam> [, <inparam> ...])] [RETURNS (<outparam> [, <outparam> ...])] AS [<declarations>] BEGIN [<PSQL_statements>] END
Das vollständige Syntaxdetail finden Sie unter CREATE PROCEDURE
.
Die Anweisung CREATE OR ALTER PROCEDURE
erstellt eine neue gespeicherte Prozedur oder ändert eine vorhandene Prozedur.
Wenn die gespeicherte Prozedur nicht vorhanden ist, wird sie durch das transparente Aufrufen einer Anweisung CREATE PROCEDURE
erstellt.
Wenn die Prozedur bereits vorhanden ist, wird sie geändert und kompiliert, ohne die vorhandenen Berechtigungen und Abhängigkeiten zu beeinträchtigen.
Creating or altering the GET_EMP_PROJ
procedure.
CREATE OR ALTER PROCEDURE GET_EMP_PROJ (
EMP_NO SMALLINT)
RETURNS (
PROJ_ID VARCHAR(20))
AS
BEGIN
FOR SELECT
PROJ_ID
FROM
EMPLOYEE_PROJECT
WHERE
EMP_NO = :emp_no
INTO :proj_id
DO
SUSPEND;
END
5.8.4. DROP PROCEDURE
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 Löschversuch fehl und der entsprechende Fehler wird ausgelöst.
Der Prozedureigentümer und Administratoren haben die Berechtigung, DROP PROCEDURE
zu verwenden.
Löschen der gespeicherten Prozedur GET_EMP_PROJ
DROP PROCEDURE GET_EMP_PROJ;
5.8.5. RECREATE PROCEDURE
Eine neue gespeicherte Prozedur erstellen oder eine vorhandene wiederherstellen
DSQL
RECREATE PROCEDURE procname [(<inparam> [, <inparam> ...])] [RETURNS (<outparam> [, <outparam> ...])] AS [<declarations>] BEGIN [<PSQL_statements>] END
Das vollständige Syntaxdetail finden Sie unter CREATE PROCEDURE
.
Die Anweisung RECREATE PROCEDURE
erstellt eine neue gespeicherte Prozedur oder erstellt eine vorhandene Prozedur neu.
Wenn es bereits eine Prozedur mit diesem Namen gibt, versucht die Engine diese zu löschen und eine neue zu erstellen.
Das Wiederherstellen einer vorhandenen Prozedur schlägt bei der Anforderung COMMIT
fehl, wenn die Prozedur Abhängigkeiten aufweist.
Beachten Sie, dass Abhängigkeitsfehler erst in der Phase |
Nachdem eine Prozedur erfolgreich neu erstellt wurde, werden Berechtigungen zum Ausführen der gespeicherten Prozedur und die Berechtigungen der gespeicherten Prozedur selbst gelöscht.
Erstellen der neuen gespeicherten Prozedur GET_EMP_PROJ
oder Wiederherstellen 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. EXTERNAL FUNCTION
ÜBERPRÜFUNGSSTATUS
Alle Abschnitte von diesem Punkt bis zum Ende des Kapitels warten auf eine technische und redaktionelle Überprüfung. |
Externe Funktionen, die auch als “benutzerdefinierte Funktionen” (UDFs) bezeichnet werden, sind Programme, die in einer externen Programmiersprache geschrieben und in dynamisch geladenen Bibliotheken gespeichert werden. Sobald sie in einer Datenbank deklariert sind, werden sie in dynamischen und prozeduralen Anweisungen verfügbar, als wären sie intern in der SQL-Sprache implementiert.
Externe Funktionen erweitern die Möglichkeiten zur Datenverarbeitung mit SQL erheblich.
Um eine Funktion für eine Datenbank verfügbar zu machen, wird sie mit der Anweisung DECLARE EXTERNAL FUNCTON
deklariert.
Die Bibliothek, die eine Funktion enthält, wird geladen, wenn eine darin enthaltene Funktion aufgerufen wird.
Externe Funktionen können in mehr als einem Bibliotheks- oder “-Modul” enthalten sein, wie es in der Syntax erwähnt wird. |
5.9.1. DECLARE EXTERNAL FUNCTION
Deklarieren einer benutzerdefinierten Funktion (UDF) zur Datenbank
DSQL, ESQL
DECLARE EXTERNAL FUNCTION funcname [<arg_type_decl> [, <arg_type_decl> ...]] RETURNS { <sqltype> [BY {DESCRIPTOR | VALUE}] | CSTRING(length) | PARAMETER param_num } [FREE_IT] ENTRY_POINT 'entry_point' MODULE_NAME 'library_name' <arg_type_decl> ::= <sqltype> [{BY DESCRIPTOR} | NULL] | CSTRING(length) [NULL]
Parameter | Beschreibung |
---|---|
funcname |
Funktionsname in der Datenbank.
Es kann aus bis zu 31 Zeichen bestehen.
Es sollte unter allen internen und externen Funktionsnamen in der Datenbank eindeutig sein und nicht mit dem Namen übereinstimmen, der aus der UDF-Bibliothek über |
entry_point |
Der exportierte Name der Funktion |
library_name |
Der Name des Moduls ( |
sqltype |
SQL-Datentyp. Es kann kein Array oder Array-Element sein |
length |
Die maximale Länge einer nullterminierten Zeichenfolge, angegeben in Bytes |
param_num |
Die Nummer des Eingabeparameters, von 1 in der Liste der Eingabeparameter in der Deklaration nummeriert, beschreibt den Datentyp, 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 soll.
Es gibt keine Notwendigkeit, UDFs zu deklarieren, die niemals verwendet werden.
Der Name der externen Funktion muss unter allen Funktionsnamen eindeutig sein.
Es kann sich vom exportierten Namen der Funktion unterscheiden, wie im ENTRY_POINT
-Argument angegeben.
DECLARE EXTERNAL FUNCTION
-Eingabeparameter
Die Eingabeparameter der Funktion folgen dem Namen der Funktion und sind durch Kommas getrennt.
Für jeden Parameter ist ein SQL-Datentyp angegeben.
Arrays können nicht als Funktionsparameter verwendet werden.
Neben den SQL-Typen steht der CSTRING
-Typ für die Angabe einer nullterminierten Zeichenfolge mit einer maximalen Länge von LENGTH
Bytes zur Verfügung.
Standardmäßig werden Eingabeparameter durch Referenz übergeben.
Die BY DESCRIPTOR
-Klausel kann stattdessen angegeben werden, wenn der Eingabeparameter durch den Deskriptor übergeben wird.
Das Übergeben eines Parameters nach Deskriptor ermöglicht die Verarbeitung von NULL
s.
Klauseln und Schlüsselwörter
RETURNS
-Klausel-
(Erforderlich) gibt den von der Funktion zurückgegebenen Ausgabeparameter an. Eine Funktion ist skalar: Sie gibt nur einen Parameter zurück. Der Ausgabeparameter kann von einem beliebigen SQL-Typ (außer einem Array oder einem Array-Element) oder einer nullterminierten Zeichenfolge (
CSTRING
) sein. Der Ausgabeparameter kann durch Referenz (Standard), Deskriptor oder Wert übergeben werden. Wenn die KlauselBY DESCRIPTOR
angegeben ist, wird der Ausgabeparameter von Deskriptor übergeben. Wenn dieBY VALUE
-Klausel angegeben ist, wird der Ausgabeparameter über den Wert übergeben. PARAMETER
-Schlüsselwort-
gibt an, dass die Funktion den Wert aus dem Parameter an Stelle param_num zurückgibt. Es ist notwendig, wenn Sie einen Wert des Datentyps
BLOB
zurückgeben müssen. FREE_IT
Schlüsselwort-
bedeutet, dass der Speicher, der zum Speichern des Rückgabewerts zugewiesen wurde, freigegeben wird, nachdem die Funktion ausgeführt wurde. Sie wird nur verwendet, wenn der Speicher in der UDF dynamisch zugewiesen wurde. In einer solchen UDF muss der Speicher mit Hilfe der Funktion
ib_util_malloc
aus demib_util
-Modul zugewiesen werden, was die Kompatibilität mit den im Firebird-Code verwendeten Funktionen und 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, der aus dem Modul exportiert wird.
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 Unterverzeichnis
../UDF
des Firebird-Serverstammes) oder an einem infirebird.conf
explizit konfigurierten Speicherort befindet, ist es einfacher, die Datenbank zwischen verschiedene Plattformen. Der Parameter UDFAccess in der Datei firebird.conf ermöglicht die Konfiguration von Zugriffsbeschränkungen für externe Funktionsmodule.
Jeder Benutzer, der mit der Datenbank verbunden ist, kann eine externe Funktion (UDF) deklarieren.
Beispiele zuv Verwendung von DECLARE EXTERNAL FUNCTION
-
Deklarieren der externen
addDay
-Funktion imfbudf
-Modul. Die Ein- und Ausgabeparameter werden als Referenz übergeben.DECLARE EXTERNAL FUNCTION addDay TIMESTAMP, INT RETURNS TIMESTAMP ENTRY_POINT 'addDay' MODULE_NAME 'fbudf';
-
Deklaration der externen
Invl
-Funktion imfbudf
-Modul. Die Ein- und Ausgabeparameter werden vom Deskriptor übergeben.DECLARE EXTERNAL FUNCTION invl INT BY DESCRIPTOR, INT BY DESCRIPTOR RETURNS INT BY DESCRIPTOR ENTRY_POINT 'idNvl' MODULE_NAME 'fbudf';
-
Declaring the
isLeapYear
external function located in thefbudf
module. The input parameter is passed by reference, while the output parameter is passed by value.DECLARE EXTERNAL FUNCTION isLeapYear TIMESTAMP RETURNS INT BY VALUE ENTRY_POINT 'isLeapYear' MODULE_NAME 'fbudf';
-
Deklaration der externen Funktion
i64Truncate
imfbudf
-Modul. Die Ein- und Ausgabeparameter werden vom Deskriptor übergeben. Der zweite Parameter der Funktion wird als Rückgabewert verwendet.DECLARE EXTERNAL FUNCTION i64Truncate NUMERIC(18) BY DESCRIPTOR, NUMERIC(18) BY DESCRIPTOR RETURNS PARAMETER 2 ENTRY_POINT 'fbtruncate' MODULE_NAME 'fbudf';
5.9.2. ALTER EXTERNAL FUNCTION
Ändern des Eintrittspunkts 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 Eintrittspunkt und / oder den Modulnamen für eine benutzerdefinierte Funktion (UDF).
Vorhandene Abhängigkeiten bleiben erhalten, nachdem die Anweisung mit der Änderung[s] ausgeführt wurde.
- Die
ENTRY_POINT
-Klausel -
dient zur Angabe des neuen Eintrittspunktes (der Name der Funktion, die aus dem Modul exportiert wird).
- Die
MODULE_NAME
-Klausel -
Gibt den neuen Namen des Moduls an, in dem sich die exportierte Funktion befindet.
Jeder an die Datenbank angeschlossene Benutzer kann den Eintrittspunkt und den Modulnamen ändern.
-
Ändern des Einstiegspunkts für eine externe Funktion
ALTER EXTERNAL FUNCTION invl ENTRY_POINT 'intNvl';
-
Ändern des Modulnamens für eine externe Funktion
ALTER EXTERNAL FUNCTION invl MODULE_NAME 'fbudf2';
5.9.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 es Abhängigkeiten von der externen Funktion gibt, schlägt die Anweisung fehl und der entsprechende Fehler wird ausgelöst.
Jeder mit der Datenbank verbundene Benutzer kann die Deklaration einer internen Funktion löschen.
DROP EXTERNAL FUNCTION
Löschen der Deklaration der addDay
-Funktion.
DROP EXTERNAL FUNCTION addDay;
5.10. FILTER
Ein BLOB FILTER
-Filter ist ein Datenbankobjekt, das eigentlich ein spezieller Typ einer externen Funktion ist, mit dem alleinigen Zweck, ein BLOB
-Objekt in einem Format zu verwenden und es zu konvertieren zu einem BLOB
-Objekt in einem anderen Format.
Die Formate der BLOB
-Objekte werden mit benutzerdefinierten BLOB
-Subtypen angegeben.
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äre Datentypen.
5.10.1. DECLARE FILTER
Deklarieren eines BLOB
-Filters zur 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 |
Name des Filters in der Datenbank.
Es kann aus bis zu 31 Zeichen bestehen.
Es muss nicht derselbe Name sein, wie der aus der Filterbibliothek via |
sub_type |
|
number |
|
mnemonic |
|
function_name |
Der exportierte Name (Einstiegspunkt) der Funktion |
library_name |
Der Name des Moduls, in dem sich der Filter befindet |
user_defined |
Benutzerdefinierter |
Mit der Anweisung DECLARE FILTER
wird ein BLOB
-Filter für die Datenbank verfügbar.
Der Name des BLOB
-Filters muss unter den Namen von BLOB
-Filtern eindeutig sein.
Spezifizieren der Subtypen
Die Subtypen können als Untertypnummer oder als Subtyp-Mnemonikname angegeben werden.
Benutzerdefinierte Subtypen müssen durch negative Zahlen (von -1 bis -32.768) dargestellt werden.
Ein Versuch, mehr als einen BLOB
-Filter mit derselben Kombination der Ein- 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 definiert den
BLOB
-Subtyp des zu erstellenden Objekts.
Mnemonische Namen können für benutzerdefinierte
Nachdem die Transaktion bestätigt wurde, können die mnemonischen Namen in Deklarationen verwendet werden, wenn Sie neue Filter erstellen. Der Wert der Spalte Warning
Ab Firebird 3 sind die Systemtabellen nicht mehr von Benutzern beschreibbar.
However, inserting custom types into |
Parameters
ENTRY_POINT
-
Klausel, die den Namen des Einstiegspunkts (den Namen der importierten Funktion) im Modul definiert.
MODULE_NAME
-
Die Klausel definiert den Namen des Moduls, in dem sich die exportierte Funktion befindet. Standardmäßig müssen sich die Module im UDF-Ordner des Stammverzeichnisses auf dem Server befinden. Der Parameter UDFAccess in
firebird.conf
ermöglicht das Bearbeiten von Zugriffsbeschränkungen für Filterbibliotheken.
Jeder Benutzer, der mit der Datenbank verbunden ist, kann einen BLOB-Filter deklarieren.
Beispiele
-
Erstellen eines
BLOB
-Filters mit Subtypnummern.DECLARE FILTER DESC_FILTER INPUT_TYPE 1 OUTPUT_TYPE -4 ENTRY_POINT 'desc_filter' MODULE_NAME 'FILTERLIB';
-
Erstellen eines
BLOB
-Filters mit Untertyp-Mnemoniknamen.DECLARE FILTER FUNNEL INPUT_TYPE blr OUTPUT_TYPE text ENTRY_POINT 'blr2asc' MODULE_NAME 'myfilterlib';
5.10.2. DROP FILTER
Entfernen einer BLOB
-Filterdeklaration aus der Datenbank
DSQL, ESQL
DROP FILTER filtername
Parameter | Beschreibung |
---|---|
filtername |
Filtername in der Datenbank |
Die Anweisung DROP FILTER
entfernt die Deklaration eines BLOB
-Filters aus der Datenbank.
Wenn Sie einen BLOB
-Filter aus einer Datenbank entfernen, kann er für diese Datenbank nicht mehr verwendet werden.
Die dynamische Bibliothek, in der sich die Konvertierungsfunktion befindet, bleibt erhalten und das Entfernen aus einer Datenbank hat keine Auswirkungen auf andere Datenbanken, in denen derselbe BLOB
-Filter noch deklariert ist.
Jeder Benutzer, der mit der Datenbank verbunden ist, kann einen BLOB-Filter löschen.
Löschen eines BLOB
-Filters.
DROP FILTER DESC_FILTER;
5.11. SEQUENCE (GENERATOR)
Eine Sequenz oder ein Generator ist ein Datenbankobjekt, das verwendet wird, um eindeutige Zahlenwerte zu erhalten, um eine Reihe zu füllen. “Sequenz” ist der SQL-konforme Begriff für das gleiche Ding, das in Firebird traditionell als “Generator” bekannt ist. Beide Begriffe sind in Firebird implementiert. Für beide Terme ist eine Syntax implementiert.
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. Das Übergeben eines Sequenzwerts an ein 32-Bit-Feld oder eine Variable führt nicht zu Fehlern, solange der aktuelle Wert der Sequenz die Grenzen einer 32-Bit-Zahl nicht überschreitet. Sobald jedoch der Sequenzwert diese Grenze überschreitet, erzeugt die Datenbank in Dialekt 3 einen Fehler. Eine Datenbank in Dialekt 1 wird weiterhin die Werte beschneiden, was die Einzigartigkeit der Serie beeinträchtigt. |
In diesem Abschnitt wird beschrieben, wie Sequenzen erstellt, festgelegt und gelöscht werden.
5.11.1. CREATE SEQUENCE (GENERATOR)
Erstellen einer neuen SEQUENCE
(GENERATOR
)
DSQL, ESQL
CREATE {SEQUENCE | GENERATOR} seq_name
Parameter | Beschreibung |
---|---|
seq_name |
Name der Sequenz (Generator). Diese kann aus bis zu 31 Zeichen bestehen |
Die Anweisungen CREATE SEQUENCE
und CREATE GENERATOR
sind Synonyme — beide erzeugen eine neue Sequenz.
Jede kann verwendet werden, jedoch wird CREATE SEQUENCE
empfohlen, sofern die normkonforme Metadatenverwaltung wichtig ist.
Wenn eine Sequenz erstellt wird, wird ihr Wert auf 0 gesetzt.
Jedes Mal, wenn der NEXT VALUE FOR seq_name
wird der Wert um 1 erhöht.
Die GEN_ID(seq_name, <step>)
-Funktion kann stattdessen aufgerufen werden, um die Reihe durch eine andere Ganzzahl zu erhöhen oder zu reduzieren.
Jeder Benutzer, der mit der Datenbank verbunden ist, kann eine Sequenz (Generator) erstellen.
-
Erstellen der
EMP_NO_GEN
-Sequenz mittelsCREATE SEQUENCE
.CREATE SEQUENCE EMP_NO_GEN;
-
Erstellen der
EMP_NO_GEN
-Sequenz mittelsCREATE GENERATOR
.CREATE GENERATOR EMP_NO_GEN;
5.11.2. ALTER SEQUENCE
Festlegen des Werts einer Sequenz oder eines Generators auf einen bestimmten Wert
DSQL
ALTER SEQUENCE seq_name RESTART WITH new_val
Parameter | Beschreibung |
---|---|
seq_name |
Name der Sequenz (Generator) |
new_val |
Neuer Sequenzwert (Generatorwert). Eine 64-Bit-Ganzzahl von -2-63 bis 263-1. |
Mit der Anweisung ALTER SEQUENCE
wird der aktuelle Wert einer Sequenz oder eines Generators auf den angegebenen Wert gesetzt.
Die falsche Verwendung der |
Jeder an die Datenbank angeschlossene Benutzer kann den Sequenzwert (Generator) festlegen.
-
Festlegen des Werts der
EMP_NO_GEN
-Sequenz auf 145.ALTER SEQUENCE EMP_NO_GEN RESTART WITH 145;
-
Das gleiche Prozedere unter Verwendung von
SET GENERATOR
:SET GENERATOR EMP_NO_GEN TO 145;
5.11.3. SET GENERATOR
Festlegen des Werts einer Sequenz oder eines Generators auf einen bestimmten Wert
DSQL, ESQL
SET GENERATOR seq_name TO new_val
Parameter | Beschreibung |
---|---|
seq_name |
Name des Generators (Sequenz) |
new_val |
Neuer Sequenzwert (Generatorwert). Eine 64-Bit-Ganzzahl von -2-63 bis 263-1. |
Mit der Anweisung SET GENERATOR
wird der aktuelle Wert einer Sequenz oder eines Generators auf den angegebenen Wert gesetzt.
Obwohl |
Jeder an die Datenbank angeschlossene Benutzer kann den Sequenzwert (Generator) festlegen.
-
Einstellen des Werts der
EMP_NO_GEN
-Sequenz auf 145:SET GENERATOR EMP_NO_GEN TO 145;
-
Das gleiche Prozedere unter Verwendung von
ALTER SEQUENCE
:ALTER SEQUENCE EMP_NO_GEN RESTART WITH 145;
5.11.4. DROP SEQUENCE (GENERATOR)
Löschen von SEQUENCE
(GENERATOR
)
DSQL, ESQL
DROP {SEQUENCE | GENERATOR} seq_name
Parameter | Beschreibung |
---|---|
seq_name |
Name der Sequenz (Generator). Kann aus bis zu 31 Zeichen bestehen. |
Die Anweisungen DROP SEQUENCE
und DROP GENERATOR
sind gleichwertig: Beide löschen eine vorhandene Sequenz (Generator).
Beide sind gültig, jedoch wird DROP SEQUENCE
empfohlen.
Die Anweisungen schlagen fehl, wenn die Sequenz (Generator) Abhängigkeiten hat.
Jeder an die Datenbank angeschlossene Benutzer kann eine Sequenz (Generator) löschen.
Löschen der EMP_NO_GEN
-Sequenz:
DROP SEQUENCE EMP_NO_GEN;
5.12. EXCEPTION
In diesem Abschnitt wird beschrieben, wie benutzerdefinierte Ausnahmen zur Verwendung in Fehlerbehandlungsroutinen in PSQL-Modulen erstellt, geändert und gelöscht werden.
5.12.1. CREATE EXCEPTION
Erstellen einer neuen Ausnahme für die Verwendung in PSQL-Modulen
DSQL, ESQL
CREATE EXCEPTION exception_name 'message'
Parameter | Beschreibung |
---|---|
exception_name |
Name der Ausnahme. Die maximale Länge beträgt 31 Zeichen |
message |
Standardfehlermeldung. Die maximale Länge beträgt 1.021 Zeichen |
Die Anweisung CREATE EXCEPTION
erstellt eine neue Ausnahme zur Verwendung in PSQL-Modulen.
Wenn eine Ausnahme desselben Namens existiert, schlägt die Anweisung mit einer entsprechenden Fehlermeldung fehl.
Der Name der Ausnahme ist eine Standardkennung. In einer Dialekt 3-Datenbank kann sie in doppelte Anführungszeichen eingeschlossen werden, um Groß- und 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 Einbytezeichensatzes.
Der Text kann im PSQL-Code überschrieben werden, wenn die Ausnahme ausgelöst wird.
Jeder Benutzer, der mit der Datenbank verbunden ist, kann eine Ausnahme erstellen.
-
Erstellen einer Ausnahme mit dem Namen
E_LARGE_VALUE
:CREATE EXCEPTION E_LARGE_VALUE 'The value is out of range';
-
Erstellen einer Ausnahme mit dem Namen
ERROR_REFIN_RATE
:CREATE EXCEPTION ERROR_REFIN_RATE 'Error detected in the spread of discount rates';
Tipps
Die Zusammenfassung von Benutzerdefinierte Ausnahmen werden in der Systemtabelle gespeichert |
5.12.2. ALTER EXCEPTION
Ändern der Nachricht, die von einer benutzerdefinierten Ausnahme zurückgegeben wird
DSQL, ESQL
ALTER EXCEPTION exception_name 'message'
Parameter | Beschreibung |
---|---|
exception_name |
Name der Ausnahme |
message |
Neue Standardfehlermeldung. Die maximale Länge beträgt 1.021 Zeichen |
Die Anweisung ALTER EXCEPTION
kann jederzeit verwendet werden, um den Standardtext der Nachricht zu ändern.
Jeder Benutzer, der mit der Datenbank verbunden ist, kann eine Ausnahmemeldung ändern.
-
Ändern der Standardnachricht für die Ausnahme
E_LARGE_VALUE
:ALTER EXCEPTION E_LARGE_VALUE 'The value exceeds the prescribed limit of 32,765 bytes';
-
Ändern der Standardnachricht für die Ausnahme
ERROR_REFIN_RATE
:ALTER EXCEPTION ERROR_REFIN_RATE 'Rate is outside the allowed range';
5.12.3. CREATE OR ALTER EXCEPTION
Ändern der Nachricht, die von einer benutzerdefinierten Ausnahme zurückgegeben wird, wenn die Ausnahme existiert; andernfalls erstellen Sie eine neue Ausnahme
DSQL
CREATE OR ALTER EXCEPTION exception_name 'message'
Parameter | Beschreibung |
---|---|
exception_name |
Name der Ausnahme |
message |
Fehlermeldung. Die maximale Länge ist auf 1.021 Zeichen begrenzt |
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 eine vorhandene Ausnahme durch diese Anweisung geändert wird, bleiben vorhandene Abhängigkeiten erhalten.
Jeder Benutzer, der mit der Datenbank verbunden ist, kann diese Anweisung verwenden, um eine Ausnahme zu erstellen oder den bereits vorhandenen Text zu ändern.
Nachricht für die Ausnahme ändern E_LARGE_VALUE
:
CREATE OR ALTER EXCEPTION E_LARGE_VALUE
'The value is higher than the permitted range 0 to 32,765';
5.12.4. DROP EXCEPTION
Löschen einer benutzerdefinierten Ausnahme
DSQL, ESQL
DROP EXCEPTION exception_name
Parameter | Beschreibung |
---|---|
exception_name |
Name der Ausnahme |
Die Anweisung DROP EXCEPTION
wird zum Löschen einer Ausnahme verwendet.
Alle Abhängigkeiten von der Ausnahme führen dazu, dass die Anweisung fehlschlägt und nicht gelöscht wird.
Wenn eine Ausnahme nur in gespeicherten Prozeduren verwendet wird, kann sie jederzeit gelöscht werden. Wenn es in einem Auslöser verwendet wird, kann es nicht gelöscht werden.
Bei der Planung, eine Ausnahme zu löschen, sollten alle Verweise darauf aus dem Code der gespeicherten Prozeduren entfernt werden, um zu vermeiden, dass die Abwesenheit Fehler verursacht.
Jeder Benutzer, der mit der Datenbank verbunden ist, kann eine Ausnahme löschen.
-
Ausnahme löschen
ERROR_REFIN_RATE
:DROP EXCEPTION ERROR_REFIN_RATE;
-
Ausnahme löschen
E_LARGE_VALUE
:DROP EXCEPTION E_LARGE_VALUE;
5.12.5. RECREATE EXCEPTION
Eine neue benutzerdefinierte Ausnahme oder eine bestehende erstellen
DSQL
RECREATE EXCEPTION exception_name 'message'
Parameter | Beschreibung |
---|---|
exception_name |
Name der Ausnahme. Die maximale Länge beträgt 31 Zeichen |
message |
Fehlermeldung. Die maximale Länge ist auf 1.021 Zeichen begrenzt |
Die Anweisung RECREATE EXCEPTION
erstellt eine neue Ausnahme für die Verwendung in PSQL-Modulen.
Wenn bereits eine Exception mit demselben Namen existiert, versucht die Anweisung RECREATE EXCEPTION
, diese zu löschen und eine neue zu erstellen.
Wenn es Abhängigkeiten zur bestehenden Ausnahme gibt, schlägt die versuchte Löschung fehl und RECREATE EXCEPTION
wird nicht ausgeführt.
Jeder Benutzer, der mit der Datenbank verbunden ist, kann eine Ausnahme erstellen.
Wiederherstellen der Ausnahme E_LARGE_VALUE
:
RECREATE EXCEPTION E_LARGE_VALUE
'The value exceeds its limit';
5.13. COLLATION
5.13.1. CREATE COLLATION
Erstellen einer neuen Sortierung für einen unterstützten Zeichensatz, der für die Datenbank verfügbar ist
DSQL
CREATE COLLATION collname FOR charset [FROM basecoll | FROM 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 Name, der für die neue Collation verwendet werden soll. Die maximale Länge beträgt 31 Zeichen |
charset |
Ein in der Datenbank vorhandener Zeichensatz |
basecoll |
Eine bereits in der Datenbank vorhandene Collation |
extname |
Der in der Datei |
Die Anweisung CREATE COLLATION
“erzeugt” nichts: Sie dient dazu, eine Datenbank-Collation bekannt zu machen.
Die Collation muss bereits auf dem System vorhanden sein, normalerweise in einer Bibliotheksdatei und muss ordnungsgemäß in einer .conf
-Datei im Unterverzeichnis intl
der Firebird-Installation registriert sein.
Die Collation kann alternativ auf einer basieren, die bereits in der Datenbank vorhanden ist.
Wie die Engine die Collation erkennt
Wenn keine FROM
-Klausel vorhanden ist, scannt Firebird die .conf
-Dateien im intl
-Unterverzeichnis nach einer Collation mit dem Namen, der als Objekt mit CREATE COLLATION
gesetzt wurde.
Anders ausgedrückt, das Weglassen der FROM basecoll
-Klausel entspricht der Angabe von FROM EXTERNAL ('collname')
.
Bei der Angabe von extname muss die Groß- / Kleinschreibung beachtet werden und sie muss genau mit dem Collations-Namen in der Datei .conf
übereinstimmen.
Bei den Parametern collname, charset und basecoll wird zwischen Groß- und Kleinschreibung unterschieden, sofern sie nicht in doppelten Anführungszeichen eingeschlossen sind.
Spezifische Attribute
Die verfügbaren spezifischen Attribute sind in der folgenden Tabelle aufgeführt. Nicht alle spezifischen Attribute gelten für jede Collation auch wenn sie nicht durch einen Fehler verursacht werden.
Spezifische Attribute unterscheiden Groß- und Kleinschreibung. |
In der Tabelle zeigt “1 bpc” an, dass ein Attribut für Collationen von Zeichensätzen mit 1 Byte pro Zeichen (sogenannte enge Zeichensätze) gültig ist. “UNI” steht für “UNICODE Collationen”.
Atrribute | Wert | Gültig für | Kommentar |
---|---|---|---|
|
|
1 bpc |
Deaktiviert Komprimierungen (a.k.a. Kontraktionen). Komprimierungen bewirken, dass bestimmte Zeichenfolgen als atomare Einheiten sortiert werden, z.B. spanisch c + h als einzelner Buchstabe ch |
|
|
1 bpc |
Deaktiviert Erweiterungen. Erweiterungen bewirken, dass bestimmte Zeichen (z. B. Ligaturen oder umlautete Vokale) als Zeichenfolgen behandelt und entsprechend sortiert werden |
|
default oder H.U |
UNI |
Gibt die zu verwendende ICU-Bibliotheksversion an.
Gültige Werte sind diejenigen, die im anwendbaren Element <intl_module> in |
|
xx_YY |
UNI |
Gibt das Sortierungskriterium an. Benötigt eine vollständige Version von ICU-Bibliotheken. Format: eine Gebietsschema-Zeichenfolge wie “du_NL” (nicht angegeben) |
|
|
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 ist auch als natürliche Sortierung bekannt) |
|
|
1 bpc |
Ordnet Sonderzeichen (Leerzeichen, Symbole etc.) vor alphanumerischen Zeichen |
Deklarieren Sie die gespeicherte Prozedur Damit dies funktioniert, muss der Zeichensatz auf dem System vorhanden und in einer |
Jeder Benutzer, der mit der Datenbank verbunden ist, kann CREATE COLLATION
verwenden, um eine neue Collation hinzuzufügen.
Beispiele für die Verwendung von CREATE COLLATION
-
Erstellen einer Collation mit dem in der Datei
fbintl.conf
enthaltenen Namen (Groß- und Kleinschreibung beachten).CREATE COLLATION ISO8859_1_UNICODE FOR ISO8859_1;
-
Erstellen einer Collation, mithilfe 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 Collation, die Groß- und Kleinschreibung nicht berücksichtigt und auf einer bereits in der Datenbank vorhandenen basiert.
CREATE COLLATION ES_ES_NOPAD_CI FOR ISO8859_1 FROM ES_ES NO PAD CASE INSENSITIVE;
-
Erstellen einer Collation, die Groß- und Kleinschreibung nicht berücksichtigt und auf einer bereits in der Datenbank vorhandenen basiert. Angabe spezifischer Attribute.
CREATE COLLATION ES_ES_CI_COMPR FOR ISO8859_1 FROM ES_ES CASE INSENSITIVE 'DISABLE-COMPRESSIONS=0';
-
Erstellen einer Collation, die Groß- und Kleinschreibung nicht berücksichtigt durch Verwendung von Zahlen (die sogenannte natürliche Collation).
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 (manufacturer) numbers CREATE TABLE wares(id int primary key, articul dm_nums ...);
5.13.2. DROP COLLATION
Löschen einer Collation aus der Datenbank
DSQL
DROP COLLATION collname
Parameter | Beschreibung |
---|---|
collname |
Name der Collation |
Die Anweisung DROP COLLATION
entfernt die angegebene Collation aus der Datenbank, falls vorhanden.
Ein Fehler wird ausgelöst, wenn die angegebene Collation nicht vorhanden ist.
Deklarieren und führen Sie die gespeicherte Prozedur |
Jeder Benutzer, der mit der Datenbank verbunden ist, kann DROP COLLATION
verwenden, um eine Collation zu entfernen.
DROP COLLATION
Löschen der ES_ES_NOPAD_CI
-Collation.
DROP COLLATION ES_ES_NOPAD_CI;
5.14. CHARACTER SET
5.14.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 Sortierung |
Die Anweisung ALTER CHARACTER SET
ändert die Standardsortierung für den angegebenen Zeichensatz.
Dies wirkt sich auf die zukünftige Verwendung des Zeichensatzes aus, mit Ausnahme der Fälle, in denen die COLLATE
-Klausel explizit überschrieben wird.
In diesem Fall bleibt die Sortierreihenfolge bestehender Domainn, Spalten und PSQL-Variablen nach der Änderung der Standardsortierung des zugrunde liegenden Zeichensatzes erhalten.
Hinweise
Wenn Sie die Standardsortierung für den Datenbankzeichensatz ändern (die beim Erstellen der Datenbank definiert wurde), wird die Standardsortierung für die Datenbank geändert. Wenn Sie die Standardsortierung für den Zeichensatz ändern, der während der Verbindung angegeben wurde, werden Stringkonstanten gemäß dem neuen Sortierungswert interpretiert, außer in den Fällen, in denen der Zeichensatz und / oder die Collation überschrieben wurde. |
Festlegen der Standard-UNICODE_CI_AI
-Sortierung für die UTF8
-Codierung.
ALTER CHARACTER SET UTF8
SET DEFAULT COLLATION UNICODE_CI_AI;
5.15. ROLE
Eine Rolle ist ein Datenbankobjekt, das eine Reihe SQL-Berechtigungen paketiert. Rollen implementieren das Konzept der Zugriffskontrolle auf Gruppenebene. Der Rolle werden mehrere Berechtigungen erteilt, und diese Rolle kann einem oder mehreren Benutzern gewährt oder widerrufen werden.
Ein Benutzer, dem eine Rolle zugewiesen wurde, muss diese Rolle in seinen Anmeldeinformationen bereitstellen, um die zugehörigen Berechtigungen auszuüben. Alle anderen Berechtigungen, die dem Benutzer gewährt werden, sind von seiner Anmeldung mit der Rolle nicht betroffen. Die gleichzeitige Anmeldung mit mehreren Rollen wird nicht unterstützt.
In diesem Abschnitt werden die Aufgaben zum Erstellen und Löschen von Rollen besprochen.
5.15.1. CREATE ROLE
Erstellen eines neuen ROLE
-Objektes
DSQL, ESQL
CREATE ROLE rolename
Parameter | Beschreibung |
---|---|
rolename |
Rollenname. Die maximale Länge beträgt 31 Zeichen |
Die Anweisung CREATE ROLE
erstellt ein neues Rollenobjekt, dem später ein oder mehrere Berechtigungen erteilt werden können.
Der Name einer Rolle muss unter den Namen der Rollen in der aktuellen Datenbank eindeutig sein.
Es ist ratsam, den Namen einer Rolle auch unter den Benutzernamen eindeutig zu machen. Das System verhindert nicht die Erstellung einer Rolle, deren Name mit einem vorhandenen Benutzernamen kollidiert. Wenn dies der Fall ist, kann der Benutzer keine Verbindung zur Datenbank herstellen. |
Jeder Benutzer, der mit der Datenbank verbunden ist, kann eine Rolle erstellen. Der Benutzer, der eine Rolle erstellt, wird zu seinem Besitzer.
Erstellen einer Rolle SELLERS
:
CREATE ROLE SELLERS;
5.15.2. ALTER ROLE
ALTER ROLE
hat keinen Platz im create-alter-drop-Paradigma für Datenbankobjekte, da eine Rolle keine Attribute besitzt, die geändert werden können.
Sein tatsächlicher Effekt besteht darin, ein Attribut der Datenbank zu ändern: Firebird verwendet es, um die Fähigkeit von Windows Adminstratoren zu aktivieren und zu deaktivieren, beim Anmelden automatisch Administratorrechte zu erhalten.
Diese Prozedur trifft nur auf eine Rolle zu: Die systemgenerierte Rolle RDB$ADMIN
, die in jeder Datenbank von ODS 11.2 oder höher vorhanden ist.
Bei der Aktivierung dieser Funktion sind mehrere Faktoren beteiligt.
Für weitere Details, siehe AUTO ADMIN MAPPING im Kapitel Sicherheit.
5.15.3. DROP ROLE
Löschen einer Rolle
DSQL, ESQL
DROP ROLE rolename
Die Anweisung DROP ROLE
löscht eine vorhandene Rolle.
Es braucht nur ein einziges Argument, den Namen der Rolle.
Sobald die Rolle gelöscht wurde, wird der gesamte Berechtigungssatz von allen Benutzern und Objekten, denen die Rolle gewährt wurde, widerrufen.
Eine Rolle kann vom Eigentümer gelöscht oder von einem Administrator.
Löschen der Rolle SELLERS
:
DROP ROLE SELLERS;
5.16. COMMENTS
Datenbankobjekte und eine Datenbank selbst können Kommentare enthalten.
Es ist ein bequemer Mechanismus zur Dokumentation der Entwicklung und Pflege einer Datenbank.
Kommentare, die mit COMMENT ON
erstellt wurden, überstehen eine gbak-Sicherung und -Wiederherstellung.
5.16.1. COMMENT ON
Documentation von Metadaten
DSQL
COMMENT ON <object> IS {'sometext' | NULL} <object> ::= DATABASE | <basic-type> objectname | COLUMN relationname.fieldname | PARAMETER procname.paramname <basic-type> ::= CHARACTER SET | COLLATION | DOMAIN | EXCEPTION | EXTERNAL FUNCTION | FILTER | GENERATOR | INDEX | PROCEDURE | ROLE | SEQUENCE | TABLE | TRIGGER | VIEW
Parameter | Beschreibung |
---|---|
sometext |
Kommentartext |
basic-type |
Metadatenobjekttyp |
objectname |
Name des Metadatenobjekts |
relationname |
Name der Tabelle oder View |
procname |
Name der Stored Procedure |
paramname |
Name des Stored Procedure Parameters |
Die Anweisung COMMENT ON
fügt Kommentare zu Datenbankobjekten (Metadaten) hinzu.
Kommentare werden in Textfeldern des Typs BLOB
in der Spalte RDB$DESCRIPTION
der entsprechenden Systemtabellen gespeichert.
Clientanwendungen können Kommentare aus diesen Feldern anzeigen.
Wenn Sie einen leeren Kommentar (“ |
Der Eigentümer der Tabelle oder Prozedur und Administratoren haben die notwendigen Berechtigungen die Anweisung COMMENT ON
zu verwenden.
COMMENT ON
-
Einen Kommentar für die aktuelle Datenbank hinzufügen
COMMENT ON DATABASE IS 'It is a test (''my.fdb'') database';
-
Einen Kommentar für die
METALS
-Tabelle hinzufügenCOMMENT ON TABLE METALS IS 'Metal directory';
-
Hinzufügen eines Kommentars zum Feld
ISALLOY
in derMETALS
-TabelleCOMMENT ON COLUMN METALS.ISALLOY IS '0 = fine metal, 1 = alloy';
-
Einen Kommentar für einen Parameter hinzufügen
COMMENT ON PARAMETER ADD_EMP_PROJ.EMP_NO IS 'Employee ID';
6. Statements der Data Manipulation Language (DML)
ÜBERPRÜFUNGSSTATUS
Alle Abschnitte von diesem Punkt bis zum Ende des Kapitels warten auf technische und redaktionelle Überprüfung. |
DML — Datenbearbeitungssprache — ist die Teilmenge von SQL, die von Anwendungen und prozeduralen Modulen zum Extrahieren und Ändern von Daten verwendet wird.
Die Extraktion zum Lesen sowohl roher als auch manipulierter Daten wird mit der Anweisung SELECT
erreicht.
INSERT
dient zum Hinzufügen neuer Daten und DELETE
dient zum Löschen von Daten, die nicht mehr benötigt werden.
UPDATE
, MERGE
und UPDATE OR INSERT
ändern alle Daten auf verschiedene Arten.
6.1. SELECT
Retrieving data
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>]] [FOR UPDATE [OF <columns>]] [WITH LOCK] [INTO <variables>] <variables> ::= [:]varname [, [:]varname ...]
Beschreibung
Die Anweisung SELECT
ruft Daten aus der Datenbank ab und übergibt sie an die Anwendung oder die umschließende SQL-Anweisung.
Daten werden in null oder mehr Zeilen zurückgegeben, die jeweils eine oder mehrere Spalten oder Felder enthalten.
Die Summe der zurückgegebenen Zeilen ist die Ergebnismenge der Anweisung.
Die einzigen obligatorischen Teile der Anweisung SELECT
sind:
-
Das Schlüsselwort
SELECT
gefolgt von einer Spaltenliste. Dieser Teil spezifiziert was Sie abrufen möchten. -
Das Schlüsselwort
FROM
gefolgt von einem auswählbaren Objekt. Dies sagt der Engine von wo Sie Daten erhalten möchten.
In der einfachsten Form ruft SELECT
eine Anzahl von Spalten aus einer einzelnen Tabelle oder Sicht ab, wie folgt:
select id, name, address
from contacts
Oder, um alle Spalten abzurufen:
select * from sales
In der Praxis werden die abgerufenen Zeilen oft durch eine Klausel WHERE
begrenzt.
Die Ergebnismenge kann nach einer ORDER BY
-Klausel sortiert werden.
FIRST
, SKIP
oder ROWS
können die Anzahl der Ausgabezeilen eingrenzen.
Die Spaltenliste kann alle Arten von Ausdrücken anstelle von nur Spaltennamen 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 einer JOIN
kombiniert werden, und mehrere Ergebnismengen können in einer UNION
kombiniert werden.
In den folgenden Abschnitten werden die verfügbaren SELECT
Unterklauseln und ihre Verwendung im Detail erläutert.
6.1.1. FIRST
, SKIP
Abrufen eines Teiles von Zeilen aus einer geordneten Menge
DSQL, PSQL
SELECT [FIRST <m>] [SKIP <n>] FROM ... ... <m>, <n> ::= <integer-literal> | <query-parameter> | (<integer-expression>)
Argument | Beschreibung |
---|---|
integer literal |
Ganzzahliges Literal |
query parameter |
Abfrageparameter-Platzhalter.
|
integer-expression |
Ausdruck, der einen Ganzzahlwert zurückgibt |
FIRST und SKIP sind keine Standardsyntax
|
Beschreibung
FIRST
begrenz die Ausgabe der Abfrage auf die ersten m Zeilen.
SKIP
übergeht die ersten n Zeilen, bevor mit der Ausgabe begonnen wird.
FIRST
und SKIP
sind optional.
Bei Verwendung in “FIRST m SKIP n
” werden die obersten n Zeilen der Ausgabe verworfen und die ersten m Zeilen des Rests der 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 stehen. Dies bedeutet, dass ein Unterabfrageausdruck in zwei Klammern eingeschlossen sein muss. -
SKIP 0
ist erlaubt, jedoch vollkommen sinnlos. -
FIRST 0
ist ebenfalls erlaubt und gibt eine leere Ergebnismenge zurück. -
Negative Werte für
SKIP
und/oderFIRST
resultieren in einem Fehler. -
Wenn eine
SKIP
hinter 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 als der Wert des m-Arguments fürFIRST
ist, wird die kleinere Anzahl von Zeilen zurückgegeben. Dies sind gültige Ergebnisse, keine Fehler.
Wenn Sie
löscht alle Datensätze aus der Tabelle.
Die Unterabfrage ruft jedesmal 10 Zeilen ab, löscht sie und die Operation wird wiederholt, bis die Tabelle leer ist.
Beachten Sie dies!
Oder, besser, verwenden Sie die Klausel |
Beispiele für FIRST
/SKIP
Die folgende Abfrage gibt die ersten 10 Namen aus der People
-Tabelle 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 liefert einen Wert für eine Ausgabespalte.
Alternativ kann *
(“select star”) verwendet werden, um für alle Spalten in einer Beziehung zu stehen (d.H. Für eine Tabelle, eine Ansicht oder eine auswählbare gespeicherte Prozedur).
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 Beziehung (Sicht, gespeicherte Prozedur, abgeleitete Tabelle); oder ein Alias dafür |
collation |
Nur für zeichenartige Spalten: Ein Collations-Name der für den Zeichensatz der Daten existiert und gültig ist |
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 |
Skalarer oder Aggregatfunktionsaufrufausdruck |
single-value-subselect |
Eine Unterabfrage, die einen Skalarwert zurückgibt (Singleton) |
CASE-construct |
|
other-single-value-expr |
Jeder andere Ausdruck, der einen einzelnen Wert eines Firebird-Datentyps zurückgibt;
oder |
Beschreibung
Es ist immer gültig, einen Spaltennamen zu qualifizieren (oder “*
”).
Dies geschieht mit dem Namen oder Alias der Tabelle, Ansicht oder abfragbaren gespeicherten Prozedur, gefolgt von einem Punkt, z.B. relationname.columnname
, relationname.*
, alias.columnname
, alias.*
.
Qualifizierend ist required, wenn der Spaltenname in mehr als einer Relation auftritt, die an einem Join beteiligt ist.
Qualifizierendes “*
” ist immer obligatorisch, wenn es nicht das einzige Element in der Spaltenliste ist.
Aliase verschleiern den ursprünglichen Beziehungsnamen: Sobald eine Tabelle, eine Sicht oder eine Prozedur mit einem Alias versehen wurde, kann nur der Alias als Qualifikationsmerkmal für die gesamte Abfrage verwendet werden. Der Beziehungsname selbst ist nicht mehr verfügbar. |
Der Spaltenliste kann optional eines der Schlüsselwörter DISTINCT
or ALL
vorangestellt werden:
-
DISTINCT
filtert alle doppelten Zeilen aus. Das heißt, wenn zwei oder mehr Zeilen die gleichen Werte in jeder entsprechenden Spalte haben, ist nur einer von ihnen in der Ergebnismenge enthalten -
ALL
ist der Standard: es gibt alle Zeilen zurück, einschließlich Duplikate.ALL
wird selten verwendet; Es wird für die Einhaltung des SQL-Standards unterstützt.
Eine Klausel COLLATE
ändert das Erscheinungsbild der Spalte als solche nicht.
Wenn die angegebene Sortierung jedoch die Groß- / Kleinschreibung der Spalte ändert, kann dies folgende Auswirkungen haben:
-
Die Reihenfolge, wenn eine Klausel
ORDER BY
ebenfalls vorhanden ist und diese Spalte umfasst -
Gruppierung, wenn die Spalte Teil einer Klausel
GROUP BY
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 Unterabfragen
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 führt dasselbe wie das vorherige mit Joins statt Unterabfragen durch:
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 die korrekte Anrede zu ermitteln, z.B. für das 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 einer auswählbaren gespeicherten Prozedur:
select * from interesting_transactions(2010, 3, 'S')
order by amount
Auswahl aus Spalten einer abgeleiteten Tabelle.
Eine abgeleitete Tabelle ist eine eingeklammerte SELECT
-Anweisung, deren Ergebnismenge in einer einschließenden Abfrage so verwendet wird, als wäre sie eine reguläre Tabelle oder Sicht.
Die abgeleitete Tabelle ist hier fett dargestellt:
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
Die Zeit durch eine Kontextvariable abfragen (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 nur genau eine Zeile enthält.
Obwohl es für diesen Zweck nicht erstellt wurde, ist es unter Firebird-Programmierern Standard geworden, diese Tabelle abzufragen, wenn Sie “aus nichts” abfragen möchten, d.h. wenn Sie Daten benötigen, die nicht an eine Tabelle oder Ansicht gebunden sind, diese aber über Ausdrücke in den Ausgabespalten abgeleitet werden können.
Ein anderes Beispiel ist:
select power(12, 2) as twelve_squared, power(12, 3) as twelve_cubed
from rdb$database
Zum Schluss ein Beispiel, in dem Sie aussagekräftige Informationen aus RDB$DATABASE
selbst ermitteln:
select rdb$character_set_name from rdb$database
Wie Sie vielleicht schon vermutet haben, erhalten Sie den Standardzeichensatz der Datenbank.
Eingebaute Funktionen, Aggregatfunktionen, Kontextvariablen, CASE
, Unterabfragen
6.1.3. Die FROM
-Klausel
Die Klausel FROM
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 gespeicherten Prozedur |
args |
Auswählbare Argumente für gespeicherte Prozeduren |
derived table |
Abgeleiteter Tabellenabfrageausdruck |
cte-def |
Definition des gemeinsamen Tabellenausdrucks (Common Table Expression, CTE), einschließlich eines “ad hoc”-Namens |
select-statement |
Beliebige SELECT-Anweisung |
column-aliases |
Alias für eine Spalte in einer Relation, CTE oder abgeleitete Tabelle |
name |
Der “ad hoc”-Name für eine CTE |
alias |
Der Alias einer Datenquelle (Tabelle, View, Prozedur, CTE, abgeleitete Tabelle) |
Abfragen einer Tabelle oder Ansicht mit FROM
Bei der Auswahl aus einer einzelnen Tabelle oder Sicht muss die FROM
-Klausel nichts mehr als den Namen enthalten.
Ein Alias kann nützlich oder sogar notwendig sein, wenn es Unterabfragen gibt, die auf die Haupt-Select-Anweisung verweisen (wie sie es sooft tun — Unterabfragen wie diese werden auch korrelierte Unterabfragen genannt).
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 Spaltenaliasnamen!
Wenn Sie einen Alias für eine Tabelle oder eine Sicht angeben, müssen Sie diesen Alias anstelle des Tabellennamens immer verwenden, wenn Sie die Spalten der Relation abfragen (und wo auch immer Sie auf Spalten verweisen, z.B. Richtige Verwendung:
Falsche Verwendung:
|
Abfragen einer gespeicherten Prozedur mit FROM
Eine auswählbare gespeicherte Prozedur ist eine Prozedur, die:
-
enthält mindestens einen Ausgabeparameter und
-
das Schlüsselwort
SUSPEND
verwendet, damit der Aufrufer die Ausgabezeilen nacheinander abrufen kann, genau so wie bei der Auswahl aus einer Tabelle oder Ansicht.
Die Ausgabeparameter einer auswählbaren gespeicherten Prozedur entsprechen den Spalten einer regulären Tabelle.
Die Abfrage aus einer gespeicherten Prozedur ohne Eingabeparameter entspricht der Abfrage aus einer Tabelle oder Ansicht:
select * from suspicious_transactions
where assignee = 'John'
Alle erforderlichen Eingabeparameter müssen nach dem in Klammern angegebenen Prozedurnamen 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 (d.h. Parameter, für die Standardwerte definiert wurden) können weggelassen oder bereitgestellt werden. Wenn Sie diese jedoch nur teilweise angeben, müssen die Parameter, die Sie weglassen, alle am Ende stehen.
Angenommen, die Prozedur visible_stars
aus dem vorherigen Beispiel hat zwei optionale Parameter: min_magn
(numeric(3,1)
) und spectral_class
(varchar(12)
).
Die folgenden Abfragen sind alle 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');
Diese jedoch nicht, da es ein “Loch” in der Parameterliste gibt:
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 die Parameterliste weggelassen werden:
select
number,
(select name from contestants c where c.number = get_winners.number)
from get_winners('#34517', 'AMS')
Abfragen aus einer abgeleiteten Tabelle mittels FROM
Eine abgeleitete Tabelle ist eine gültige SELECT
-Anweisung, die in Klammern eingeschlossen ist, optional gefolgt von einem Tabellenalias und / oder Spaltenaliasnamen.
Die Ergebnismenge der Anweisung fungiert als virtuelle Tabelle, die die umschließende Anweisung abfragen kann.
(<select-query>) [[AS] derived-table-alias] [(<derived-column-aliases>)] <derived-column-aliases> := column-alias [, column-alias ...]
Die von diesem “SELECT FROM(SELECT FROM …)
”-Stil der Anweisung zurückgegebene Datenmenge ist eine virtuelle Tabelle, die innerhalb der umschließenden Anweisung abgefragt werden kann, als wäre sie eine normale Tabelle oder Ansicht.
Beispiel mit einer abgeleiteten Tabelle
Die abgeleitete Tabelle in der folgenden Abfrage gibt die Liste der Tabellennamen in der Datenbank und die Anzahl der Spalten in jeder Datenbank 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 demonstriert, 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
Weiter gilt:
|
Ein nützlicheres Beispiel
Angenommen, wir haben eine Tabelle COEFFS
, die die Koeffizienten einer Anzahl von quadratischen Gleichungen enthält, die wir lösen müssen.
Diese wurde folgendermaßen 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 für 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 für die 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 dabei 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 keine schlechte Idee ist), können wir die Abfrage folgendermaßen ä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 Liste mit Spaltenaliasen für die abgeleitete Tabelle verwendet, nutzt die zweite Abfrage intern hinzugefügte Alias, wo diese benötigt werden. Beide Methoden funktionieren, solange jede Spalte einen Namen hat.
Abfragen einer CTE mittels FROM
Ein allgemeiner Tabellenausdruck (Common Table Expression) oder CTE ist eine komplexere Variante der abgeleiteten Tabelle, aber auch leistungsfähiger.
Eine Präambel, beginnend mit dem Schlüsselwort WITH
, definiert einen oder mehrere benannte CTE mit jeweils einer optionalen Spalten-Alias-Liste.
Die Hauptabfrage, die der Präambel folgt, kann dann auf diese CTE wie normale Tabellen oder Ansichten zugreifen.
Sobald die Hauptabfrage ausgeführt wurde, werden die CTEs nicht mehr betrachtet.
Für eine vollständige Beschreibung der CTEs, beachten Sie bitte den Abschnitt Common Table Expressions (WITH … AS … SELECT).
Das folgende ist eine andere Variante 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 von der Tatsache, dass die Berechnungen, die zuerst gemacht werden müssen, jetzt am Anfang stehen, ist dies keine große Verbesserung gegenüber der abgeleiteten Tabellenversion. Aber wir können 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, könnte aber effizienter ausgeführt werden (abhängig davon, was mehr Zeit benötigt: die Ausführung der Funktion SQRT
oder die Übergabe der Werte von b
, D
und denom
durch eine weitere CTE).
Übrigens hätten wir das Gleiche mit abgeleiteten Tabellen tun können, aber das würde Verschachtelung bedeuten.
6.1.4. Joins
Joins kombinieren Daten aus zwei Quellen zu einem einzelnen Satz.
Dies wird durch einen Zeile-für-Zeilen-Vergleich durchgeführt und beinhaltet üblicherweise eine Join-Bedingung, um festzulegen welche Zeilen zusammengeführt werden sollen und im Ergebnisdatensatz erscheinen sollen.
Es gibt unterschiedliche Arten (INNER
, OUTER
) und Klassen (qualifiziert, natürlich, etc.), jede mit eigener Syntax und Regeln.
Da Joins verkettet werden können, können die an einem Join beteiligten Datensätze 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 gespeicherten Prozedur |
args |
Wählbare gespeicherte Prozedur-Eingangsparameter |
derived-table |
Referenz, namentlich, auf eine abgeleitete Tabelle |
common-table-expression |
Verweis auf einen gemeinsamen Tabellenausdruck (CTE) |
alias |
Ein Alias für eine Datenquelle (Tabelle, View, 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 Mengen (normalerweise als die linke Menge und die rechte Menge bezeichnet). Standardmäßig werden nur Zeilen in die Ergebnismenge aufgenommen, die die Join-Bedingung erfüllen (d.h. wenn bei der Join-Bedingung mindestens eine Zeile in der anderen Gruppe übereinstimmt). Dieser Standardtyp von Join 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 Zeile von A
wurde mit der zweiten Zeile von B
verbunden, weil sie zusammen die Bedingung “A.id = B.code
” erfüllten.
Die anderen Zeilen aus den Quellentabellen haben keine Übereinstimmung in der entgegengesetzten Menge und sind daher nicht in der Verknüpfung enthalten.
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 Standardeinstellung ist, wird dies selten durchgeführt.
Es ist durchaus möglich, dass eine Zeile im linken Satz mit mehreren Zeilen vom 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 brauchen) alle die Zeilen einer oder beider Quellen in der verbundenen Menge erscheinen, unabhängig davon, ob sie mit einem Datensatz in der anderen Quelle übereinstimmen.
An dieser Stelle kommen Outer Joins ins Spiel.
Ein Outer Join LEFT
enthält alle Datensätze aus dem linken Satz, aber nur übereinstimmende Datensätze aus dem richtigen Satz.
In einem RIGHT
Outer Join ist es umgekehrt.
FULL
Outer Joins umfassen alle Datensätze aus beiden Sets.
In allen äußeren Joins sind die “Löcher” (die Stellen, an denen ein eingeschlossener Quelldatensatz keine Übereinstimmung in der anderen Menge hat) mit NULL
gefüllt.
Um einen Outer Join zu erstellen, müssen Sie LEFT
, RIGHT
oder FULL
angeben, optional vom Schlüsselwort OUTER
gefolgt.
Im Folgenden sind die Ergebnisse der verschiedenen äußeren Joins aufgeführt, 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 Klausel ON
mit einer expliziten Bedingung, bei der es sich um einen beliebigen gültigen booleschen Ausdruck handeln kann, der jedoch normalerweise einen Vergleich zwischen den beiden beteiligten Quellen beinhaltet.
Häufig ist die Bedingung ein Gleichheitstest (oder eine Anzahl von AND
-verknüpften Gleichheitstests) unter Verwendung des Operators “=
”.
Joins wie diese heißen [term]_Equi-Joins _.
(Die Beispiele im Abschnitt über innere und äußere Verknüpfung waren Equi-Joins.)
Beispiele für Joins mit expliziter Bedingung:
/* Wählen Sie alle Detroit-Kunden aus, die 2013 einen
Kauf getätigt haben, 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;
/* Dasselbe 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ähle für jeden Mann die Frauen aus, die größer sind als er.
Männer, für die keine solche Frau existiert, sind nicht enthalten. */
select m.fullname as man, f.fullname as woman
from males m
join females f on f.height > m.height;
/* Wähle alle Schüler mit ihrer Klasse und ihrem Mentor aus.
Schüler ohne Mentor sind ebenfalls enthalten. Schüler ohne
Klasse sind nicht enthalten. */
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 für benannte Spalten
Equi-Joins vergleichen häufig Spalten, die in beiden Tabellen denselben Namen haben. Wenn dies der Fall ist, können wir auch den zweiten Typ von qualifiziertem Join verwenden: die Joins für benannte Spalten.
Joins für benannte Spalten werden in Dialekt 1 nicht unterstützt. |
Joins für benannte Spalten besitzen eine USING
-Klausel, welche nur die Spaltennamen enthält.
Anstelle dieser Variante:
select * from flotsam f
join jetsam j
on f.sea = j.sea
and f.ship = j.ship;
können wir auch diese schreiben:
select * from flotsam
join jetsam using (sea, ship)
welche deutlich kürzer ist.
Der Ergebnissatz ist etwas anders — zumindest bei der Verwendung von “SELECT *
”:
-
Der Join mit expliziter Bedingung — mit der
ON
-Klausel — wird jede der SpaltenSEA
undSHIP
zweimal enthalten: einmal für TabelleFLOTSAM
und einmal für TabelleJETSAM
. Offensichtlich werden sie die gleichen Werte haben. -
Der Join für benannte 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 das gleiche Ergebnis wie beim Join der expliziten Bedingung.
Für einen Join mit benannten Spalte vom Typ OUTER
gibt es eine zusätzliche Wendung, wenn “SELECT
*” oder ein nicht qualifizierter Spaltenname aus der USING
-Liste verwendet wird:
Wenn eine Zeile aus einer Quellgruppe keine Übereinstimmung in der anderen enthält, muss sie dennoch aufgrund der LEFT
-, RIGHT
- oder FULL
-Direktive enthalten sein.
Die zusammengeführte Spalte im zusammengeführten Satz erhält den Wert nicht-NULL
.
Das ist soweit gut, aber jetzt können Sie nicht sagen, ob dieser Wert aus der linken, rechten oder beiden Mengen stammt.
Dies kann besonders trügerisch sein, wenn der Wert von der rechten Seite stammt, weil “*
” immer kombinierte Spalten im linken Teil zeigt — selbst im Falle eines RIGHT
Join.
Ob dies ein Problem ist oder nicht, hängt von der Situation ab.
Wenn ja, benutzen Sie die “a.*, b.*
”-Ansatz wie oben gezeigt, mit a
und b
als Namen oder Alias der beiden Quellen.
Oder noch besser, vermeiden Sie “*
” insgesamt in Ihren seriösen Abfragen und qualifizieren Sie alle Spaltennamen in verbundenen Mengen.
Dies hat den zusätzlichen Vorteil, dass Sie gezwungen sind, darüber nachzudenken, welche Daten Sie abrufen möchten und woher.
Es liegt in Ihrer Verantwortung sicherzustellen, dass die Spaltennamen in der USING
-Liste kompatible 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 sie die Werte vergleicht.
Dies ist auch der Datentyp der zusammengeführten Spalte, die in der Ergebnismenge angezeigt wird, wenn “SELECT *
” oder der nicht qualifizierte Spaltenname verwendet wird.
Qualifizierte Spalten behalten ihren ursprünglichen Datentyp immer bei.
Natürliche Joins
Greift man die Idee der benannten Spalten auf und geht noch einen Schritt weiter, führt ein natürlicher Join einen automatischen Equi-Join für alle Spalten durch, die in der linken und rechten Tabelle den gleichen Namen haben. Die Datentypen dieser Spalten müssen kompatibel sein.
Natürliche Joins werden in Dialekt 1-Datenbanken nicht unterstützt. |
<natural-join> ::= NATURAL [<join-type>] JOIN <source> <join-type> ::= INNER | {LEFT | RIGHT | FULL} [OUTER]
Gegeben sind 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 auf TA
und TB
würde die Spalten a
und ins_date
einbeziehen, und die folgenden zwei Anweisungen würden die gleiche Wirkung haben:
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, die Sie jedoch durch Angabe von LEFT
, RIGHT
oder FULL
vor dem Schlüsselwort JOIN
in äußere Joins umwandeln können.
Vorsicht: Wenn in den beiden Quellbeziehungen keine Spalten mit demselben Namen vorhanden sind, wird ein CROSS JOIN
ausgeführt.
Wir kommen in einer Minute zu dieser Art von Join.
Eine Anmerkung zur Gleichheit
Diese Notiz über Gleichheits- und Ungleichheitsoperatoren gilt überall in der Firebird SQL-Sprache, nicht nur unter |
Der Operator “=
”, welcher explizit für diverse bedingte Joins und implizit in Joins mit benannten Spalten und natürlichen Joins verwendet wird, vergleicht nur Werte mit Werten.
Nach dem SQL-Standard gilt, dass NULL
kein Wert ist und somit zwei NULL
en wedet identisch noch unidentisch zueinander sind.
Wenn Sie NULL
benötigen, um in einem Join übereinzustimmen, verwenden Sie den Operator IS NOT DISTINCT FROM
.
Dieser Operator gibt "true" zurück, wenn die Operanden denselben Wert haben oder wenn sie beide NULL
sind.
select *
from A join B
on A.id is not distinct from B.code;
In den — extrem seltenen — Fällen, in denen Sie im Join auf die in-Gleichheit prüfen möchsten, verwenden Sie IS DISTINCT FROM
, nicht “<>
”, falls Sie NULL
von anderen Werten unterscheiden müssen und zwei NULL
en als gleich betrachtet werden sollen:
select *
from A join B
on A.id is distinct from B.code;
Cross Joins
Ein Cross Join erzeugt das vollständige Set-Produkt der beiden Datenquellen. Dies bedeutet, dass jede Zeile in der linken Quelle mit jeder Zeile in der rechten Quelle übereinstimmt.
<cross-join> ::= {CROSS JOIN | ,} <source>
Bitte beachten Sie, dass die Kommasyntax veraltet ist! Es wird nur unterstützt, um Legacy-Code zu erhalten, und wird möglicherweise in einer zukünftigen Version verschwinden.
Das Zusammenführen von zwei Sätzen ist gleichbedeutend damit, dass sie sich einer Tautologie anschließen (eine Bedingung, die immer wahr ist). Die folgenden beiden Aussagen haben den gleichen Effekt:
select * from TA
cross join TB;
select * from TA
join TB on 1 = 1;
Cross Joins sind innere Joins, da sie nur übereinstimmende Datensätze enthalten — dies ergibt sich daraus, dass jeder-Eintrag übereinstimmt! Ein äußerer Cross Join würde, falls vorhanden, dem Ergebnis nichts hinzufügen, weil die äußeren Joins keine übereinstimmenden Datensätze sind und diese nicht in Cross Joins existieren.
Cross Joins sind selten nützlich, 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, gibt diese Abfrage alle Kombinationen zurück:
select m.name, s.size, c.name
from materials m
cross join sizes s
cross join colors c;
Mehrdeutige Feldnamen in Joins
Firebird weist unqualifizierte Feldnamen in einer Abfrage zurück, wenn diese Feldnamen in mehr als einem Datensatz vorhanden sind, der an einem Join beteiligt ist.
Dies gilt sogar für innere Equi-Joins, bei denen der Feldname in der ON
-Klausel so aussieht:
select a, b, c
from TA
join TB on TA.a = TB.a;
Es gibt eine Ausnahme zu dieser Regel: Bei Joins mit benannten Spalten und natürlichen Joins kann der nicht qualifizierte Feldname einer Spalte, die am Matching-Prozess teilnimmt, legal verwendet werden und verweist auf die zusammengeführte Spalte mit demselben Namen.
Für Joins mit benannten Spalten sind dies die in der USING
-Klausel aufgelisteten Spalten.
Bei natürlichen Joins sind dies die Spalten, die in beiden Relationen denselben Namen haben.
Aber bitte beachten Sie noch einmal, dass insbesondere in Outer Joins der reine Spaltenname
nicht immer dasselbe ist wie links.Spaltenname
oder rechts.`Spaltenname
.
Die Typen können sich unterscheiden, und eine der qualifizierten Spalten kann NULL
sein, während die andere nicht.
In diesem Fall kann der Wert in der zusammengeführten, nicht qualifizierten Spalte die Tatsache maskieren, dass einer der Quellwerte nicht vorhanden ist.
Joins mit gespeicherten Prozeduren
Wenn eine Verknüpfung mit einer gespeicherten Prozedur durchgeführt wird, die nicht über Eingabeparameter mit anderen Datenströmen korreliert, gibt es keine Kuriositäten. Wenn es Korrelationen gibt, zeigt sich eine unangenehme Eigenart. Das Problem ist, dass der Optimierer sich selbst jede Möglichkeit nimmt, die Beziehungen der Eingabeparameter der Prozedur zu den Feldern in den anderen Datenströmen zu bestimmen:
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
(kein aktueller Datensatz für die Abrufoperation) wird ausgelöst, wodurch die Ausführung unterbrochen wird.
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 der Prozedur gelesen wird und alles ordnungsgemäß funktioniert.
Diese Eigenart wurde als Fehler im Optimierer 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, an denen der Aufrufer interessiert ist.
Die Bedingung nach dem Schlüsselwort WHERE
kann so einfach sein wie “Anzahl = 3
” oder ein mehrschichtiger, geschachtelter Ausdruck, der Unterabfragen, Prädikate, Funktionsaufrufe, mathematische und logische Operatoren, Kontextvariablen und mehr enthält.
Die Bedingung in der WHERE
-Klausel wird häufig als die Suchbedingung, der Suchausdruck oder einfach die Suche bezeichnet.
In DSQL und ESQL kann der Suchausdruck Parameter enthalten.
Dies ist nützlich, wenn eine Abfrage mehrmals mit unterschiedlichen Eingabewerten wiederholt werden muss.
In der SQL-Zeichenfolge, die an den Server übergeben wird, werden Fragezeichen als Platzhalter für die Parameter verwendet.
Sie heißen Positionsparameter, weil sie nur durch ihre Position in der Zeichenfolge voneinander getrennt werden können.
Konnektivitätsbibliotheken unterstützen oft benannte Parameter der Form :id
, :amount
, :a
usw.
Diese sind benutzerfreundlicher;
Die Bibliothek sorgt dafür, dass die benannten Parameter in Positionsparameter übersetzt werden, 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> [...] <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, sind in der Ergebnismenge enthalten.
Seien Sie vorsichtig mit möglichen NULL
-Ergebnissen: Wenn Sie einen NULL
-Ausdruck mit NOT
negieren, ist das Ergebnis immer NULL
und die Zeile wird nicht berücksichtigt.
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 auf NULL
ausgewertet wird.
Angenommen, Sie haben eine Tabelle mit den Namen einiger Kinder und der Anzahl der Murmeln, 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 |
Zuerst beachten Sie bitte den Unterschied zwischen NULL
und 0: Fritz ist bekannt dafür überhaupt keine Murmeln zu haben, Chris' und Hadassahs Murmelanzahlen unbekannt.
Nun, wenn Sie diese SQL-Anweisung ausgeben:
select list(child) from marbletable where marbles > 10;
Sie werden die Namen Anita, Bob E., Eve und Gerry bekommen. 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. Chris und Hadassah sind nicht enthalten, weil nicht bekannt ist, dass sie zehn oder weniger Murmeln besitzen. Sollten Sie diese letzte Abfrage ändern in:
select list(child) from marbletable where marbles <= 10;
wird das Ergebnis immer noch dasselbe sein, weil der Ausdruck NULL <=10
nun UNKNOWN
ergibt.
Das ist nicht dasselbe wie TRUE
, also sind Chris und Hadassah nicht aufgelistet.
Wenn Sie sie mit den “armen”-Kindern anzeigen möchten, ä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, weil “marbles is null
” gibt in diesem Fall offensichtlich TRUE
zurück.
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 selbst wenn es überhaupt möglich ist.
Beachten Sie, dass Abfragen wie diese nicht sofort ausgeführt werden können: Sie müssen zuerst vorbereitet (prepared) sein.
Nachdem eine parametrisierte Abfrage vorbereitet wurde, kann der Benutzer (oder der Aufrufcode) Werte für die Parameter bereitstellen und sie mehrmals ausführen lassen, wobei vor jedem Aufruf neue Werte eingegeben werden.
Wie die Werte eingegeben werden und die Ausführung gestartet wird, ist Sache der Anwendung.
In einer GUI-Umgebung gibt der Benutzer die Parameterwerte in der Regel in ein oder mehrere Textfelder ein und klickt dann auf die Schaltfläche “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 sie zuerst in das andere Format konvertieren und benannte Parameter den Positionsparametern zuordnen.
6.1.6. Die GROUP BY
-Klausel
GROUP BY
führt Ausgangszeilen mit derselben Kombination von Werten in der Elementliste in eine einzelne Zeile zusammen.
Aggregatfunktionen in der Auswahlliste werden für jede Gruppe einzeln und nicht für das gesamte Dataset angewendet.
Wenn die Auswahlliste nur Aggregatspalten oder allgemeiner Spalten enthält, deren Werte nicht von einzelnen Zeilen in der zugrunde liegenden Menge abhängen, ist GROUP BY
optional.
Wenn sie weggelassen wird, besteht die endgültige Ergebnismenge aus einer einzelnen Zeile (vorausgesetzt, dass mindestens eine aggregierte Spalte vorhanden ist).
Wenn die Auswahlliste sowohl Aggregatspalten als auch Spalten enthält, deren Werte je Zeile variieren können, wird die Klausel GROUP BY
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 Literalkopie aus der |
column-alias |
Der Alias aus der |
column-position |
Die Positionsnummer in der |
Eine allgemeine Faustregel besagt, dass jedes nicht aggregierte Element in der SELECT
-Liste ebenfalls in der GROUP BY
-Liste enthalten sein muss.
Sie können dies auf drei Arten tun:
-
Indem der Gegenstand wörtlich aus der Auswahlliste kopiert wird, 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 sich aus Ausdrücken oder Parametersubstitutionen ergeben, sind einfach unveränderlich und werden als solche in der Gruppierung verwendet. Sie werden jedoch keinen Effekt haben, 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 bedeutet, dass die Gruppierung nach der Spaltenposition, anstatt den Unterabfrageausdruck in der Gruppierungsklausel zu duplizieren, Tastenanschläge und Bytes speichert, dies ist jedoch keine Möglichkeit, Verarbeitungszyklen zu speichern! |
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-Aggregat-Ausdrücke, die auf solchen Spalten basieren. Das Hinzufügen solcher Spalten kann die Gruppen weiter unterteilen. Da diese Spalten jedoch nicht in der Auswahlliste enthalten sind, können Sie nicht feststellen, welche aggregierte Zeile mit welchem Wert in der Spalte übereinstimmt. Wenn Sie also an diesen Informationen interessiert sind, fügen Sie auch die Spalte oder den Ausdruck in die Auswahlliste — ein, die Sie wieder zur Regel führt: “Jede Nicht-Aggregat-Spalte in der Auswahlliste muss ebenfalls in der Gruppierungsliste sein”.
-
Ausdrücke, die nicht von den Daten in dem zugrunde liegenden Satz abhängen, z. Konstanten, Kontextvariablen, einwertige nicht-korrelierte Subselects 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 erscheinen, ohne in die Gruppierungsliste kopiert zu werden.
Beispiele
Wenn die Auswahlliste nur Aggregatspalten enthält, ist GROUP BY
nicht obligatorisch:
select count(*), avg(age) from students
where sex = 'M';
Dies wird eine einzelne Zeile zurückgeben, die die Anzahl der männlichen Studenten und deren Durchschnittsalter auflistet.
Das Hinzufügen von Ausdrücken, die nicht von Werten in einzelnen Zeilen der Tabelle STUDENTS
abhängen, ändert das nicht:
select count(*), avg(age), current_date from students
where sex = 'M';
Die Zeile wird jetzt eine zusätzliche Spalte haben, die das aktuelle Datum anzeigt, aber ansonsten hat sich nichts Grundlegendes geändert.
Eine Klausel GROUP BY
ist weiterhin nicht erforderlich.
In beiden obigen Beispielen ist dies jedoch erlaubt. Dies ist absolut gültig:
select count(*), avg(age) from students
where sex = 'M'
group by class;
und gibt eine Reihe für jede Klasse zurück, in der sich Jungen befinden, die die Anzahl der Jungen und ihr Durchschnittsalter in dieser bestimmten Klasse auflistet.
(Wenn Sie auch das Feld current_date
beibehalten, wird dieser Wert in jeder Zeile wiederholt, was nicht besonders aufregend ist.)
Die obige Abfrage hat jedoch einen großen Nachteil: Sie gibt Ihnen Informationen über die verschiedenen Klassen, aber Sie erfahren 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 durch das Hinzufügen der Spalte CLASS
auch die Klausel GROUP BY
obligatorisch wird.
Wir können diese Klausel nicht mehr löschen, es sei denn, wir entfernen auch CLASS
aus der Spaltenliste.
Die Ausgabe unserer letzten Abfrage könnte 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 nicht sehr informativ. In einem einfachen Fall wie diesem, könnten Sie damit durchkommen, aber im Allgemeinen sollten Sie aggregierten Spalten einen aussagekräftigen Namen geben, indem wir je einen Alias nutzen:
select class,
count(*) as num_boys,
avg(age) as boys_avg_age
from students
where sex = 'M'
group by class;
Wie Sie aus der formalen Syntax der Spaltenliste entnehmen können, ist das Schlüsselwort AS
optional.
Wenn Sie weitere nicht aggregierte (oder besser: zeilenabhängige) Spalten hinzufügen, müssen Sie sie auch der Klausel GROUP BY
hinzufügen.
Zum Beispiel möchten Sie vielleicht die oben genannten Informationen auch für Mädchen sehen;
und Sie möchten vielleicht 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 Variablen class, sex und boarding type. Die zusammengefassten Ergebnisse — Anzahl und durchschnittliches Alter — sind für jede dieser eher spezifischen Gruppen einzeln angegeben. In einer Abfrage wie dieser sehen Sie keine Gesamtzahl für Jungen als Ganzes oder Tagesschüler als Ganzes. Das ist der Nachteil: Je mehr Nicht-Aggregat-Spalten Sie hinzufügen, desto mehr können Sie sehr spezifische Gruppen bestimmen, aber desto mehr verlieren Sie auch das allgemeine Bild aus den Augen. Natürlich können Sie die “gröberen” Aggregate auch über separate Abfragen erhalten.
HAVING
Genau wie eine WHERE
-Klausel die Zeilen in einer Datenmenge auf solche begrenzt, die die Suchbedingung erfüllen, so beschränkt die Unterklasse HAVING
die aggregierten Zeilen in einer gruppierten Gruppe.
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 die am häufigsten verwendete Alternative.
-
Jeder aggregierte Ausdruck, der nicht in der Auswahlliste enthalten ist, aber im Kontext der Abfrage zulässig ist. Dies ist manchmal auch nützlich.
-
Eine beliebige Spalte in der Liste
GROUP BY
. Obwohl dies legal ist, ist es effizienter, diese nicht aggregierten Daten zu einem früheren Zeitpunkt zu filtern: in der KlauselWHERE
. -
Ein beliebiger Ausdruck, dessen Wert nicht vom Inhalt des Datasets abhängt (wie eine Konstante oder eine Kontextvariable). Das ist zwar stichhaltig, aber völlig sinnlos, weil es entweder die gesamte Menge unterdrückt oder sie 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 Ganzzahl in der
HAVING
-Klausel ist nur eine Ganzzahl. -
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 Gruppen von Schülern 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 einem Mindestalter 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, diese normalerweise einschließen würden mittels min(age)
und max(age)
–- oder dem Ausdruck “max(age) - min(age)
” -– auch in der Select-Liste!
Um nur die 3. Klassen einzuschließen:
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 einzureichen, wodurch der Plan überschrieben wird, den der Optimierer automatisch erstellt hätte.
PLAN <plan-expr> <plan-expr> ::= (<plan-item> [, <plan-item> ...]) | <sorted-item> | <joined-item> | <merged-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> ...]) <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 |
Ansichtname |
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 isql
-Dienstprogramm geschieht dies mit dem Befehl SET PLAN ON
.
Wenn Sie Abfragepläne analysieren und keine Abfragen ausführen, zeigt SET PLANONLY ON
den Plan an, ohne die Abfrage auszuführen.
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, die nicht leistungsfähig sind, lohnt es sich möglicherweise, 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 Beziehungsnamen gefolgt von einer Abrufmethode.
Z.B. für eine unsortierte Ein-Tabellen-Auswahl ohne eine WHERE
-Klausel:
select * from students
plan (students natural);
Wenn eine WHERE
- oder eine HAVING
-Klausel vorhanden ist, können Sie den Index angeben, der zum Auffinden von Übereinstimmungen verwendet werden soll:
select * from students
where class = '3C'
plan (students index (ix_stud_class));
Die Anweisung INDEX
wird auch für Join-Bedingungen verwendet (etwas später diskutiert). Es kann eine Liste von Indizes enthalten, die durch Kommata getrennt sind.
ORDER
gibt den Index zum Sortieren des Satzes an, wenn eine Klausel ORDER BY
oder GROUP BY
vorhanden ist:
select * from students
plan (students order pk_students)
order by id;
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;
Es ist völlig 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;
Wenn Sie einen Sortiersatz verwenden möchten, wenn kein verwendbarer Index verfügbar ist (oder wenn Sie die Verwendung des Index unterdrücken möchten), lassen Sie ORDER
aus und stellen Sie dem Planausdruck SORT
voran:
select * from students
plan sort (students natural)
order by name;
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;
Beachten Sie, dass sich SORT
im Gegensatz zu ORDER
außerhalb der Klammern befindet.
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.
Zum Beispiel, wenn Sie eine Ansicht FRESHMEN
haben, die nur die Erstsemester auswählt:
select * from freshmen
plan (freshmen students natural);
Oder zum Beispiel:
select * from freshmen
where id > 10
plan sort (freshmen students index (pk_students))
order by name desc;
Wenn eine Tabelle oder Sicht mit einem Alias versehen wurde, muss der Alias und nicht der ursprüngliche Name in der Klausel |
Zusammengesetzte Pläne
Wenn ein Join erstellt wird, können Sie den Index angeben, der für den Abgleich verwendet werden soll.
Sie müssen auch die Anweisung JOIN
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));
Derselbe Join, 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;
Und auf einer nicht indizierten 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;
Mit einer Suche hinzugefügt:
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;
Als linker 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;
Wenn für die Join-Kriterien kein Index verfügbar ist (oder wenn Sie ihn nicht verwenden möchten), muss der Plan zuerst beide Streams in ihren Join-Spalten sortieren und dann zusammenführen.
Dies wird mit der Anweisung SORT
(die wir bereits erreicht haben) und MERGE
anstelle von JOIN
erreicht:
select * from students s
join classes c on c.cookie = s.cookie
plan merge (sort (c natural), sort (s natural));
Durch das Hinzufügen einer ORDER BY
-Klausel muss das Ergebnis der Zusammenführung ebenfalls sortiert werden:
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 indexierbare 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 Datenströme kombinieren.
Außerdem kann jeder Planausdruck als Planposten in einem umfassenden Plan verwendet werden.
Dies bedeutet, dass Pläne bestimmter komplizierter Abfragen verschiedene Verschachtelungsebenen haben können.
Schließlich können Sie anstelle von MERGE
auch SORT MERGE
schreiben.
Da dies absolut keinen Unterschied macht und zu Verwechslungen mit “real” SORT
-Direktiven führen kann (diejenigen, die etwas tun machen einen Unterschied), ist es wahrscheinlich am besten zu bleiben zu einfach MERGE
.
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
Ein UNION
verkettet zwei oder mehr Datasets und erhöht so die Anzahl der Zeilen, nicht aber die Anzahl der Spalten.
Datasets, die an einer UNION
teilnehmen, müssen die gleiche Anzahl von Spalten haben, und Spalten an entsprechenden Positionen müssen vom selben Typ sein.
Abgesehen davon können sie völlig unabhängig sein.
Standardmäßig unterdrückt eine Union doppelte Zeilen.
UNION ALL
zeigt alle Zeilen einschließlich aller Duplikate an.
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]] [FOR UPDATE [OF <columns>]] [WITH LOCK] [INTO <PSQL-varlist>]
Unions ermitteln ihre Spaltennamen aus der ersten Abfrage.
Wenn Sie einen Alias für Vereinigungsspalten verwenden möchten, tun Sie dies in der Spaltenliste des obersten SELECT
.
Aliasnamen in anderen teilnehmenden Selects sind zulässig und können sogar nützlich sein, werden jedoch nicht auf Unionsebene weitergegeben.
Wenn eine Union eine ORDER BY
-Klausel hat, sind die einzigen zulässigen Sortierelemente Integerliterale, die 1-basierte Spaltenpositionen angeben, optional gefolgt von einem ASC
/DESC
und/oder einer NULLS {FIRST | LAST}
-Direktive.
Dies bedeutet auch, dass Sie eine Union nicht nach etwas sortieren können, die keine Spalte in der Union ist.
(Sie können jedoch eine abgeleitete Tabelle einfügen, 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 eingebunden werden.
Beispiele
Diese Abfrage präsentiert Informationen aus verschiedenen Musiksammlungen in einem Datensatz mithilfe von Unionen:
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 Felder in den involvierten 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;
Das Qualifizieren der “Sternchen” ist hier notwendig, da sie nicht das einzige Element in der Spaltenliste sind. Beachten Sie, dass die Aliase von “c” in der ersten und dritten Auswahl nicht miteinander in Konflikt stehen: ihre Gültigkeitsbereiche sind nicht unionsweit, sondern gelten nur für ihre jeweiligen Auswahlabfragen.
Die nächste Abfrage ruft Namen und Telefonnummern von Übersetzern und Korrektoren ab.
Übersetzer, die auch als Korrekturleser arbeiten, werden nur einmal im Ergebnis angezeigt, sofern ihre Telefonnummer in beiden Tabellen identisch ist.
Das gleiche Ergebnis kann ohne DISTINCT
erzielt werden.
Mit ALL
würden diese Personen zweimal angezeigt.
select name, phone from translators
union distinct
select name, telephone from proofreaders;
Ein 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, ist die Ergebnismenge in keiner Weise sortiert.
Es kommt häufig vor, dass Zeilen chronologisch sortiert angezeigt werden, weil sie in derselben Reihenfolge zurückgegeben werden, in der sie von INSERT
-Anweisungen zur Tabelle hinzugefügt wurden.
Um eine Sortierreihenfolge für die Mengenspezifikation 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 |
Jeder Ausdruck |
collation-name |
Collations-Name (Sortierreihenfolge für String-Typen) |
Beschreibung
Die ORDER BY
-Klausel besteht aus einer durch Komma getrennten Liste der Spalten, auf denen der Ergebnisdatensatz sortiert werden soll.
Die Sortierreihenfolge kann durch den Namen der Spalte angegeben werden — jedoch nur, wenn die Spalte zuvor in der Spaltenliste SELECT
nicht mit einem Alias versehen war.
Der Alias muss verwendet werden, wenn er dort verwendet wurde.
Die Ordnungsnummer der Spalte, des Alias, der der Spalte in der SELECT
-Liste mit Hilfe des Schlüsselworts AS
zugewiesen wurde, oder die Nummer der Spalte in der Liste SELECT
können uneingeschränkt verwendet werden.
Die drei Arten, die Spalten für die Sortierreihenfolge auszudrücken, können in der gleichen ORDER BY
-Klausel gemischt werden.
Zum Beispiel kann eine Spalte in der Liste durch ihren Namen spezifiziert werden und eine andere Spalte kann durch ihre Nummer spezifiziert werden.
Wenn Sie die Spaltenposition verwenden, um die Sortierreihenfolge für eine Abfrage des |
Sortierrichtung
Das Schlüsselwort ASCENDING
, normalerweise abgekürzt als ASC
, gibt eine Sortierrichtung vom niedrigsten zum höchsten an.
ASCENDING
ist die Standardsortierrichtung.
Das Schlüsselwort DESCENDING
, normalerweise abgekürzt als DESC
, gibt eine Sortierrichtung vom höchsten zum niedrigsten an.
Angeben der aufsteigenden Reihenfolge für eine Spalte und der absteigenden Reihenfolge für eine andere Spalte ist zulässig.
Collations-Reihenfolge
Das Schlüsselwort COLLATE
gibt die Sortierreihenfolge für eine Zeichenfolgespalte an, wenn Sie eine Sortierung benötigen, die sich von der normalen Sortierung für diese Spalte unterscheidet.
Die normale Sortierreihenfolge ist entweder die Standardreihenfolge für den Datenbankzeichensatz oder eine, die explizit in der Definition der Spalte festgelegt wurde.
NULLen positionieren
Das Schlüsselwort NULLS
gibt an, wo NULL in der betroffenen Spalte in der Sortierung stehen wird: NULLS FIRST
platziert die Zeilen mit der NULL
-Spalte oberhalb der Zeilen mit den Spaltenwerten;
NULLS LAST
platziert diese Zeilen hinter den Spaltenwerten.
NULLS FIRST
ist der Standard.
Sortieren von UNION
-Sätzen
Die einzelnen Abfragen, die zu einer UNION
beitragen, können keine ORDER BY
-Klausel verwenden.
Die einzige Option besteht darin, die gesamte Ausgabe mit einer ORDER BY
-Klausel am Ende der gesamten Abfrage zu sortieren.
Das einfachste — und in einigen Fällen die einzige — Methode zum Angeben der Sortierreihenfolge ist die Ordinalspaltenposition. Es ist jedoch auch zulässig, die Spaltennamen oder Aliase nur aus der ersten beitragenden Abfrage zu verwenden.
Die Anweisungen ASC
/DESC
und / oder NULLS
sind für diese globale Gruppe verfügbar.
Wenn eine diskrete Reihenfolge innerhalb der beitragenden Menge erforderlich ist, kann die Verwendung von abgeleiteten Tabellen oder allgemeinen Tabellenausdrücken für diese Mengen eine Lösung sein.
Beispiele
Sortierung der Ergebnismenge in aufsteigender Reihenfolge, Sortierung nach den Spalten RDB$CHARACTER_SET_ID, 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 Sortieren nach den Spaltenaliasnamen:
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;
Sortierung einer SELECT *
-Abfrage nach Positionsnummern möglich, aber hässlich und nicht empfohlen:
SELECT *
FROM RDB$COLLATIONS
ORDER BY 3, 2;
Sortierung nach der zweiten Spalte in der BOOKS
-Tabelle:
SELECT
BOOKS.*,
FILMS.DIRECTOR
FROM BOOKS, FILMS
ORDER BY 2;
Sortierung in absteigender Reihenfolge nach den Werten der Spalte PROCESS_TIME
, wobei NULL
am Anfang der Menge steht:
SELECT *
FROM MSG
ORDER BY PROCESS_TIME DESC NULLS FIRST;
Sortieren der Menge, die von einer UNION
von zwei Abfragen erhalten wurde.
Die Ergebnisse werden in absteigender Reihenfolge für die Werte in der zweiten Spalte sortiert, wobei NULL
en am Ende der Menge stehen;
und in aufsteigender Reihenfolge für die Werte der ersten Spalte mit NULL
en 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 Stücks von Zeilen aus einer geordneten Menge
DSQL, PSQL
SELECT <columns> FROM ... [WHERE ...] [ORDER BY ...] ROWS m [TO n]
Argument | Beschreibung |
---|---|
m, n |
Beliebiger Integer-Ausdrücke |
Begrenzt die Anzahl der Zeilen, die von der Anweisung SELECT
an eine angegebene Zahl oder einen angegebenen Bereich zurückgegeben werden.
Die Klauseln FIRST
und SKIP
haben die gleiche Aufgabe wie ROWS
, but neither are SQL-compliant.
Im Gegensatz zu FIRST
und SKIP
akzeptieren die Klauseln ROWS
und TO
beliebige Integerausdrücke als Argumente ohne Klammern.
Natürlich können Klammern für verschachtelte Auswertungen innerhalb des Ausdrucks noch immer benötigt werden und eine Unterabfrage muss immer in Klammern eingeschlossen sein.
|
Der Aufruf von ROWS m
gibt die ersten m Zeilen des angegebenen Satzes zurück.
Merkmale der Verwendung von`ROWS m` ohne eine TO
-Klausel:
-
Wenn m größer als die Gesamtzahl der Datensätze im Zwischendatensatz ist, wird die gesamte Menge zurückgegeben
-
Wenn m = 0, wird ein leerer Satz zurückgegeben
-
Wenn m < 0, wird der
SELECT
-Aufruf in einem Fehler enden
Der Aufruf von ROWS m TO n
gibt die Zeilen aus dem Satz zurück, beginnend mit Zeile m und endend nach Zeile n — inklusive Satz.
Merkmale der Verwendung von ROWS m
mit einer TO
-Klausel:
-
Wenn m größer ist als die Gesamtzahl der Zeilen in der Zwischengruppe und n >= m, wird eine leere Menge zurückgegeben
-
Ist m nicht größer als n und n größer als die Gesamtzahl der Zeilen in der Zwischengruppe, ist die Ergebnismenge beschränkt auf Zeilen beginnend mit m bis zum Ende des Satzes
-
Wenn m < 1 und n < 1, schlägt der
SELECT
-Anweisungsaufruf mit einem Fehler fehl -
Wenn n = m - 1 ist, wird eine leere Menge zurückgegeben
-
Wenn n < m - 1, schlägt der Aufruf der Anweisung
SELECT
mit einem Fehler fehl
Verwenden einer TO
-Klausel ohne eine ROWS
-Klausel:
Während ROWS
die Syntax FIRST
und SKIP
ersetzt, gibt es eine Situation, in der die ROWS
-Syntax nicht das gleiche Verhalten bietet: Mit Angabe von SKIP n
wird der gesamte Zwischensatz ohne die ersten n-Reihen zurückgegeben.
Die ROWS … TO
-Syntax benötigt ein wenig Hilfe, um dies zu erreichen.
Bei der Syntax ROWS
benötigen Sie eine ROWS
-Klausel in Verbindung mit der TO
-Klausel.
Anschließend machen Sie das zweite Argument (n) 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 in der Zwischengruppe abzurufen, und 1 dazu addiert.
Das Mischen von ROWS
und FIRST
/SKIP
Die Syntax ROWS
kann nicht mit der Syntax FIRST
/SKIP
im selben SELECT
-Ausdruck gemischt werden.
Die Verwendung der verschiedenen Syntaxen in verschiedenen Unterabfragen in derselben Anweisung ist jedoch zulässig.
ROWS
-Syntax in UNION
-Abfragen
Wenn ROWS
in einer UNION
-Abfrage verwendet wird, wird die ROWS
-Direktive auf UNION-Satzes angewendet und muss hinter dem letzten SELECT
-Statement stehen.
Wenn es erforderlich ist, die Teilmengen zu begrenzen, die von einer oder mehreren SELECT
-Anweisungen innerhalb von UNION
zurückgegeben werden, gibt es eine Reihe von Optionen:
-
Verwenden Sie die
FIRST
/SKIP
-Syntax in diesenSELECT
-Anweisungen — bedenken Sie, dass eine ordering-Klausel (ORDER BY
) nicht lokal auf die einzelnen Abfragen angewendet werden kann, aber nur für den kombinierten Ausgang. -
Konvertieren Sie die Abfragen in abgeleitete Tabellen mit ihren eigenen
ROWS
-Klauseln.
Beispiele
Die folgenden Beispiele schreiben die Beispiele des Abschnitts über FIRST
und SKIP
, weiter oben in diesem Kapitel, neu.
Gib die ersten zehn Namen aus der Ausgabe einer sortierten Abfrage in der Tabelle PEOPLE
aus:
SELECT id, name
FROM People
ORDER BY name ASC
ROWS 1 TO 10;
oder äquivalent dazu:
SELECT id, name
FROM People
ORDER BY name ASC
ROWS 10;
Gib alle Datensätze aus der PEOPLE
-Tabelle mit Ausnahme der ersten 10 Namen zurück:
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);
Diese 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. FOR UPDATE [OF]
SELECT ... FROM single_table [WHERE ...] [FOR UPDATE [OF ...]]
FOR UPDATE
tut nicht, was es vorgibt.
Der einzige Effekt ist derzeit, den Pre-Fetch-Puffer zu deaktivieren.
Dies wird sich wahrscheinlich in Zukunft ändern: Es ist geplant, mit |
Die OF
-Unterklausel tut rein gar nichts.
6.1.12. WITH LOCK
DSQL, PSQL
Begrenzte pessimistische Sperrung
WITH LOCK
bietet eine begrenzte explizite pessimistische Sperrfunktion für die vorsichtige Verwendung unter Bedingungen, bei denen für den betroffenen Zeilensatz Folgendes gilt:
-
extrem klein (idealerweise ein Singleton), und
-
genau 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 das explizite Sperren in Ihrer Anwendung implementieren. |
SELECT ... FROM single_table [WHERE ...] [FOR UPDATE [OF ...]] WITH LOCK
Wenn die WITH LOCK
-Klausel erfolgreich ist, sichert sie eine Sperre für die ausgewählten Zeilen und verhindert, dass eine andere Transaktion Schreibzugriff auf eine dieser Zeilen oder deren abhängige Elemente erhält, bis die Transaktion endet.
WITH LOCK
kann nur mit einer SELECT
-Anweisung der obersten Ebene verwendet werden.
Es steht nicht zur Verfügung:
-
in einer Unterabfrage-Spezifikation
-
für Join-Sätze
-
mit dem
DISTINCT
-Operator, einerGROUP BY
-Klausel oder einer anderen Aggregat-Operation -
mit einer Ansicht
-
mit der Ausgabe einer abfragbaren gespeicherten Prozedur
-
mit einem externen Tabelle
-
mit einer
UNION
-Abfrage
Da die Engine wiederum berücksichtigt, dass jeder Datensatz unter eine explizite Sperranweisung fällt, gibt sie entweder die derzeit festgeschriebene Datensatzversion (zum Zeitpunkt als die Anweisung gesendet wurde) zurück, unabhängig vom Datenbankstatus, 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 von impliziten oder expliziten 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 festgeschrieben wurde, seit die Transaktion versucht hat, die explizite Sperre zu starten, 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 Ausführung der expliziten Sperre festgeschrieben wurde, wird sofort eine Ausnahme für den Aktualisierungskonflikt ausgelöst. Wenn eine aktive Transaktion den Besitz dieses Datensatzes innehat (durch explizites Sperren oder durch eine normale optimistische Schreibsperre), wartet die Transaktion, die die explizite Sperre verursacht, auf das Ergebnis der blockierenden Transaktion und, wenn sie beendet ist, versucht sie, die Sperre zu wieder aufzuheben. Wenn die blockierende Transaktion eine geänderte Version dieses Datensatzes erstellt hat, wird eine Ausnahme für den Aktualisierungskonflikt ausgelöst. |
isc_tpb_read_committed + isc_tpb_nowait |
Wenn es eine aktive Transaktion gibt, die den Besitz für diesen Datensatz innehat (durch explizites Sperren oder normale Aktualisierung), wird sofort eine Aktualisierungskonfliktausnahme ausgelöst. |
isc_tpb_read_committed + isc_tpb_wait |
Wenn es eine aktive Transaktion gibt, die den Besitz dieses Datensatzes innehat (durch explizites Sperren oder durch eine normale optimistische Schreibsperre), wartet die Transaktion, die die explizite Sperre verursacht, auf das Ergebnis der Blockierungstransaktion und wenn sie beendet ist, versucht sie die Sperre zu wieder aufzuheben. Aktualisierungskonfliktausnahmen können niemals durch eine explizite Sperranweisung in diesem TPB-Modus ausgelöst werden. |
Verwendung einer FOR UPDATE
-Klausel
Wenn die Unterklausel FOR UPDATE
der Unterklausel WITH LOCK
vorangestellt ist, werden gepufferte Abrufe unterdrückt.
Somit wird die Sperre zum Zeitpunkt des Abrufens auf jede Zeile einzeln angewendet.
Es wird dann möglich, dass eine Sperre, die bei Anforderung erfolgreich zu sein scheint, trotzdem anschließend fehlschlägt, wenn versucht wird, eine Zeile abzurufen, die in der Zwischenzeit durch eine andere Transaktion gesperrt wurde.
Als eine Alternative kann es in Ihren Zugriffskomponenten möglich sein, die Größe des Abrufpuffers auf 1 zu setzen. Dies würde es Ihnen ermöglichen, die aktuell gesperrte Zeile zu verarbeiten, bevor die nächste geholt und gesperrt wird, oder Fehler zu behandeln, ohne ihre Transaktion zurückzurollen. |
OF <Spaltennamen> Diese optionale Unterklausel macht ü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 abhängig vom TPB-Modus entweder eine Aktualisierungskonfliktausnahme aus oder wartet auf den Abschluss der Sperrtransaktion.
Das Verhalten der Engine ist hier so, als wäre dieser Datensatz bereits durch die Sperrtransaktion modifiziert worden.
Bei Konflikten mit pessimistischen Sperren werden keine speziellen gdscodes zurückgegeben.
Die Engine garantiert, dass alle von einer expliziten Lock-Anweisung zurückgegebenen Datensätze tatsächlich gesperrt sind und die in der WHERE
-Klausel angegebenen Suchbedingungen erfüllen, solange die Suchbedingungen nicht von anderen Tabellen, Joins, Unterabfragen usw. abhängen.
Außerdem wird garantiert, dass Zeilen, die die Suchbedingungen nicht erfüllen, nicht von der Anweisung gesperrt werden.
Sie kann nicht garantieren, dass es keine Zeilen gibt, die zwar die Suchbedingungen erfüllen, aber nicht gesperrt sind.
Diese Situation kann auftreten, wenn andere parallele Transaktionen ihre Änderungen im Verlauf der Ausführung der Sperranweisung festschreiben. |
Die Engine sperrt Zeilen zum Abrufzeitpunkt. Dies hat wichtige Konsequenzen, wenn Sie mehrere Zeilen gleichzeitig sperren. Bei vielen Zugriffsmethoden für Firebird-Datenbanken wird die Ausgabe standardmäßig in Paketen mit einigen hundert Zeilen abgerufen (“gepufferte Abrufe”). Die meisten Datenzugriffskomponenten können die Zeilen, die im zuletzt abgerufenen Paket enthalten sind, nicht anzeigen, wenn ein Fehler aufgetreten ist.
Fallstricke mit WITH LOCK
-
Durch das Zurücksetzen eines impliziten oder expliziten Sicherungspunkts werden Datensatzsperren freigegeben, die unter diesem Sicherungspunkt ausgeführt wurden, jedoch keine wartenden Transaktionen. Anwendungen sollten nicht von diesem Verhalten abhängig sein, da sie sich in Zukunft ändern können.
-
Während explizite Sperren zum Verhindern und / oder Behandeln ungewöhnlicher Fehler beim Updatekonflikt verwendet werden können, steigt die Anzahl der Deadlockfehler, wenn Sie Ihre Sperrstrategie nicht sorgfältig planen und streng steuern.
-
Die meisten Anwendungen benötigen keine expliziten Sperren. Die Hauptzwecke expliziter Sperren sind (1) die teure Behandlung von Fehlern bei der Aktualisierung von Konflikten in stark ausgelasteten Anwendungen zu verhindern und (2) die Integrität von Objekten zu erhalten, die einer relationalen Datenbank in einer Clusterumgebung zugeordnet sind. Wenn Ihre explizite Sperrung nicht in eine dieser beiden Kategorien fällt, ist dies die falsche Vorgehensweise in Firebird.
-
Explizites Sperren ist eine erweiterte Funktion. Missbrauchen Sie sie nicht! Während Lösungen für diese Art von Problemen sehr wichtig für Websites sein können, die Tausende von gleichzeitigen Schreibzugriffen behandeln, oder für ERP / CRM-Systeme, die in großen Unternehmen arbeiten, müssen die meisten Anwendungsprogramme unter solchen Bedingungen nicht arbeiten.
6.1.13. INTO
Übergabe von SELECT
-Ausgaben an Variablen
PSQL
Im PSQL-Code (Trigger, gespeicherte Prozeduren und ausführbare Blöcke) können die Ergebnisse einer SELECT
-Anweisung Zeile für Zeile in lokale Variablen geladen werden.
Es ist oft die einzige Möglichkeit, etwas mit den zurückgegebenen Werten zu tun.
Die Anzahl, Reihenfolge und Typen der Variablen müssen mit den Spalten in der Ausgabezeile übereinstimmen.
Eine “reine” SELECT
-Anweisung kann nur in PSQL verwendet werden, wenn sie höchstens eine Zeile zurückgibt, d.h. wenn es ein Singleton ist.
Bei mehrzeiligen Auswahlmöglichkeiten bietet PSQL das FOR SELECT
-Schleifenkonstrukt, das später im PSQL-Kapitel erläutert wird.
PSQL unterstützt auch die Anweisung DECLARE CURSOR
, die einen benannten Cursor an eine Anweisung SELECT
bindet.
Der Cursor kann dann verwendet werden, um die Ergebnismenge zu durchlaufen.
In PSQL wird die INTO
-Klausel am Ende des SELECT
-Statements platziert.
SELECT [...] <column-list> FROM ... [...] [INTO <variable-list>] <variable-list> ::= [:]psqlvar [, [:]psqlvar ...]
Das Doppelpunkt-Präfix vor lokalen Variablennamen in PSQL ist optional. |
Beispiele
Einige aggregierte Werte auswählen und an zuvor deklarierte Variablen min_amt
, avg_amt
und max_amt
übergeben:
select min(amount), avg(cast(amount as float)), max(amount)
from orders
where artno = 372218
into min_amt, avg_amt, max_amt;
Die |
Ein PSQL-Trigger der zwei Werte als BLOB
zurückliefert (Verwendung der LIST()
-Funktion) und diese mittels INTO
einem dritten Feld zuweist:
select list(name, ', ')
from persons p
where p.id in (new.father, new.mother)
into new.parentnames;
6.1.14. Common Table Expressions (“WITH … AS … SELECT
”)
DSQL, PSQL
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 sie reguläre Tabellen oder Sichten. CTEs kann rekursiv sein, d.h. sich selbst referenzieren, aber sie können nicht verschachtelt sein.
<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 Hauptanweisung |
name |
Alias für einen Tabellenausdruck |
column-alias |
Alias für eine Spalte in einem Tabellenausdruck |
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
);
CTE-Hinweise
-
Eine CTE-Definition kann ein beliebiges
SELECT
-Statement sein, sofern es keine “WITH…
”-Präampel besitzt (keine Verschachtelung). -
CTEs für die gleiche Hauptabfrage definiert sind, können sich gegenseitig referenzieren, es sollte jedoch darauf geachtet werden, Schleifen zu vermeiden.
-
CTEs kann von überall in der Hauptabfrage referenziert werden.
-
Jede CTE kann in der Hauptabfrage mehrmals referenziert werden, wobei bei Bedarf verschiedene Aliase verwendet werden.
-
Ein in Klammern eingeschlossenes CTE-Konstrukt kann als Unterabfrage in
SELECT
-Statements verwendet werden, aber auch inUPDATE
s,MERGE
s etc. -
In PSQL, werden CTEs auch in
FOR
-Schleifen verwendet:for with my_rivers as (select * from rivers where owner = 'me') select name, length from my_rivers into :rname, :rlen do begin .. end
Wenn eine CTE deklariert ist, muss sie später verwendet werden: Andernfalls erhalten Sie einen Fehler wie diesen: ‘CTE "AAA" is not used in query’. |
Rekursive CTEs
Eine rekursive (selbstverweisende) CTE ist eine UNION
, die mindestens ein nicht-rekursives Member haben muss, das als Anker bezeichnet wird.
Die nicht rekursiven Member müssen vor den rekursiven Membern platziert werden.
Rekursive Member sind untereinander und mit ihrem nicht rekursiven Nachbarn durch UNION ALL
-Operatoren verknüpft.
Die Verbindungen zwischen nicht-rekursiven Mitgliedern können beliebiger Art sein.
Für rekursive CTEs muss das Schlüsselwort RECURSIVE
unmittelbar nach WITH
vorhanden sein.
Jedes rekursive Union-Member darf sich nur einmal selbst referenzieren, und zwar in einer FROM
-Klausel.
Ein großer Vorteil von rekursiven CTEs ist, dass sie viel weniger Speicher und CPU-Zyklen als eine äquivalente rekursive gespeicherte Prozedur verwenden.
Ausführungsmuster
Das Ausführungsmuster einer rekursiven CTE lautet wie folgt:
-
Die Engine beginnt mit der Ausführung von einem nicht rekursiven Element.
-
Für jede ausgewertete Zeile beginnt die Ausführung jedes rekursiven Elements nacheinander, wobei die aktuellen Werte aus der äußeren Reihe 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.
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 Rekursion gleichzeitig in zwei Zweigen des Stammbaums 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
etc) sind in rekursiven Union-Membern 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> [, <ret_value> ...] <ret_value> ::= colname | <value> <variables> ::= [:]varname [, [:]varname ...]
Argument | Beschreibung |
---|---|
target |
Der Name der Tabelle oder Sicht, zu der eine neue Zeile oder ein Stapel von Zeilen hinzugefügt werden soll |
colname |
Spalte in der Tabelle oder in der Ansicht |
value |
Ein Ausdruck, dessen Wert zum Einfügen in die Tabelle verwendet wird |
ret_value |
Der Ausdruck, der in der |
varname |
Name einer lokalen PSQL-Variablen |
Das INSERT
-Statement wird verwendet um Zeilen zu einer Tabelle hinzuzufügen.
Alternativ können Zeilen auch in eine oder mehrere Tabellen eingefügt werden, die als Basis für eine View dienen:
-
Wenn die Spaltenwerte in einer
VALUES
-Klausel angegeben werden, wird genau eine Zeile eingefügt -
Die Werte können stattdessen durch einen Ausdruck
SELECT
bereitgestellt werden, in welchem Fall null bis viele Zeilen eingefügt werden können -
Mit der Klausel
DEFAULT VALUES
werden überhaupt keine Werte bereitgestellt und genau eine Zeile eingefügt.
Beschränkungen
|
ACHTUNG : 'BEFORE INSERT'-Trigger
Berücksichtigen Sie unabhängig von der zum Einfügen von Zeilen verwendeten Methode alle Spalten in der Zieltabelle oder -sicht, die von |
6.2.1. INSERT … VALUES
Die VALUES
-Liste muss für jede Spalte in der Spaltenliste einen Wert in derselben Reihenfolge und mit dem richtigen Typ angeben.
Die Spaltenliste muss nicht jede Spalte im Ziel angeben, aber wenn die Spaltenliste nicht vorhanden ist, benötigt die Engine für jede Spalte in der Tabelle oder Ansicht einen Wert (berechnete Spalten ausgeschlossen).
Introducer-Syntax bietet eine Möglichkeit, den Zeichensatz eines Werts zu identifizieren, der eine Zeichenfolgenkonstante (Literal) ist. Die Introducer-Syntax funktioniert nur mit Literalstrings: Sie kann nicht auf Stringvariablen, Parameter, Spaltenreferenzen oder Werte, die Ausdrücke sind, angewendet werden. |
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
Für diese Einfügemethode müssen die Ausgabespalten der Anweisung SELECT
für jede Zielspalte in der Spaltenliste einen Wert in derselben Reihenfolge und vom richtigen Typ bereitstellen.
Literale Werte, Kontextvariablen oder Ausdrücke des kompatiblen Typs können für jede Spalte in der Quellzeile verwendet werden.
In diesem Fall sind eine Quellenspaltenliste und eine entsprechende VALUES
-Liste erforderlich.
Wenn die Spaltenliste abwesend ist — wie es ist, wenn SELECT *
für den Quellausdruck — verwendet wird, muss die column_list die Namen jeder Spalte in der Zieltabelle oder Sicht enthalten (berechnete Spalten ausgeschlossen).
INSERT INTO cars (make, model, year)
SELECT make, model, year
FROM new_cars;
INSERT INTO cars
SELECT * FROM new_cars;
INSERT INTO Members (number, name)
SELECT number, name FROM NewMembers
WHERE Accepted = 1
UNION ALL
SELECT number, name FROM SuspendedMembers
WHERE Vindicated = 1
INSERT INTO numbers(num)
WITH RECURSIVE r(n) as (
SELECT 1 FROM rdb$database
UNION ALL
SELECT n+1 FROM r WHERE n < 100
)
SELECT n FROM r
Natürlich müssen die Spaltennamen in der Quelltabelle nicht mit denen in der Zieltabelle übereinstimmen.
Jede Art von SELECT
-Anweisung ist zulässig, solange ihre Ausgabespalten exakt mit den Einfügespalten in Anzahl, Reihenfolge und Typ übereinstimmen.
Typen müssen nicht exakt gleich sein, aber sie müssen zuweisungskompatibel sein.
Das Problem mit dem “instabilen Cursor”
In Firebird muss bis zu dieser Version ein Implementierungsfehler geachtet werden, der diese Art von Einfügungen betrifft, wenn das Ziel darin besteht, Zeilen in derselben Tabelle zu duplizieren. Beispielsweise
INSERT INTO T
SELECT * FROM T;
liebevoll als die “unendliche Einfügeschleife” bezeichnet, wählt fortlaufend Zeilen aus und fügt sie immer wieder ein, bis das System keinen Speicherplatz mehr hat.
Dies ist eine Eigenart, die alle datenverändernden DML-Operationen mit einer Vielzahl von Effekten beeinflusst. Dies geschieht, weil DML-Anweisungen in den Ausführungsebenen implizite Cursor zum Ausführen der Operationen verwenden. Mit unserem einfachen Beispiel funktioniert die Ausführung folgendermaßen:
FOR SELECT <values> FROM T INTO <tmp_vars>
DO
INSERT INTO T VALUES (<tmp_vars>);
Die Implementierung führt zu einem Verhalten, das nicht mit den SQL-Standards übereinstimmt. Zukünftige Versionen von Firebird werden dem Standard entsprechen.
6.2.3. INSERT … DEFAULT VALUES
Die DEFAULT VALUES
-Klausel erlaubt das Einfügen eines Datensatzes ohne die Angabe von Werten, weder direkt oder durch ein SELECT
-Statement.
Dies ist nur möglich, wenn jede NOT NULL
- oder CHECK
-basierte Spalte in der Tabelle entweder einen gültigen Standardwert deklariert hat oder die Werte über einen BEFORE INSERT
-Trigger erhält.
Darüber hinaus dürfen Trigger, die erforderliche Feldwerte bereitstellen, nicht von dem Vorhandensein von Eingabewerten abhängen.
INSERT INTO journal
DEFAULT VALUES
RETURNING entry_id;
6.2.4. Die RETURNING
-Klausel
Eine INSERT
-Anweisung, die höchstens eine Zeile hinzufügt, kann optional eine Klausel RETURNING
enthalten, um Werte aus der eingefügten Zeile zurückzugeben.
Die Klausel, falls vorhanden, muss nicht alle Einfügungsspalten enthalten und kann auch andere Spalten oder Ausdrücke enthalten.
Die zurückgegebenen Werte spiegeln alle Änderungen wider, die möglicherweise in den BEFORE INSERT
-Triggern vorgenommen wurden.
ACHTUNG : Mehrfache INSERTs
In DSQL gibt eine Anweisung mit |
INSERT INTO Scholars (
firstname,
lastname,
address,
phone,
email)
VALUES (
'Henry',
'Higgins',
'27A Wimpole Street',
'3231212',
NULL)
RETURNING lastname, fullname, id;
INSERT INTO Dumbbells (firstname, lastname, iq)
SELECT fname, lname, iq
FROM Friends
ORDER BY iq ROWS 1
RETURNING id, firstname, iq
INTO :id, :fname, :iq;
-
RETURNING
wird nur unterstützt fürVALUES
-Inserts und Singleton-SELECT
-Inserts. -
In DSQL gibt eine Anweisung mit einer
RETURNING
-Klausel immer genau eine Zeile zurück. Wenn tatsächlich kein Datensatz eingefügt wurde, sind die Felder in dieser Zeile alleNULL
. Dieses Verhalten kann sich in einer späteren Version von Firebird ändern. Wenn in PSQL keine Zeile eingefügt wurde, wird nichts zurückgegeben und die Zielvariablen behalten ihre vorhandenen Werte bei.
6.2.5. Einfügen in BLOB
-Spalten
Das Einfügen in BLOB
-Spalten ist nur unter folgenden Umständen möglich:
-
Die Client-Anwendung hat mit der Firebird-API spezielle Vorkehrungen für solche Einsätze getroffen. In diesem Fall ist der modus operandi anwendungsspezifisch und außerhalb des Geltungsbereichs dieses Handbuchs.
-
Der eingegebene Wert ist eine Textzeichenfolge von nicht mehr als 32767 Byte.
Wenn der Wert kein String-Literal ist, achten Sie auf Verkettungen, da die Ausgabe des Ausdrucks die maximale Länge überschreiten kann.
-
Sie nutzen die Form “
INSERT … SELECT
” und eine oder mehr Spalten im Rückgabesatz sindBLOB
s.
6.3. UPDATE
Ändern von Zeilen in Tabellen und Sichten
DSQL, ESQL, PSQL
UPDATE target [[AS] alias] SET col = <value> [, col = <value> ...] [WHERE {<search-conditions> | CURRENT OF cursorname}] [PLAN <plan_items>] [ORDER BY <sort_items>] [ROWS m [TO n]] [RETURNING <returning_list> [INTO <variables>]] <returning_list> ::= <ret_value> [, <ret_value> ...] <ret_value> ::= colname | NEW.colname | OLD.colname | <value> <variables> ::= [:]varname [, [:]varname ...]
Argument | Beschreibung |
---|---|
target |
Der Name der Tabelle oder Sicht, in der die Datensätze aktualisiert werden |
alias |
Alias für den Tisch oder die Ansicht |
col |
Name oder Alias einer Spalte in der Tabelle oder Sicht |
newval |
Neuer Wert für eine Spalte, die von der Anweisung in der Tabelle oder Sicht aktualisiert werden soll |
search-conditions |
Eine Suchbedingung, die den Satz der zu aktualisierenden Zeilen begrenzt |
cursorname |
Der Name des Cursors, über den die zu aktualisierende Zeile(n) positioniert wird |
plan_items |
Klauseln im Abfrageplan |
sort_items |
Spalten, die in einer |
m, n |
Integer-Ausdrücke zur Begrenzung der Anzahl der zu aktualisierenden Zeilen |
ret_value |
Ein Wert, der in der |
varname |
Name einer lokalen PSQL-Variablen |
Die Anweisung UPDATE
ändert Werte in einer Tabelle oder in einer oder mehreren Tabellen, die einer Sicht zugrunde liegen.
Die betroffenen Spalten sind in der Klausel SET
angegeben.
Die betroffenen Zeilen können durch die Klauseln WHERE
und ROWS
eingeschränkt sein.
Wenn weder WHERE
noch ROWS
vorhanden ist, werden alle Datensätze in der Tabelle aktualisiert.
6.3.1. Verwendung eines Alias
Wenn Sie einer Tabelle oder einer Ansicht einen Alias zuweisen, muss der Alias verwendet werden, wenn Spalten angegeben werden, und auch in Spaltenreferenzen, die in anderen Klauseln enthalten sind.
Korrekte Verwendung:
update Fruit set soort = 'pisang' where ...
update Fruit set Fruit.soort = 'pisang' where ...
update Fruit F set soort = 'pisang' where ...
update Fruit F set F.soort = 'pisang' where ...
Nicht möglich:
update Fruit F set Fruit.soort = 'pisang' where ...
6.3.2. Die SET
-Klausel
In der Klausel SET
werden die Zuweisungsausdrücke, die die Spalten mit den festzulegenden Werten enthalten, durch Kommata getrennt.
In einer Zuweisungsphrase befinden sich Spaltennamen auf der linken Seite und die Werte oder Ausdrücke, die die Zuweisungswerte enthalten, befinden sich auf der rechten Seite.
Eine Spalte darf nur einmal in der Klausel SET
enthalten sein.
Ein Spaltenname kann in Ausdrücken auf der rechten Seite verwendet werden.
Der alte Wert der Spalte wird immer in diesen Werten auf der rechten Seite verwendet, auch wenn der Spalte bereits in der SET
-Klausel ein neuer Wert zugewiesen wurde.
Daten in der TSET
-Tabelle:
A B
---
1 0
2 0
Die Anweisung
UPDATE tset SET a = 5, b = a;
ändert die Werte zu
A B
---
5 1
5 2
Beachten Sie, dass die alten Werte (1 und 2) verwendet werden, um die Spalte b zu aktualisieren, auch nachdem der Spalte ein neuer Wert zugewiesen wurde (5).
Dies war nicht immer so. Vor der Version 2.5 erhielten Spalten ihre neuen Werte sofort nach der Zuweisung. Das war jedoch kein standardmäßiges Verhalten, und wurde in Version 2.5 behoben. Um die Kompatibilität mit Legacy-Code zu erhalten, enthält die Konfigurationsdatei |
6.3.3. Die WHERE
-Klausel
Die WHERE
-Klausel legt die Bedingungen fest, die die Datensatzgruppe für ein searched update begrenzen.
Wenn in PSQL ein benannter Cursor zum Aktualisieren eines Satzes verwendet wird und die Klausel WHERE CURRENT OF
verwendet wird, ist die Aktion auf die Zeile beschränkt, in der sich der Cursor gerade befindet.
Dies ist ein positioniertes Update.
Die Klausel |
UPDATE People
SET firstname = 'Boris'
WHERE lastname = 'Johnson';
UPDATE employee e
SET salary = salary * 1.05
WHERE EXISTS(
SELECT *
FROM employee_project ep
WHERE e.emp_no = ep.emp_no);
UPDATE addresses
SET city = 'Saint Petersburg', citycode = 'PET'
WHERE city = 'Leningrad'
UPDATE employees
SET salary = 2.5 * salary
WHERE title = 'CEO'
Bei String-Literalen, mit denen der Parser Hilfe beim Interpretieren des Zeichensatzes der Daten benötigt, kann die Einführersyntax verwendet werden. Dem Zeichenfolgenliteral ist der Zeichensatzname vorangestellt, dem ein Unterstrich vorangestellt ist:
-- notice the '_' prefix
UPDATE People
SET name = _ISO8859_1 'Hans-Jörg Schäfer'
WHERE id = 53662;
Das Problem mit dem “instabilen Cursor”
In Firebird muss bis zu dieser Version ein Implementierungsfehler geachtet werden, der diese Art von Aktualisierungen betrifft, wenn die WHERE
-Bedingungen das IN (<select-expr>)
und die select-expr in der Form SELECT FIRST n
oder SELECT … ROWS
vorliegt.
Zum Beispiel
UPDATE T
SET ...
WHERE ID IN (SELECT FIRST 1 ID FROM T);
liebevoll als die “unendliche Update-Schleife” bekannt, wird fortlaufend Zeilen aktualisieren, immer und immer wieder, bis der Server hängen bleibt.
Quarks wie dieses können sich auf datenverändernde DML-Operationen auswirken, meistens wenn die Auswahlbedingungen eine Unterabfrage betreffen. Fälle wurden gemeldet, bei denen die Sortierreihenfolge die Erwartungen beeinträchtigt, ohne dass eine Unterabfrage erforderlich ist. Dies geschieht, weil DML-Anweisungen in den Ausführungsebenen anstelle eines stabilen “Zielsatzes” und anschließendem Ausführen der Datenänderungen für jedes gesetzte Element implizite Cursor zum Ausführen der Operationen für die Zeile verwenden, die derzeit die Bedingungen erfüllt, ohne zu wissen, ob diese Zeile zuvor die Bedingung nicht erfüllt hat oder bereits aktualisiert wurde. Also, mit einem einfachen Beispielmuster:
UPDATE T SET <fields> = <values> WHERE <conditions>
the execution works as:
FOR SELECT <values> FROM T WHERE <conditions> INTO <tmp_vars> AS CURSOR <cursor> DO UPDATE T SET <fields> = <tmp_vars> WHERE CURRENT OF <cursor>
Die Implementierung von Firebird stimmt nicht mit den SQL-Standards überein, nach denen ein stabiles Set eingerichtet werden muss, bevor Daten geändert werden. Versionen von Firebird ab V.3 werden dem Standard entsprechen.
6.3.4. Die ORDER BY
- und ROWS
-Klauseln
Die Klauseln ORDER BY
und ROWS
sind nur sinnvoll, wenn sie zusammen verwendet werden.
Sie können jedoch auch getrennt verwendet werden.
Wenn ROWS
ein Argument, m, hat, sind die zu aktualisierenden Zeilen auf die ersten m Zeilen beschränkt.
-
Wenn m > der Anzahl der Zeilen, die verarbeitet werden, wird der gesamte Zeilensatz aktualisiert
-
Wenn m = 0, wird keine Zeile aktualisiert
-
Wenn m < 0, tritt ein Fehler auf und die Aktualisierung schlägt fehl
Wenn zwei Argumente verwendet werden, m und n, begrenzt ROWS
die Zeilen, die aktualisiert werden, auf Zeilen von m bis n einschließlich.
Beide Argumente sind Ganzzahlen und beginnen bei 1.
-
Wenn m > der Anzahl der zu verarbeitenden Zeilen, werden keine Zeilen aktualisiert
-
Wenn n > der Anzahl der Zeilen, werden Zeilen von m bis zum Ende des Satzes werden aktualisiert
-
Wenn m < 1 oder n < 1, tritt ein Fehler auf und das Update schlägt fehl
-
Wenn n = m - 1, werden keine Zeilen aktualisiert.
-
Wenn n < m -1, ein Fehler tritt auf und die Aktualisierung schlägt fehl
UPDATE employees
SET salary = salary + 50
ORDER BY salary ASC
ROWS 20;
6.3.5. Die RETURNING
-Klausel
Eine Anweisung UPDATE
mit höchstens einer Zeile kann RETURNING
enthalten, um einige Werte aus der aktualisierten Zeile zurückzugeben.
RETURNING
kann Daten aus einer beliebigen Spalte der Zeile enthalten, nicht unbedingt aus den Spalten, die gerade aktualisiert werden.
Es kann Literale enthalten, die nicht mit Spalten verknüpft sind, wenn dies erforderlich ist.
Wenn der RETURNING
-Satz Daten aus der aktuellen Zeile enthält, melden die zurückgegebenen Werte Änderungen, die in den BEFORE UPDATE
Triggern vorgenommen wurden, nicht jedoch in AFTER UPDATE
-Triggern.
Die Kontextvariablen OLD.fieldname
und NEW.fieldname
können als Spaltennamen verwendet werden.
Wenn OLD.
oder NEW.
nicht angegeben ist, sind die zurückgegebenen Spaltenwerte die in NEW.
.
In DSQL gibt eine Anweisung mit RETURNING
immer eine einzelne Zeile zurück.
Wenn die Anweisung keine Datensätze aktualisiert, enthalten die zurückgegebenen Werte NULL
.
Dieses Verhalten kann sich in zukünftigen Firebird-Versionen ändern.
Die INTO
-Unterklausel
In PSQL kann die Klausel INTO
verwendet werden, um die zurückgegebenen Werte an lokale Variablen zu übergeben.
Es ist nicht in DSQL verfügbar.
Wenn keine Datensätze aktualisiert werden, wird nichts zurückgegeben, und die in RETURNING
angegebenen Variablen behalten ihre vorherigen Werte bei.
Wenn ein Wert zurückgegeben und einer
und dies gültig:
|
6.3.6. Aktualisieren von BLOB
-Spalten
Das Aktualisieren einer BLOB
-Spalte ersetzt immer den gesamten Inhalt.
Sogar die BLOB
-ID, das “Handle” welches direkt in der Spalte gespeichert wird, ändert sich.
BLOB
s können aktualisiert werden wenn:
-
Die Client-Anwendung mit der Firebird-API spezielle Vorkehrungen für diese Operation getroffen hat. In diesem Fall ist der modus operandi anwendungsspezifisch und außerhalb des Geltungsbereichs dieses Handbuchs.
-
Der neue Wert eine Textzeichenfolge von höchstens 32767 Byte ist. Bitte beachten Sie: Wenn der Wert kein String-Literal ist, achten Sie auf Verkettungen, da diese die maximale Länge überschreiten können.
-
Die Quelle selbst eine
BLOB
-Spalte oder allgemeiner ein Ausdruck ist, der einBLOB
zurückgibt. -
Sie die Anweisung
INSERT CURSOR
verwenden (nur ESQL).
6.4. UPDATE OR INSERT
Updating an existing record in a table or, if it does not exist, inserting it
DSQL, PSQL
UPDATE OR INSERT INTO target [(<column_list>)] VALUES (<value_list>) [MATCHING (<column_list>)] [RETURNING <values> [INTO <variables>]] <column_list> ::= colname [, colname ...] <value_list> ::= <value> [, <value> ...] <returning_list> ::= <ret_value> [, <ret_value> ...] <ret_value> ::= colname | NEW.colname | OLD.colname | <value> <variables> ::= [:]varname [, [:]varname ...]
Argument | Beschreibung |
---|---|
target |
The name of the table or view where the record[s] is to be updated or a new record inserted |
colname |
Name of a column in the table or view |
value |
An expression whose value is to be used for inserting or updating the table |
ret_value |
An expression returned in the RETURNING clause |
varname |
Variable name — PSQL only |
UPDATE OR INSERT
inserts a new record or updates one or more existing records.
The action taken depends on the values provided for the columns in the MATCHING
clause (or, if the latter is absent, in the primary key).
If there are records found matching those values, they are updated.
If not, a new record is inserted.
A match only counts if all the values in the MATCHING
or PK
columns are equal.
Matching is done with the IS NOT DISTINCT
operator, so one NULL
matches another.
Beschränkungen
|
6.4.1. The RETURNING
clause
The optional RETURNING
clause, if present, need not contain all the columns mentioned in the statement and may also contain other columns or expressions.
The returned values reflect any changes that may have been made in BEFORE
triggers, but not those in AFTER
triggers. OLD.fieldname
and NEW.fieldname
may both be used in the list of columns to return;
for field names not preceded by either of these, the new value is returned.
In DSQL, a statement with a RETURNING
clause always returns exactly one row.
If a RETURNING
clause is present and more than one matching record is found, an error is raised.
This behaviour may change in a later version of Firebird.
6.4.2. Beispiel
Modifying data in a table, using UPDATE OR INSERT
in a PSQL module.
The return value is passed to a local variable, whose colon prefix is optional.
UPDATE OR INSERT INTO Cows (Name, Number, Location)
VALUES ('Suzy Creamcheese', 3278823, 'Green Pastures')
MATCHING (Number)
RETURNING rec_id into :id;
The “Unstable Cursor” Problem
Because of the way the execution of data-changing DML is implemented in Firebird, up to and including this version, the sets targeted for updating sometimes produce unexpected results.
For more information, refer to The Unstable Cursor Problem in the |
6.5. DELETE
Löschen von Zeilen aus einer Tabelle oder Ansicht
DSQL, ESQL, PSQL
DELETE FROM target [[AS] alias] [WHERE {<search-conditions> | CURRENT OF cursorname}] [PLAN <plan_items>] [ORDER BY <sort_items>] [ROWS m [TO n]] [RETURNING <returning_list> [INTO <variables>]] <returning_list> ::= <ret_value> [, <ret_value> ...] <ret_value> ::= colname | <value> <variables> ::= [:]varname [, [:]varname ...]
Argument | Beschreibung |
---|---|
target |
Der Name der Tabelle oder Sicht, aus der die Datensätze gelöscht werden sollen |
alias |
Alias für die Zieltabelle oder -ansicht |
search-conditions |
Suchbedingung, die den Satz von Zeilen begrenzt, die zum Löschen vorgesehen sind |
cursorname |
Der Name des Cursors, in dem der aktuelle Datensatz zum Löschen positioniert ist |
plan_items |
Abfrageplanklausel |
sort_items |
|
m, n |
Integer-Ausdrücke zum Begrenzen der Anzahl der gelöschten Zeilen |
ret_value |
Ein Ausdruck, der in der |
varname |
Name einer PSQL-Variablen |
DELETE
entfernt Zeilen aus einer Datenbanktabelle oder aus einer oder mehreren Tabellen, die einer Sicht zugrunde liegen.
WHERE
- und ROWS
-Klauseln können die Anzahl der gelöschten Zeilen begrenzen.
Wenn weder WHERE
noch ROWS
vorhanden ist, löscht DELETE
alle Zeilen in der Beziehung.
6.5.1. Aliases
Wenn für die Zieltabelle oder -sicht ein Alias angegeben ist, muss dieser verwendet werden, um alle Feldnamenreferenzen in der Anweisung DELETE
zu qualifizieren.
Unterstützte Verwendung:
delete from Cities where name starting 'Alex';
delete from Cities where Cities.name starting 'Alex';
delete from Cities C where name starting 'Alex';
delete from Cities C where C.name starting 'Alex';
Nicht möglich:
delete from Cities C where Cities.name starting 'Alex';
6.5.2. WHERE
Die WHERE
-Klausel legt die Bedingungen fest, die den Satz von Datensätzen für ein searched delete begrenzen.
Wenn in PSQL ein benannter Cursor zum Löschen eines Satzes verwendet wird und die Klausel WHERE CURRENT OF
verwendet wird, ist die Aktion auf die Zeile beschränkt, in der sich der Cursor gerade befindet.
Dies ist ein positioniertes Update.
Die Klausel |
DELETE FROM People
WHERE firstname <> 'Boris' AND lastname <> 'Johnson';
DELETE FROM employee e
WHERE NOT EXISTS(
SELECT *
FROM employee_project ep
WHERE e.emp_no = ep.emp_no);
DELETE FROM Cities
WHERE CURRENT OF Cur_Cities; -- ESQL and PSQL only
6.5.3. PLAN
Eine PLAN
-Klausel erlaubt dem Benutzer die Operation manuell zu optimieren.
DELETE FROM Submissions
WHERE date_entered < '1-Jan-2002'
PLAN (Submissions INDEX ix_subm_date);
6.5.4. ORDER BY
und ROWS
Die Klausel ORDER BY
sortiert die Menge vor dem eigentlichen Löschen.
Es ist nur in Kombination mit ROWS
sinnvoll, aber auch ohne gültig.
Die Klausel ROWS
begrenzt die Anzahl der gelöschten Zeilen.
Integer-Literale oder beliebige Integer-Ausdrücke können für die Argumente m und n verwendet werden.
Wenn ROWS
ein Argument, m, hat, sind die zu löschenden Zeilen auf die ersten m Zeilen beschränkt.
-
Wenn m > der Anzahl zu verarbeitender Zeilen, wird der gesamte Zeilensatz gelöscht
-
Wenn m = 0, werden keine Zeilen gelöscht
-
Wenn m < 0, tritt ein Fehler auf und die Löschung schlägt fehl
Werden die zwei Argumente, m und n verwendet, begrenzt ROWS
die Anzahl der zu löschenden Zeilen, inklusive m bis n.
Beide Argumente sind Ganzzahlen und beginnen bei 1.
-
Wenn m > der Anzahl der zu verarbeitenden Zeilen, wird keine Zeile gelöscht
-
Wenn m > 0 und <= der Anzahl der Zeilen im Satz und n außerhalb dieser Werte liegt, werden die Zeilen von m bis zum Ende des Satzes gelöscht
-
Wenn m < 1 oder n < 1, wird ein Fehler ausgegeben und das Löschen schlägt fehl
-
Wenn n = m - 1, werden keine Zeilen gelöscht
-
Wenn n < m - 1, wird ein Fehler ausgegeben und das Löschen schlägt fehl
Löschen des ältesten Kaufs:
DELETE FROM Purchases
ORDER BY date ROWS 1;
Löschen der höchsten Kundennummer(n):
DELETE FROM Sales
ORDER BY custno DESC ROWS 1 to 10;
Löschen aller Verkäufe, ORDER BY
-Klausel ist sinnlos:
DELETE FROM Sales
ORDER BY custno DESC;
Löschen eines Datensatzes beginnend vom Ende, z.B. von Z…:
DELETE FROM popgroups
ORDER BY name DESC ROWS 1;
Löschen der fünf ältesten Gruppen:
DELETE FROM popgroups
ORDER BY formed ROWS 5;
Es wird keine Sortierung (ORDER BY
) angegeben, so dass 8 gefundene Datensätze, beginnend mit dem fünften, gelöscht werden:
DELETE FROM popgroups
ROWS 5 TO 12;
6.5.5. RETURNING
Eine Anweisung DELETE
, die in höchstens einer Zeile löscht, kann optional eine Klausel RETURNING
enthalten, um Werte aus der gelöschten Zeile zurückzugeben.
Die Klausel muss, falls vorhanden, nicht alle Spalten der Beziehung enthalten und kann auch andere Spalten oder Ausdrücke enthalten.
Hinweise
|
DELETE FROM Scholars WHERE firstname = 'Henry' and lastname = 'Higgins' RETURNING lastname, fullname, id; DELETE FROM Dumbbells ORDER BY iq DESC ROWS 1 RETURNING lastname, iq into :lname, :iq;
Das Problem mit dem “instabilen Cursor”
Aufgrund der Art und Weise, in der die Ausführung der datenverändernden DML in Firebird bis einschließlich dieser Version implementiert wird, führen die zum Löschen bestimmten Sätze manchmal zu unerwarteten Ergebnissen.
Weitere Informationen finden Sie unter Das Problem mit dem instabilen Cursor Problem im Abschnitt |
6.6. MERGE
Daten aus einer Quellenmenge in eine Zielbeziehung zusammenführen
DSQL, PSQL
MERGE INTO target [[AS] target-alias] USING <source> [[AS] source-alias] ON <join-condition> [ WHEN MATCHED THEN UPDATE SET colname = <value> [, <colname> = <value> ...]] [ WHEN NOT MATCHED THEN INSERT [(<columns>)] VALUES (<values>)] <source> ::= tablename | (<select-stmt>) <columns> ::= colname [, colname ...] <values> ::= <value> [, <value> ...]
Argument | Beschreibung |
---|---|
target |
Name der Zielbeziehung (Tabelle oder änderbare Sicht) |
source |
Datenquelle. Es kann eine Tabelle, eine Ansicht, eine gespeicherte Prozedur oder eine abgeleitete Tabelle sein |
target-alias |
Alias für die Zielbeziehung (Tabelle oder änderbare Sicht) |
source-alias |
Alias für die Quellbeziehung oder den Quellsatz |
join-conditions |
Die ( |
colname |
Name einer Spalte in der Zielbeziehung |
value |
Der Wert, der einer Spalte in der Zieltabelle zugewiesen ist. Es ist ein Ausdruck, der ein Literalwert, eine PSQL-Variable, eine Spalte aus der Quelle oder eine kompatible Kontextvariable sein kann |
Beschreibung
Das MERGE
-Statement führt Daten in einer Tabelle oder aktualisierbaren Sicht zusammen.
Die Quelle kann eine Tabelle, eine Sicht oder ein allgemeines “SELECT FROM
” sein.
Jeder Quelldatensatz wird verwendet, um einen oder mehrere Zieldatensätze zu aktualisieren, einen neuen Datensatz in die Zieltabelle einzufügen oder keinen.
Die durchgeführte Aktion hängt von der angegebenen Join-Bedingung und den WHEN
-Klauseln ab.
Die Bedingung enthält normalerweise einen Vergleich der Felder in den Quell- und Zielbeziehungen.
Hinweise
Mindestens eine der
Derzeit gibt die Variable |
ACHTUNG : Eine weitere Unregelmäßigkeit!
Wenn die Klausel |
MERGE INTO books b
USING purchases p
ON p.title = b.title and p.type = 'bk'
WHEN MATCHED THEN
UPDATE SET b.desc = b.desc || '; ' || p.desc
WHEN NOT MATCHED THEN
INSERT (title, desc, bought) values (p.title, p.desc, p.bought);
MERGE INTO customers c
USING (SELECT * from customers_delta WHERE id > 10) cd
ON (c.id = cd.id)
WHEN MATCHED THEN
UPDATE SET name = cd.name
WHEN NOT MATCHED THEN
INSERT (id, name) values (cd.id, cd.name);
MERGE INTO numbers
USING (
WITH RECURSIVE r(n) AS (
SELECT 1 FROM rdb$database
UNION ALL
SELECT n+1 FROM r WHERE n < 200
)
SELECT n FROM r
) t
ON numbers.num = t.n
WHEN NOT MATCHED THEN
INSERT(num) VALUES(t.n);
Das Problem mit dem “instabilen Cursor”
Aufgrund der Art und Weise, in der die Ausführung der datenverändernden DML in Firebird bis einschließlich dieser Version implementiert wird, führen die für das Zusammenführen ausgewählten Sets manchmal zu unerwarteten Ergebnissen.
Für weitere Informationen vergleichen Sie auch Das Problem mit dem instabilen Cursor im Abschnitt |
6.7. EXECUTE PROCEDURE
Ausführen einer gespeicherten Prozedur
DSQL, ESQL, PSQL
EXECUTE PROCEDURE procname [<inparam> [, <inparam> ...]] | [(<inparam> [, <inparam> ...])] [RETURNING_VALUES <outvar> [, <outvar> ...] | (<outvar> [, <outvar> ...])] <outvar> ::= [:]varname
Argument | Beschreibung |
---|---|
procname |
Name der gespeicherten Prozedur |
inparam |
Ein Ausdruck, der den deklarierten Datentyp eines Eingabeparameters auswertet |
varname |
Eine PSQL-Variable zum Empfangen des Rückgabewerts |
Führt eine ausführbare gespeicherte Prozedur aus, die eine Liste aus einem oder mehreren Eingabeparametern verwendet, wenn sie für die Prozedur definiert sind, und eine einreihige Menge von Ausgabewerten zurückgibt, wenn sie für die Prozedur definiert sind.
6.7.1. “Ausführbare” gespeicherte Prozedur
Das Statement EXECUTE PROCEDURE
wird am häufigsten verwendet, um die Art gespeicherte Prozedur aufzurufen, die geschrieben wird, um eine datenmodifizierende Aufgabe auf der Serverseite auszuführen — das sind die Prozeduren, die kein SUSPEND
-Statement im Code enthalten.
Sie können so entworfen werden, dass sie eine Ergebnismenge zurückgeben, die nur aus einer Zeile besteht, die normalerweise über eine Gruppe von RETURNING_VALUES()
-Variablen an eine andere gespeicherte Prozedur übergeben wird, die sie aufruft.
Clientschnittstellen verfügen normalerweise über einen API-Wrapper, der beim Aufruf von EXECUTE PROCEDURE
in DSQL die Ausgabewerte in einen einzeiligen Puffer abrufen kann.
Das Aufrufen der anderen Prozedurart — eine “auswählbare” — ist möglich mit EXECUTE PROCEDURE
, aber es gibt nur die erste Zeile eines Ausgabesatzes zurück, der fast sicher als mehrzeilig ausgelegt ist.
Auswählbare gespeicherte Prozeduren werden von einer Anweisung SELECT
aufgerufen, die eine Ausgabe erzeugt, die sich wie eine virtuelle Tabelle verhält.
Hinweise
|
6.7.2. Beispiele
In PSQL mit optionalen Doppelpunkten und ohne optionale Klammern:
EXECUTE PROCEDURE MakeFullName
:FirstName, :MiddleName, :LastName
RETURNING_VALUES :FullName;
In Firebirds Befehlszeilenprogramm isql mit Literalparametern und optionalen Klammern:
EXECUTE PROCEDURE MakeFullName ('J', 'Edgar', 'Hoover');
In isql, wird |
Ein PSQL-Beispiel mit Ausdrucksparametern und optionalen Klammern:
EXECUTE PROCEDURE MakeFullName
('Mr./Mrs. ' || FirstName, MiddleName, upper(LastName))
RETURNING_VALUES (FullName);
6.8. EXECUTE BLOCK
Erstellen eines “anonymen” Blocks von PSQL-Code in DSQL zur sofortigen Ausführung
DSQL
EXECUTE BLOCK [(<inparams>)] [RETURNS (<outparams>)] AS [<declarations>] BEGIN [<PSQL statements>] END <inparams> ::= <param_decl> = ? [, <inparams> ] <outparams> ::= <param_decl> [, <outparams>] <param_decl> ::= paramname <type> [NOT NULL] [COLLATE collation] <type> ::= <datatype> | [TYPE OF] domain | TYPE OF COLUMN rel.col <datatype> ::= {SMALLINT | INTEGER | BIGINT} | {FLOAT | DOUBLE PRECISION} | {DATE | TIME | TIMESTAMP} | {DECIMAL | NUMERIC} [(precision [, scale])] | {CHAR | CHARACTER} [VARYING] | VARCHAR} [(size)] [CHARACTER SET charset] | {NCHAR | NATIONAL {CHARACTER | CHAR}} [VARYING] [(size)] | BLOB [SUB_TYPE {subtype_num | subtype_name}] [SEGMENT SIZE seglen] [CHARACTER SET charset] | BLOB [(seglen [, subtype_num])] <declarations> ::= <declare_item> [<declare_item> ...] <declare_item> ::= <declare_var>; | <declare_cursor>;
Argument | Beschreibung |
---|---|
param_decl |
Name und Beschreibung eines Eingabe- oder Ausgabeparameters |
declarations |
Ein Abschnitt zum Deklarieren von lokalen Variablen und benannten Cursorn |
declare_var |
Lokale Variablendeklaration |
declare_cursor |
Deklaration eines benannten Cursors |
paramname |
Der Name eines Eingabe- oder Ausgabeparameters des prozeduralen Blocks mit bis zu 31 Zeichen. Der Name muss unter Ein- und Ausgabeparametern und lokalen Variablen im Block eindeutig sein |
datatype |
SQL-Datentyp |
collation |
Sortierreihenfolge |
domain |
Domain |
rel |
Name einer Tabelle oder Ansicht |
col |
Name einer Spalte in einer Tabelle oder Sicht |
precision |
Präzision. Von 1 bis 18 |
scale |
Rahmen. Von 0 bis 18. Es muss kleiner oder gleich precision sein |
size |
Die maximale Größe einer Zeichenfolge in Zeichen |
charset |
Zeichensatz |
subtype_num |
|
subtype_name |
mnemotechnischer Name des |
seglen |
Segmentgröße, kann nicht größer als 65.535 sein |
Führt einen PSQL-Block aus, als wäre es eine gespeicherte Prozedur, optional mit Eingabe- und Ausgabeparametern und Variablendeklarationen. Dies ermöglicht dem Benutzer “on-the-fly” PSQL innerhalb eines DSQL-Kontexts auszuführen.
In diesem Beispiel werden die Zahlen 0 bis 127 und die entsprechenden ASCII-Zeichen in die Tabelle ASCIITABLE
eingefügt:
EXECUTE BLOCK
AS
declare i INT = 0;
BEGIN
WHILE (i < 128) DO
BEGIN
INSERT INTO AsciiTable VALUES (:i, ascii_char(:i));
i = i + 1;
END
END
Das nächste Beispiel berechnet das geometrische Mittel zweier Zahlen und gibt es an den Benutzer zurück:
EXECUTE BLOCK (x DOUBLE PRECISION = ?, y DOUBLE PRECISION = ?)
RETURNS (gmean DOUBLE PRECISION)
AS
BEGIN
gmean = SQRT(x*y);
SUSPEND;
END
Da dieser Block Eingabeparameter hat, muss er zuerst vorbereitet werden.
Dann können die Parameter eingestellt und der Block ausgeführt werden.
Es hängt von der Client-Software ab, wie dies durchgeführt werden muss.
Auch wenn dies möglich ist — beachten Sie die folgenden Hinweise.
Unser letztes Beispiel nimmt zwei ganzzahlige Werte, smallest
und largest
.
Für alle Zahlen im Bereich smallest
…largest
gibt der Block die Zahl selbst, sein Quadrat, seine dritte und seine vierte Potenz aus.
EXECUTE BLOCK (smallest INT = ?, largest INT = ?)
RETURNS (number INT, square BIGINT, cube BIGINT, fourth BIGINT)
AS
BEGIN
number = smallest;
WHILE (number <= largest) DO
BEGIN
square = number * number;
cube = number * square;
fourth = number * cube;
SUSPEND;
number = number + 1;
END
END
Auch hier hängt es von der Client-Software ab, ob und wie Sie die Parameterwerte einstellen können.
6.8.1. Eingabe- und Ausgabeparameter
Das Ausführen eines Blocks ohne Eingabeparameter sollte mit jedem Firebird-Client möglich sein, der es dem Benutzer erlaubt, seine eigenen DSQL-Anweisungen einzugeben.
Wenn es Eingabeparameter gibt, werden die Dinge komplizierter: Diese Parameter müssen ihre Werte erhalten, nachdem die Anweisung vorbereitet wurde, aber bevor sie ausgeführt wird.
Dies erfordert spezielle Voraussetzungen, die nicht jede Client-Anwendung bietet.
(Firebirds eigenes isql
, zum Beispiel, nicht.)
Der Server akzeptiert nur Fragezeichen (“?
”) als Platzhalter für die Eingabewerte, also nicht “:a
”, “:MyParam
” etc., oder Literalwerte.
Client-Software unterstützt möglicherweise die Form “:xxx
” und wandelt es vor dem Senden an den Server um.
Wenn der Block Ausgabeparameter hat, müssen Sie SUSPEND
verwenden, sonst wird nichts zurückgegeben.
Die Ausgabe erfolgt immer in Form einer Ergebnismenge, genau wie bei einer Anweisung mit SELECT
.
Sie können weder RETURNING_VALUES
verwenden noch einen Block mit INTO
in Variablen anwenden, auch wenn es nur eine Ergebniszeile gibt.
6.8.2. Statement-Terminatoren
Einige SQL-Statement-Editoren — insbesondere das isql-Dienstprogramm, 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 erzeugt einen Konflikt mit der PSQL-Syntax beim Codieren in diesen Umgebungen. Wenn Sie mit diesem Problem und seiner Lösung nicht vertraut sind, lesen Sie die Details im Kapitel PSQL im Abschnitt Umschalten des Terminators in isql.
7. Prozedurale SQL-Anweisungen (PSQL)
Prozedurales SQL (PSQL) ist eine prozedurale Erweiterung von SQL. Diese Sprachuntermenge wird zum Schreiben von gespeicherten Prozeduren, Triggern und PSQL-Blöcken verwendet.
PSQL bietet alle grundlegenden Konstrukte traditioneller strukturierter Programmiersprachen und enthält auch DML-Anweisungen (SELECT
, INSERT
, UPDATE
, DELETE
usw.), in einigen Fällen mit geringfügigen Änderungen der Syntax.
7.1. Elemente der PSQL
Eine prozedurale Erweiterung kann Deklarationen von lokalen Variablen und Cursorn, Zuweisungen, bedingten Anweisungen, Schleifen, Anweisungen zum Abrufen von benutzerdefinierten Ausnahmen, Fehlerbehandlung und Senden von Nachrichten (Ereignissen) an Clientanwendungen enthalten.
Trigger haben Zugriff auf spezielle Kontextvariablen, zwei Arrays, die die NEW
-Werte für alle Spalten während der Einfüge- und Aktualisierungsaktivität bzw. die OLD
-Werte während der Aktualisierungs- und Löscharbeiten speichern.
Anweisungen, die Metadaten ändern (DDL), sind in PSQL nicht verfügbar.
7.1.1. DML-Anweisungen mit Parametern
Wenn DML-Anweisungen (SELECT
, INSERT
, UPDATE
, DELETE
usw.) im Rumpf des Moduls (Prozedur, Trigger oder Block) Parameter verwenden, können nur benannte Parameter verwendet werden und sie müssen “existieren” bevor die Anweisung diese verwenden kann.
Sie können verfügbar gemacht werden, indem sie entweder als Ein- oder Ausgabeparameter im Header des Moduls oder als lokale Variablen in DECLARE [VARIABLE]
-Anweisungen im unteren Headerbereich deklariert werden.
Wenn eine DML-Anweisung mit Parametern im PSQL-Code enthalten ist, muss dem Parameternamen in den meisten Situationen ein Doppelpunkt (‘:
’) vorangestellt werden.
Der Doppelpunkt ist in PSQL-spezifischer Anweisungssyntax wie Zuweisungen und Bedingungen optional.
Das Doppelpunktpräfix für Parameter ist nicht erforderlich, wenn gespeicherte Prozeduren von einem anderen PSQL-Modul oder in DSQL aufgerufen werden.
7.1.2. Transaktionen
Gespeicherte Prozeduren werden im Kontext der Transaktion ausgeführt, in der sie aufgerufen werden. Trigger werden als ein intrinsischer Teil der Operation der DML-Anweisung ausgeführt: ihre Ausführung befindet sich also innerhalb des gleichen Transaktionskontextes wie die Anweisung selbst. Einzelne Transaktionen werden für Datenbank-Trigger gestartet.
Anweisungen, die Transaktionen starten und beenden, sind in PSQL nicht verfügbar, aber es ist möglich, eine Anweisung oder einen Anweisungsblock in einer autonomen Transaktion auszuführen.
7.1.3. Modulstruktur
PSQL-Codemodule bestehen aus einem Header und einem Body.
Die DDL-Anweisungen zum Definieren dieser sind komplexe Anweisungen;
das heißt, sie sind Bestandteile einer einzigen Anweisung, die Blöcke von mehreren Anweisungen umfasst.
Diese Anweisungen beginnen mit einem Verb (CREATE
, ALTER
, DROP
, RECREATE
, CREATE OR ALTER
) und enden mit die letzten END
-Anweisung des Bodys.
Der Modul-Header
Der Header gibt den Modulnamen an und definiert alle Parameter und Variablen, die im Rumpf verwendet werden. Gespeicherte Prozeduren und PSQL-Blöcke können Ein- und Ausgangsparameter haben. Trigger haben keine Ein- oder Ausgangsparameter.
Der Header eines Triggers zeigt das Datenbankereignis (Einfügen, Aktualisieren oder Löschen oder eine Kombination) und die Betriebsphase (vor (BEFORE
) oder nach (AFTER
) diesem Ereignis) an, die dazu führt, dass dieser “ausgelöst wird”.
Der Modul-Body
Der Rumpf eines PSQL-Moduls ist ein Block von Anweisungen, die wie ein Programm in einer logischen Reihenfolge ablaufen.
Ein Anweisungsblock ist in einer BEGIN
- und einer END
-Anweisung enthalten.
Der Hauptblock BEGIN … END
kann beliebig viele andere BEGIN … END
-Blöcke enthalten, sowohl eingebettete als auch sequenzielle.
Alle Anweisungen außer BEGIN
und END
werden durch Semikolons (‘;
’) abgeschlossen.
Kein anderes Zeichen ist als Terminator für PSQL-Anweisungen gültig.
7.2. Gespeicherte Prozeduren
Eine gespeicherte Prozedur ist ein Programm, das in den Datenbankmetadaten zur Ausführung auf dem Server gespeichert ist. Eine gespeicherte Prozedur kann durch gespeicherte Prozeduren (einschließlich sich selbst), Trigger und Clientanwendungen aufgerufen werden. Eine Prozedur, die sich selbst aufruft, heißt rekursiv.
7.2.1. Vorteile von gespeicherten Prozeduren
Gespeicherte Prozeduren besitzen die folgenden Vorteile:
Modularität |
Anwendungen, die mit der Datenbank arbeiten, können die gleiche gespeicherte Prozedur verwenden, wodurch die Größe des Anwendungscodes reduziert wird und eine Codeduplizierung vermieden wird. |
Vereinfachte Anwendungsunterstützung |
Wenn eine gespeicherte Prozedur geändert wird, werden Änderungen sofort allen Host-Anwendungen angezeigt, ohne dass sie bei unveränderten Parametern neu kompiliert werden müssen. |
Verbesserte Leistung |
Da gespeicherte Prozeduren auf einem Server statt auf dem Client ausgeführt werden, wird der Netzwerkverkehr reduziert, wodurch die Leistung verbessert wird. |
7.2.2. Varianten der gespeicherten Prozeduren
Firebird untertützt zwei Arten der gespeicherten Prozeduren: executable (ausführbar) selectable (abfragbar).
Ausführbare Prozeduren
Ausführbare Prozeduren ändern normalerweise Daten in einer Datenbank.
Sie können Eingabeparameter empfangen und einen einzigen Satz von Ausgabeparametern (RETURNS
) zurückgeben.
Sie werden mit der Anweisung EXECUTE PROCEDURE
aufgerufen.
Siehe auch ein Beispiel für eine ausführbare gespeicherte Prozedur am Ende des Abschnitts CREATE PROCEDURE
von Kapitel 5.
Abfragbare Prozeduren
Abfragbare bzw. auswählbare gespeicherte Prozeduren rufen normalerweise Daten aus einer Datenbank ab und geben eine beliebige Anzahl von Zeilen an den Aufrufer zurück. Der Aufrufer erhält die Ausgabe Zeile für Zeile aus einem Zeilenpuffer, der von der Datenbank-Engine darauf vorbereitet wird.
Abfragbare Prozeduren können nützlich sein, um komplexe Datensätze zu erhalten, die mit regulären DSQL SELECT
-Abfragen oft nicht oder nur schwer oder zu langsam abgerufen werden können.
Typischerweise iteriert diese Art der Prozedur durch einen Schleifenprozess des Extrahierens von Daten, wobei sie möglicherweise transformiert wird, bevor die Ausgangsvariablen (Parameter) bei jeder Iteration der Schleife mit frischen Daten gefüllt werden.
Eine SUSPEND
-Anweisung am Ende der Iteration füllt den Puffer und wartet darauf, dass der Aufrufer die Zeile abfragt.
Die Ausführung der nächsten Iteration der Schleife beginnt, wenn der Puffer gelöscht wurde.
Abfragbare Prozeduren können Eingabeparameter haben, und der Ausgabesatz wird durch die Klausel RETURNS
im Header angegeben.
Eine abfragbare gespeicherte Prozedur wird mit einer SELECT
-Anweisung aufgerufen.
Siehe auch ein Beispiel für eine abfragbare gespeicherte Prozedur am Ende des Abschnitts CREATE PROCEDURE
von Kapitel 5.
7.2.3. Erstellen einer gespeicherten Prozedur
Die Syntax zum Erstellen ausführbarer gespeicherter Prozeduren und abfragbarer gespeicherter Prozeduren ist exakt gleich. Der Unterschied liegt in der Logik des Programmcodes.
CREATE PROCEDURE procname [(<inparam> [, <inparam> ...])] [RETURNS (<outparam> [, <outparam> ...])] AS [<declarations>] BEGIN [<PSQL_statements>] END
Der Header einer gespeicherten Prozedur muss den Prozedurnamen enthalten und muss unter den Namen gespeicherter Prozeduren, Tabellen und Ansichten eindeutig sein.
Es kann auch einige Ein- und Ausgabeparameter definieren.
Eingabeparameter werden nach dem Prozedurnamen in Klammern angegeben.
Ausgabeparameter, die für abfragbare Prozeduren obligatorisch sind, sind innerhalb einer Klausel RETURNS
eingeklammert.
Das letzte Element im Header (oder das erste Element im Textkörper, abhängig von Ihrer Ansicht darüber, wo die Grenze liegt) umfasst eine oder mehrere Deklarationen von lokalen Variablen und / oder benannten Cursorn die Ihre Prozedur möglicherweise erfordert.
Nach den Deklarationen folgt der Hauptblock BEGIN … END
, der den PSQL-Code der Prozedur beschreibt.
Innerhalb dieses Blocks könnten PSQL- und DML-Anweisungen, Ablaufsteuerungsblöcke, Sequenzen anderer BEGIN … END
-Blöcke einschließlich eingebetteter Blöcke sein.
Blöcke, einschließlich des Hauptblocks, können leer sein und die Prozedur wird trotzdem kompiliert.
Es ist nicht ungewöhnlich, ein Verfahren in Stufen aus einem Gliederung zu entwickeln.
Siehe auch CREATE PROCEDURE
in Kapitel 5, Data Definition (DDL) Statements.
7.2.4. Anpassen einer gespeicherten Prozedur
Eine vorhandene gespeicherte Prozedur kann geändert werden, um die Sätze von Ein- und Ausgabeparametern und alles im Prozedurhauptteil zu ändern.
ALTER PROCEDURE procname [(<inparam> [, <inparam> ...])] [RETURNS (<outparam> [, <outparam> ...])] AS [<declarations>] BEGIN [<PSQL_statements>] END
Siehe auch ALTER PROCEDURE
, CREATE OR ALTER PROCEDURE
, RECREATE PROCEDURE
, in Kapitel 5, Data Definition (DDL) Statements.
7.2.5. Löschen einer gespeicherte Prozedur
Die Anweisung DROP PROCEDURE
wird verwendet um gespeicherte Prozeduren zu löschen.
DROP PROCEDURE procname
See DROP PROCEDURE
in Kapitel 5, Data Definition (DDL) Statements.
7.3. Gespeicherte Funktionen (Stored Functions)
Gespeicherte PSQL-Skalarfunktionen werden in dieser Version nicht unterstützt, sie kommen jedoch in Firebird 3.
In Firebird 2.5 und niedriger können Sie stattdessen eine abfragbare gespeicherte Prozedur schreiben, die ein Skalarergebnis zurückgibt, und SELECT
aus Ihrer DML-Abfrage oder Unterabfrage.
SELECT
PSQL_FUNC(T.col1, T.col2) AS col3,
col3
FROM T
kann ersetzt werden durch
SELECT
(SELECT output_column FROM PSQL_PROC(T.col1)) AS col3,
col2
FROM T
oder
SELECT
output_column AS col3,
col2,
FROM T
LEFT JOIN PSQL_PROC(T.col1)
7.4. PSQL-Blöcke
Ein in sich abgeschlossener, unbenannter (“anonymous”) Block von PSQL-Code kann dynamisch in DSQL unter Verwendung der Syntax EXECUTE BLOCK
ausgeführt werden.
Der Header eines anonymen PSQL-Blocks kann optional Eingabe- und Ausgabeparameter enthalten.
Der Körper kann lokale Variablen und Cursordeklarationen enthalten.
Ein Block von PSQL-Anweisungen folgt.
Ein anonymer PSQL-Block wird nicht definiert und als Objekt gespeichert, im Gegensatz zu gespeicherten Prozeduren und Triggern. Er wird zur Laufzeit ausgeführt und kann nicht auf sich selbst verweisen.
Genau wie gespeicherte Prozeduren können anonyme PSQL-Blöcke verwendet werden, um Daten zu verarbeiten und Daten aus der Datenbank abzurufen.
EXECUTE BLOCK [(<inparam> = ? [, <inparam> = ? ...])] [RETURNS (<outparam> [, <outparam> ...])] AS [<declarations>] BEGIN [<PSQL_statements>] END
Argument | Beschreibung |
---|---|
inparam |
Beschreibung der Eingabeparameter |
outparam |
Beschreibung der Ausgangsparameter |
declarations |
Ein Abschnitt zum Deklarieren lokaler Variablen und benannter Cursor |
PSQL statements |
PSQL- und DML-Anweisungen |
Siehe auch EXECUTE BLOCK
für weitere Details.
7.5. Trigger
Ein Trigger ist eine andere Form von ausführbarem Code, der in den Metadaten der Datenbank zur Ausführung durch den Server gespeichert wird. Ein Trigger kann nicht direkt aufgerufen werden. Er wird automatisch aufgerufen (“gefeuert”), wenn Datenänderungsereignisse mit einer bestimmten Tabelle oder Sicht (View) auftreten.
Ein Trigger gilt für genau eine Tabelle oder Sicht und nur eine Phase in einem Ereignis (vor (BEFORE
) oder nach (AFTER
) dem Ereignis).
Ein einzelner Trigger kann nur dann ausgelöst werden, wenn ein bestimmtes Datenänderungsereignis auftritt (INSERT
/ UPDATE
/ DELETE
) oder wenn es auf mehr als eines dieser Ereignisse angewendet werden soll.
Ein DML-Trigger wird im Kontext der Transaktion ausgeführt, in der die datenändernde DML-Anweisung ausgeführt wird. Bei Triggern, die auf Datenbankereignisse reagieren, ist die Regel unterschiedlich: Für einige von ihnen wird eine Standardtransaktion gestartet.
7.5.1. Reihenfolge der Ausführung
Für jede Phase-Ereignis-Kombination kann mehr als ein Trigger definiert werden.
Die Reihenfolge, in der sie ausgeführt werden (bekannt als “firing order”, kann explizit mit dem optionalen Argument POSITION
in der Triggerdefinition angegeben werden.)
Sie haben 32.767 Nummern zur Auswahl.
Die niedrigsten Positionsnummern feuern zuerst.
Wenn eine Klausel POSITION
weggelassen wird oder mehrere übereinstimmende Ereignisphasen-Trigger die gleiche Positionsnummer haben, werden die Trigger in alphabetischer Reihenfolge ausgelöst.
7.5.2. DML-Trigger
DML-Trigger sind solche, die ausgelöst werden, wenn eine DML-Operation den Datenstatus ändert: Zeilen in Tabellen ändern, neue Zeilen einfügen oder Zeilen löschen. Sie können sowohl für Tabellen als auch für Ansichten definiert werden.
Trigger-Optionen
Für die Ereignis-Phasen-Kombination für Tabellen und Ansichten stehen sechs Basisoptionen zur Verfügung:
Bevor eine neue Zeile eingefügt wird |
|
Nachdem eine neue Zeile eingefügt wurde |
|
Bevor eine Zeile aktualisiert wird |
|
Nachdem eine Zeile aktualisiert wurde |
|
Bevor eine Zeile gelöscht wird |
|
Nachdem eine Zeile gelöscht wurde |
|
Diese Basisformulare dienen zum Erstellen von Einzelphasen- / Einzelereignisauslösern.
Firebird unterstützt auch Formulare zum Erstellen von Auslösern für eine Phase und mehrere Ereignisse, z.B. BEFORE INSERT OR UPDATE OR DELETE
, oder AFTER UPDATE OR DELETE
: Die Kombinationen unterliegen Ihrer Wahl.
“Multiphasen-”-Trigger, wie |
Kontextvariablen OLD
und NEW
Für DML-Trigger bietet die Firebird-Engine Zugriff auf Sätze von OLD
- und NEW
-Kontextvariablen.
Jedes ist ein Array der Werte der gesamten Zeile: eine für die Werte, wie sie vor dem Datenänderungsereignis sind (die BEFORE
-Phase) und eine für die Werte, wie sie nach dem Ereignis sein werden (die AFTER
-Phase).
Sie werden in Anweisungen referenziert, die das Formular NEW.column_name
bzw. OLD.column_name
verwenden.
column_name
kann eine beliebige Spalte in der Definition der Tabelle sein, nicht nur die, die gerade aktualisiert wird.
Die Variablen NEW
und OLD
unterliegen einigen Regeln:
-
In allen Triggern ist der
OLD
-Wert schreibgeschützt -
In
BEFORE UPDATE
undBEFORE INSERT
Code wird derNEW
-Wert gelesen / geschrieben, sofern es sich nicht um eineCOMPUTED BY
-Spalte handelt -
In
INSERT
-Triggern sind Verweise auf dieOLD
-Variablen ungültig und lösen eine Ausnahme aus -
In
DELETE
-Triggern sind Verweise auf dieNEW
-Variablen ungültig und lösen eine Ausnahme aus -
In allen
AFTER
-Triggercodes sind dieNEW
-Variablen schreibgeschützt
7.5.3. Datenbank-Trigger
Ein mit einer Datenbank oder einem Transaktionsereignis verknüpfter Trigger kann für die folgenden Ereignisse definiert werden:
Verbindung mit einer Datenbank herstellen |
|
Bevor der Trigger ausgeführt wird, wird automatisch eine Standardtransaktion gestartet |
Trennen von einer Datenbank |
|
Bevor der Trigger ausgeführt wird, wird automatisch eine Standardtransaktion gestartet |
Wenn eine Transaktion gestartet wird |
|
Der Trigger wird im aktuellen Transaktionskontext ausgeführt |
Wenn eine Transaktion übergeben wird |
|
Der Trigger wird im aktuellen Transaktionskontext ausgeführt |
Wenn eine Transaktion abgebrochen wird |
|
Der Trigger wird im aktuellen Transaktionskontext ausgeführt |
7.5.4. Trigger erstellen
CREATE TRIGGER trigname { <relation_trigger_legacy> | <relation_trigger_sql2003> | <database_trigger> } AS [<declarations>] BEGIN [<PSQL_statements>] END <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] <mutation_list> ::= <mutation> [OR <mutation> [OR <mutation>]] <mutation> ::= { INSERT | UPDATE | DELETE } <db_event> ::= CONNECT | DISCONNECT | TRANSACTION START | TRANSACTION COMMIT | TRANSACTION ROLLBACK
Der Header muss einen Namen für den Trigger enthalten, der unter den Triggernamen eindeutig ist. Er muss das Ereignis oder die Ereignisse enthalten, die den Auslöser auslösen. Für einen DML-Trigger müssen Sie außerdem die Ereignisphase und den Namen der Tabelle oder Ansicht angeben, die den Trigger “besitzen” soll.
Der Rumpf des Triggers kann durch die Deklarationen von lokalen Variablen und Cursorn, falls vorhanden, geleitet werden.
Innerhalb des umschließenden Hauptblocks von BEGIN … END
befinden sich ein oder mehrere Blöcke von PSQL-Anweisungen, die leer sein können.
Siehe CREATE TRIGGER
in Kapitel 5, Data Definition (DDL) Statements.
7.5.5. Trigger ändern
Das Ändern der Status-, Phasen-, Tabellen- oder Ansichtsereignisse, der Auslöseposition und des Codes im Rumpf eines DML-Triggers ist möglich.
Sie können jedoch einen DML-Trigger nicht ändern, um ihn in einen Datenbank-Trigger zu konvertieren, und umgekehrt.
Jedes nicht angegebene Element wird von ALTER TRIGGER
nicht geändert.
Die alternativen Anweisungen CREATE OR ALTER TRIGGER
und RECREATE TRIGGER
ersetzen die ursprüngliche Triggerdefinition vollständig.
ALTER TRIGGER trigname [ACTIVE | INACTIVE] [{BEFORE | AFTER} <mutation_list> | ON <db_event>] [POSITION number] [ AS [<declarations>] BEGIN [<PSQL_statements>] END ] <mutation_list> ::= <mutation> [OR <mutation> [OR <mutation>]] <mutation> ::= { INSERT | UPDATE | DELETE } <db_event> ::= { CONNECT | DISCONNECT | TRANSACTION START | TRANSACTION COMMIT | TRANSACTION ROLLBACK }
Siehe ALTER TRIGGER
, CREATE OR ALTER TRIGGER
, RECREATE TRIGGER
in Kapitel 5, Data Definition (DDL) Statements.
7.5.6. Trigger löschen
Die Anweisung DROP TRIGGER
dient zum Löschen von Triggern.
DROP TRIGGER trigname
Siehe DROP TRIGGER
in Kapitel 5, Data Definition (DDL) Statements.
7.6. Schreiben des Body-Codes
In diesem Abschnitt werden die prozeduralen SQL-Sprachkonstrukte und -Anweisungen näher betrachtet, die zum Codieren des Rumpfs einer gespeicherten Prozedur, eines Triggers oder eines anonymen PSQL-Blocks verfügbar sind.
7.6.1. Zuweisungs-Statements
Zuweisen eines Werts zu einer Variablen
PSQL
varname = <value_expr>
Argument | Beschreibung |
---|---|
varname |
Name eines Parameters oder einer lokalen Variablen |
value_expr |
Ein Ausdruck, eine Konstante oder eine Variable, dessen Wert in den gleichen Datentyp wie varname |
PSQL verwendet das Äquivalenzsymbol (‘=
’) als Zuweisungsoperator.
Die Zuweisungsanweisung weist der Variablen links vom Operator den rechten SQL-Ausdruckswert zu.
Der Ausdruck kann ein beliebiger gültiger SQL-Ausdruck sein: Er kann Literale, interne Variablennamen, Arithmetik-, logische und Zeichenfolgenoperationen, Aufrufe von internen Funktionen oder externe Funktionen (UDFs) enthalten.
CREATE PROCEDURE MYPROC (
a INTEGER,
b INTEGER,
name VARCHAR (30)
)
RETURNS (
c INTEGER,
str VARCHAR(100))
AS
BEGIN
-- assigning a constant
c = 0;
str = '';
SUSPEND;
-- assigning expression values
c = a + b;
str = name || CAST(b AS VARCHAR(10));
SUSPEND;
-- assigning expression value
-- built by a query
c = (SELECT 1 FROM rdb$database);
-- assigning a value from a context variable
str = CURRENT_USER;
SUSPEND;
END
7.6.2. DECLARE CURSOR
Deklarieren eines benannten Cursors
PSQL
DECLARE [VARIABLE] cursorname CURSOR FOR (<select>) [FOR UPDATE]
Argument | Beschreibung |
---|---|
cursorname |
Name des Cursors |
select |
|
Die Anweisung DECLARE CURSOR … FOR
bindet einen benannten Cursor an die Ergebnismenge, die in der in der Klausel FOR
angegebenen SELECT
-Anweisung ermittelt wurde.
Im Body-Code kann der Cursor geöffnet werden, um zeilenweise durch die Ergebnismenge zu gehen und zu schließen.
Während der Cursor geöffnet ist, kann der Code positionierte Aktualisierungen und Löschungen unter Verwendung der Anweisung WHERE CURRENT OF
für UPDATE
oder DELETE
durchführen.
Cursor-Idiosynkrasien
-
Die optionale Klausel
FOR UPDATE
kann in derSELECT
-Anweisung enthalten sein, ihre Abwesenheit verhindert jedoch nicht die erfolgreiche Ausführung einer positionierten Aktualisierung oder Löschung. -
Es sollte darauf geachtet werden, dass die Namen von deklarierten Cursorn nicht mit Namen in Konflikt geraten, die später in Anweisungen für
AS CURSOR
-Klauseln verwendet werden. -
Wenn der Cursor nur zum Durchlaufen der Ergebnismenge benötigt wird, ist es fast immer einfacher und weniger fehleranfällig, eine Anweisung
FOR SELECT
mit der KlauselAS CURSOR
zu verwenden. Deklarierte Cursor müssen zum Abrufen von Daten explizit geöffnet und geschlossen werden. Die KontextvariableROW_COUNT
muss nach jedem Abruf überprüft werden. Wenn der Wert Null ist, muss die Schleife beendet werden. EineFOR SELECT
-Anweisung überprüft dies automatisch.Dennoch bieten deklarierte Cursor ein hohes Maß an Kontrolle über sequentielle Ereignisse und ermöglichen die parallele Verwaltung mehrerer Cursor.
-
Das
SELECT
-Statement kann Parameter enthalten. Zum Beispiel:SELECT NAME || :SFX FROM NAMES WHERE NUMBER = :NUM
Jeder Parameter muss zuvor als PSQL-Variable deklariert worden sein, auch wenn sie als Ein- und Ausgabeparameter entstehen. Wenn der Cursor geöffnet wird, wird dem Parameter der aktuelle Wert der Variablen zugewiesen.
Achtung!
Wenn sich der Wert einer PSQL-Variablen, die in der Beachten Sie besonders, dass das Verhalten möglicherweise vom Abfrageplan abhängt, insbesondere von den verwendeten Indizes. Es gibt derzeit keine strengen Regeln für solche Situationen, aber das könnte sich in zukünftigen Versionen von Firebird ändern. |
Beispiel für benannte Cursor
-
Declaring a named cursor in the trigger.
CREATE OR ALTER TRIGGER TBU_STOCK BEFORE UPDATE ON STOCK AS DECLARE C_COUNTRY CURSOR FOR ( SELECT COUNTRY, CAPITAL FROM COUNTRY ); BEGIN /* PSQL statements */ END
-
Eine Sammlung von Skripts zum Erstellen von Ansichten mit einem PSQL-Block unter Verwendung von benannten Cursors.
EXECUTE BLOCK RETURNS ( SCRIPT BLOB SUB_TYPE TEXT) AS DECLARE VARIABLE FIELDS VARCHAR(8191); DECLARE VARIABLE FIELD_NAME TYPE OF RDB$FIELD_NAME; DECLARE VARIABLE RELATION RDB$RELATION_NAME; DECLARE VARIABLE SOURCE TYPE OF COLUMN RDB$RELATIONS.RDB$VIEW_SOURCE; DECLARE VARIABLE CUR_R CURSOR FOR ( SELECT RDB$RELATION_NAME, RDB$VIEW_SOURCE FROM RDB$RELATIONS WHERE RDB$VIEW_SOURCE IS NOT NULL); -- Declaring a named cursor where -- a local variable is used DECLARE CUR_F CURSOR FOR ( SELECT RDB$FIELD_NAME FROM RDB$RELATION_FIELDS WHERE -- It is important that the variable must be declared earlier RDB$RELATION_NAME = :RELATION); BEGIN OPEN CUR_R; WHILE (1 = 1) DO BEGIN FETCH CUR_R INTO :RELATION, :SOURCE; IF (ROW_COUNT = 0) THEN LEAVE; FIELDS = NULL; -- The CUR_F cursor will use the value -- of the RELATION variable initiated above OPEN CUR_F; WHILE (1 = 1) DO BEGIN FETCH CUR_F INTO :FIELD_NAME; IF (ROW_COUNT = 0) THEN LEAVE; IF (FIELDS IS NULL) THEN FIELDS = TRIM(FIELD_NAME); ELSE FIELDS = FIELDS || ', ' || TRIM(FIELD_NAME); END CLOSE CUR_F; SCRIPT = 'CREATE VIEW ' || RELATION; IF (FIELDS IS NOT NULL) THEN SCRIPT = SCRIPT || ' (' || FIELDS || ')'; SCRIPT = SCRIPT || ' AS ' || ASCII_CHAR(13); SCRIPT = SCRIPT || SOURCE; SUSPEND; END CLOSE CUR_R; END
7.6.3. DECLARE VARIABLE
Deklaration einer lokalen Variablen
PSQL
DECLARE [VARIABLE] varname {<datatype> | domain | TYPE OF {domain | COLUMN rel.col} [NOT NULL] [CHARACTER SET charset] [COLLATE collation] [{DEFAULT | = } <initvalue>]; <datatype> ::= {SMALLINT | INTEGER | BIGINT} | {FLOAT | DOUBLE PRECISION} | {DATE | TIME | TIMESTAMP} | {DECIMAL | NUMERIC} [(precision [, scale])] | {CHAR | CHARACTER [VARYING] | VARCHAR} [(size)] [CHARACTER SET charset] | {NCHAR | NATIONAL {CHARACTER | CHAR}} [VARYING] [(size)] | BLOB [SUB_TYPE {subtype_num | subtype_name}] [SEGMENT SIZE seglen] [CHARACTER SET charset] | BLOB [(seglen [, subtype_num])] <initvalue> ::= <literal> | <context_var>
Argument | Beschreibung |
---|---|
varname |
Name der lokalen Variable |
datatype |
Ein SQL-Datentyp |
domain |
Der Name einer bestehenden Domain in dieser Datenbank |
rel.col |
Beziehungsname (Tabelle oder Sicht) in dieser Datenbank und der Name einer Spalte in dieser Beziehung |
precision |
Präzision. Von 1 bis 18 |
scale |
Rahmen. Von 0 bis 18 muss es kleiner oder gleich der Genauigkeit sein |
size |
Die maximale Größe einer Zeichenfolge in Zeichen |
subtype_num |
|
subtype_name |
Mnemonischer Name des |
seglen |
Segmentgröße, nicht größer als 65.535 |
initvalue |
Anfangswert für diese Variable |
literal |
Literal eines Typs, der mit dem Typ der lokalen Variablen kompatibel ist |
context_var |
Jede Kontextvariable, deren Typ mit dem Typ der lokalen Variablen kompatibel ist |
charset |
Zeichensatz |
collation |
Sortierfolge |
Die Anweisung DECLARE [VARIABLE]
wird zum Deklarieren einer lokalen Variable verwendet.
Das Schlüsselwor VARIABLE
kann weggelassen werden.
Je ein DECLARE [VARIABLE]
-Statement ist für jede Variable notwendig.
Jede beliebige Anzahl von DECLARE [VARIABLE]
-Statements kann in jeglicher Reihenfolge eingefügt werden.
Der Name jeder lokalen Variable muss eindeutig innerhalb der lokalen Variablen und Ausgabeparametern in der Moduldeklaration sein.
Datentypen für Variablen
Eine lokale Variable kann von einem anderen SQL-Typ als ein Array sein.
-
Ein Domainname kann als Typ angegeben werden und die Variable erbt alle ihre Attribute.
-
Wenn die
TYPE OF domain
-Klausel verwendet wird, erbt die Variable nur den Datentyp der Domain und gegebenenfalls ihre Zeichensatz- und Sortierattribute. Alle Standardwerte oder Einschränkungen wieNOT NULL
- oderCHECK
-Einschränkungen werden nicht vererbt. -
Wenn die Option
TYPE OF COLUMN relation.column
verwendet wird, um Daten aus einer Spalte in einer Tabelle oder Sicht zu “leihen”, wird die Variable nur den Datentyp der Spalte erben, und gegebenenfalls ihren Zeichensatz und Sortierattribute. Alle anderen Attribute werden ignoriert.
NOT NULL
-Constraint
Die Variable kann bei Bedarf auf NOT NULL
beschränkt werden.
Wenn eine Domain als Datentyp angegeben wurde und bereits die NOT NULL
-Einschränkung enthält, ist sie nicht erforderlich.
Bei den anderen Formen, einschließlich der Verwendung einer Domain, die nullwertfähig ist, sollte das NOT NULL
-Attribut bei Bedarf eingefügt werden.
CHARACTER SET
- und COLLATE
-Klauseln
Sofern nicht anders angegeben, sind der Zeichensatz und die Sortierfolge einer String-Variablen die Standardeinstellungen der Datenbank.
Eine Klausel CHARACTER SET
kann bei Bedarf eingefügt werden, um Zeichenkettendaten zu verarbeiten, die sich in einem anderen Zeichensatz befinden.
Eine gültige Sortierreihenfolge (COLLATE
-Klausel) kann ebenfalls mit oder ohne die Zeichensatzklausel eingeschlossen werden.
Initialisieren einer Variablen
Lokale Variablen sind NULL
, wenn die Ausführung des Moduls beginnt.
Sie können initialisiert werden, sodass ein Start- oder Standardwert verfügbar ist, wenn sie zum ersten Mal referenziert werden.
Die Form DEFAULT <initvalue>
kann verwendet werden, oder nur der Zuweisungsoperator, ‘=
’: = <initvalue>
.
Der Wert kann ein beliebiges Typ-kompatibles Literal oder eine Kontextvariable sein.
Stellen Sie sicher, dass Sie diese Klausel für alle Variablen verwenden, die auf |
Beispiele für verschiedene Möglichkeiten, lokale Variablen zu deklarieren
CREATE OR ALTER PROCEDURE SOME_PROC
AS
-- Declaring a variable of the INT type
DECLARE I INT;
-- Declaring a variable of the INT type that does not allow NULL
DECLARE VARIABLE J INT NOT NULL;
-- Declaring a variable of the INT type with the default value of 0
DECLARE VARIABLE K INT DEFAULT 0;
-- Declaring a variable of the INT type with the default value of 1
DECLARE VARIABLE L INT = 1;
-- Declaring a variable based on the COUNTRYNAME domain
DECLARE FARM_COUNTRY COUNTRYNAME;
-- Declaring a variable of the type equal to the COUNTRYNAME domain
DECLARE FROM_COUNTRY TYPE OF COUNTRYNAME;
-- Declaring a variable with the type of the CAPITAL column in the COUNTRY table
DECLARE CAPITAL TYPE OF COLUMN COUNTRY.CAPITAL;
BEGIN
/* PSQL statements */
END
7.6.4. BEGIN … END
Einen Block von Anweisungen abgrenzen
PSQL
<block> ::= BEGIN [<compound_statement> …] END <compound_statement> ::= {<block> | <statement>;}
Das Konstrukt BEGIN … END
ist eine zweiteilige Anweisung, die einen Block von Anweisungen umhüllt, die als eine Codeeinheit ausgeführt werden.
Jeder Block beginnt mit der Halb-Anweisung BEGIN
und endet mit der anderen Halb-Anweisung END
.
Blöcke können in unbegrenzter Tiefe verschachtelt werden.
Sie können leer sein, so dass sie als Stubs fungieren können, ohne dass Dummy-Anweisungen geschrieben werden müssen.
Die Anweisungen BEGIN
und END
haben keine Zeilenabschlußzeichen.
Wenn jedoch ein PSQL-Modul im Dienstprogramm isql definiert oder geändert wird, muss für diese Anwendung der letzten END
-Anweisung ein eigenes Terminatorzeichen folgen, das zuvor mithilfe von SET TERM
auf eine andere Zeichenfolge als ein Semikolon umgeschaltet wurde.
Dieser Terminator ist nicht Teil der PSQL-Syntax.
Die letzte oder äußerste END
-Anweisung in einem Trigger beendet den Trigger.
Was die letzte Anweisung END
in einer gespeicherten Prozedur macht, hängt vom Typ der Prozedur ab:
-
In einer abfragbaren Prozedur gibt die endgültige Anweisung
END
die Steuerung an den Aufrufer zurück und gibt SQLCODE 100 zurück, um anzugeben, dass keine weiteren Zeilen abgerufen werden müssen. -
In einer ausführbaren Prozedur gibt die endgültige Anweisung
END
die Kontrolle an den Aufrufer zurück, zusammen mit den aktuellen Werten aller definierten Ausgabeparameter.
Eine Beispielprozedur aus der Datenbank employee.fdb
, die die einfache Verwendung der Blöcke BEGIN … END
zeigt:
SET TERM ^;
CREATE OR ALTER PROCEDURE DEPT_BUDGET (
DNO CHAR(3))
RETURNS (
TOT DECIMAL(12,2))
AS
DECLARE VARIABLE SUMB DECIMAL(12,2);
DECLARE VARIABLE RDNO CHAR(3);
DECLARE VARIABLE CNT INTEGER;
BEGIN
TOT = 0;
SELECT
BUDGET
FROM
DEPARTMENT
WHERE DEPT_NO = :DNO
INTO :TOT;
SELECT
COUNT(BUDGET)
FROM
DEPARTMENT
WHERE HEAD_DEPT = :DNO
INTO :CNT;
IF (CNT = 0) THEN
SUSPEND;
FOR
SELECT
DEPT_NO
FROM
DEPARTMENT
WHERE HEAD_DEPT = :DNO
INTO :RDNO
DO
BEGIN
EXECUTE PROCEDURE DEPT_BUDGET(:RDNO)
RETURNING_VALUES :SUMB;
TOT = TOT + SUMB;
END
SUSPEND;
END^
SET TERM ;^
7.6.5. IF … THEN … ELSE
Bedingte Sprünge
PSQL
IF (<condition>) THEN <compound_statement> [ELSE <compound_statement>]
Argument | Beschreibung |
---|---|
condition |
Eine logische Bedingung, die TRUE, FALSE oder UNKNOWN zurückgibt |
single_statement |
Eine einzelne Anweisung wurde mit einem Semikolon abgeschlossen |
compound_statement |
Zwei oder mehr Anweisungen, die in |
Die bedingte Sprunganweisung IF … THEN
wird verwendet, um den Ausführungsprozess in einem PSQL-Modul zu verzweigen.
Die Bedingung ist immer in Klammern eingeschlossen.
Wenn es den Wert TRUE zurückgibt, verzweigt die Ausführung in die Anweisung oder den Anweisungsblock nach dem Schlüsselwort THEN
.
Wenn eine ELSE
vorhanden ist und die Bedingung FALSE oder UNKNOWN zurückgibt, verzweigt die Ausführung in die Anweisung oder den Anweisungsblock danach.
Ein Beispiel mit der IF
-Anweisung.
Angenommen, die Variablen FIRST
, LINE2
und LAST
wurden früher deklariert.
...
IF (FIRST IS NOT NULL) THEN
LINE2 = FIRST || ' ' || LAST;
ELSE
LINE2 = LAST;
...
7.6.6. WHILE … DO
Schleifenkonstrukte
PSQL
WHILE <condition> DO <compound_statement>
Argument | Beschreibung |
---|---|
condition |
Eine logische Bedingung, die TRUE, FALSE oder UNKNOWN zurückgibt |
single_statement |
Eine einzelne Anweisung wurde mit einem Semikolon abgeschlossen |
compound_statement |
Zwei oder mehr Anweisungen, die in |
Eine WHILE
-Anweisung implementiert das Schleifenkonstrukt in PSQL.
Die Anweisung oder der Anweisungsblock wird ausgeführt, bis die Bedingung TRUE zurückgibt.
Schleifen können beliebig tief verschachtelt werden.
Eine Prozedur, die die Summe der Zahlen von 1 bis I berechnet, zeigt, wie das Schleifenkonstrukt verwendet wird.
CREATE PROCEDURE SUM_INT (I INTEGER)
RETURNS (S INTEGER)
AS
BEGIN
s = 0;
WHILE (i > 0) DO
BEGIN
s = s + i;
i = i - 1;
END
END
Ausführen der Prozedur in isql:
EXECUTE PROCEDURE SUM_INT(4);
Das Ergebnis ist:
S
==========
10
IF … THEN … ELSE
, LEAVE
, EXIT
, FOR SELECT
, FOR EXECUTE STATEMENT
7.6.7. LEAVE
Eine Schleife beenden
PSQL
[label:] <loop_stmt> BEGIN ... LEAVE [label]; ... END <loop_stmt> ::= FOR <select_stmt> INTO <var_list> DO | FOR EXECUTE STATEMENT ... INTO <var_list> DO | WHILE (<condition>)} DO
Argument | Beschreibung |
---|---|
label |
Label |
select_stmt |
|
condition |
Eine logische Bedingung, die TRUE, FALSE oder UNKNOWN zurückgibt |
Eine LEAVE
-Anweisung beendet sofort die innere Schleife einer WHILE
oder FOR
Schleifenanweisung.
Der Parameter LABEL
ist optional.
LEAVE
kann auch zum Beenden von äußeren Schleifen führen.
Code wird weiterhin von der ersten Anweisung nach der Beendigung des äußeren Schleifenblocks ausgeführt.
-
Eine Schleife verlassen, wenn bei einem Einfügen in die
NUMBERS
-Tabelle ein Fehler auftritt. Der Code wird weiterhin von der ZeileC = 0
ausgeführt.... WHILE (B < 10) DO BEGIN INSERT INTO NUMBERS(B) VALUES (:B); B = B + 1; WHEN ANY DO BEGIN EXECUTE PROCEDURE LOG_ERROR ( CURRENT_TIMESTAMP, 'ERROR IN B LOOP'); LEAVE; END END C = 0; ...
-
Ein Beispiel für die Verwendung von Labels in der
LEAVE
-Anweisung.LEAVE LOOPA
beendet die äußere Schleife undLEAVE LOOPB
beendet die innere Schleife. Beachten Sie, dass die einfache AnweisungLEAVE
ausreichen würde, um die innere Schleife zu beenden.... STMT1 = 'SELECT NAME FROM FARMS'; LOOPA: FOR EXECUTE STATEMENT :STMT1 INTO :FARM DO BEGIN STMT2 = 'SELECT NAME ' || 'FROM ANIMALS WHERE FARM = '''; LOOPB: FOR EXECUTE STATEMENT :STMT2 || :FARM || '''' INTO :ANIMAL DO BEGIN IF (ANIMAL = 'FLUFFY') THEN LEAVE LOOPB; ELSE IF (ANIMAL = FARM) THEN LEAVE LOOPA; ELSE SUSPEND; END END ...
7.6.8. EXIT
Beenden der Modulausführung
PSQL
EXIT
Die Anweisung EXIT
bewirkt, dass die Ausführung der Prozedur oder des Triggers von jedem Punkt des Codes zur endgültigen END
-Anweisung springt, wodurch das Programm beendet wird.
Verwenden der EXIT
-Anweisung in einer abfragbaren Prozedur:
CREATE PROCEDURE GEN_100
RETURNS (
I INTEGER
)
AS
BEGIN
I = 1;
WHILE (1=1) DO
BEGIN
SUSPEND;
IF (I=100) THEN
EXIT;
I = I + 1;
END
END
7.6.9. SUSPEND
Übergeben der Ausgabe an den Puffer und Aussetzen der Ausführung, während darauf gewartet wird, dass der Aufrufer sie abruft
PSQL
SUSPEND
Die Anweisung SUSPEND
wird in einer abfragbaren gespeicherten Prozedur verwendet, um die Werte von Ausgabeparametern an einen Puffer zu übergeben und die Ausführung anzuhalten.
Die Ausführung bleibt ausgesetzt, bis die aufrufende Anwendung den Inhalt des Puffers abruft.
Die Ausführung wird von der Anweisung direkt nach der SUSPEND
-Anweisung fortgesetzt.
In der Praxis ist dies wahrscheinlich eine neue Iteration eines Schleifenprozesses.
Wichtige Hinweise
|
Verwenden der Anweisung SUSPEND
in einer abfragbaren Prozedur:
CREATE PROCEDURE GEN_100
RETURNS (
I INTEGER
)
AS
BEGIN
I = 1;
WHILE (1=1) DO
BEGIN
SUSPEND;
IF (I=100) THEN
EXIT;
I = I + 1;
END
END
7.6.10. EXECUTE STATEMENT
Ausführen dynamisch erstellter SQL-Anweisungen
PSQL
<execute_statement> ::= EXECUTE STATEMENT <argument> [<option> …] [INTO <variables>] <argument> ::= <paramless_stmt> | (<paramless_stmt>) | (<stmt_with_params>) (<param_values>) <param_values> ::= <named_values> | <positional_values> <named_values> ::= paramname := <value_expr> [, paramname := <value_expr> ...] <positional_values> ::= <value_expr> [, <value_expr> ...] <option> ::= WITH {AUTONOMOUS | COMMON} TRANSACTION | WITH CALLER PRIVILEGES | AS USER user | PASSWORD password | ROLE role | ON EXTERNAL [DATA SOURCE] <connect_string> <connect_string> ::= [<hostspec>] {filepath | db_alias} <hostspec> ::= <tcpip_hostspec> | <NamedPipes_hostspec> <tcpip_hostspec> ::= hostname[/port]: <NamePipes_hostspec> ::= \\hostname\ <variables> ::= [:]varname [, [:]varname ...]
Argument | Beschreibung |
---|---|
paramless_stmt |
Literale Zeichenfolge oder Variable, die eine nicht parametrisierte SQL-Abfrage enthält |
stmt_with_params |
Literale Zeichenfolge oder Variable, die eine parametrisierte SQL-Abfrage enthält |
paramname |
Name des SQL-Abfrageparameters |
value_expr |
SQL-Ausdruck, der in einen Wert aufgelöst wird |
user |
Nutzername.
Dies kann eine Zeichenfolge, |
password |
Passwort. Es kann eine Zeichenfolge oder eine Zeichenfolgevariable sein |
role |
Rolle.
Dies kann eine Zeichenfolge, |
connection_string |
Verbindungszeichenfolge. Es kann eine Zeichenfolge oder eine Zeichenfolgevariable sein |
filepath |
Pfad zur primären Datenbankdatei |
db_alias |
Datenbankalias |
hostname |
Computername oder IP-Adresse |
varname |
Variable |
Die Anweisung EXECUTE STATEMENT
verwendet einen Zeichenfolgenparameter und führt ihn wie eine DSQL-Anweisung aus.
Wenn die Anweisung Daten zurückgibt, kann sie über eine INTO
-Klausel an lokale Variablen übergeben werden.
Parametrisierte Anweisungen
Sie können die Parameter — entweder benannt oder positional — in der DSQL-Anweisungsfolge verwenden. Jedem Parameter muss ein Wert zugewiesen werden.
Spezielle Regeln für parametrisierte Anweisungen
-
Benannte und positionale Parameter können nicht in einer Abfrage gemischt werden
-
Wenn die Anweisung Parameter hat, müssen sie beim Aufruf von
EXECUTE STATEMENT
in Klammern stehen, unabhängig davon, ob sie direkt als Strings, als Variablennamen oder als Ausdrücke verwendet werden -
Jedem benannten Parameter muss in der Anweisungszeichenfolge ein Doppelpunkt (‘
:
’) vorangestellt werden, jedoch nicht, wenn dem Parameter ein Wert zugewiesen ist -
Positionsparameter müssen ihre Werte in derselben Reihenfolge erhalten, in der sie im Abfragetext erscheinen
-
Der Zuweisungsoperator für Parameter ist der Spezialoperator “
:=
”, ähnlich dem Zuweisungsoperator in Pascal -
Jeder benannte Parameter kann mehrmals in der Anweisung verwendet werden, sein Wert muss jedoch nur einmal zugewiesen werden
-
Bei Positionsparametern muss die Anzahl der zugewiesenen Werte genau der Anzahl der Parameterplatzhalter (Fragezeichen) in der Anweisung entsprechen
-
Ein benannter Parameter im Anweisungstext kann nur ein regulärer Bezeichner sein (er darf kein begrenzter Bezeichner sein)
Beispiele
Mit benannten Paramtern:
...
DECLARE license_num VARCHAR(15);
DECLARE connect_string VARCHAR (100);
DECLARE stmt VARCHAR (100) =
'SELECT license
FROM cars
WHERE driver = :driver AND location = :loc';
BEGIN
...
SELECT connstr
FROM databases
WHERE cust_id = :id
INTO connect_string;
...
FOR
SELECT id
FROM drivers
INTO current_driver
DO
BEGIN
FOR
SELECT location
FROM driver_locations
WHERE driver_id = :current_driver
INTO current_location
DO
BEGIN
...
EXECUTE STATEMENT (stmt)
(driver := current_driver,
loc := current_location)
ON EXTERNAL connect_string
INTO license_num;
...
Derselbe Code mit Positionsparametern:
DECLARE license_num VARCHAR (15);
DECLARE connect_string VARCHAR (100);
DECLARE stmt VARCHAR (100) =
'SELECT license
FROM cars
WHERE driver = ? AND location = ?';
BEGIN
...
SELECT connstr
FROM databases
WHERE cust_id = :id
into connect_string;
...
FOR
SELECT id
FROM drivers
INTO current_driver
DO
BEGIN
FOR
SELECT location
FROM driver_locations
WHERE driver_id = :current_driver
INTO current_location
DO
BEGIN
...
EXECUTE STATEMENT (stmt)
(current_driver, current_location)
ON EXTERNAL connect_string
INTO license_num;
...
WITH {AUTONOMOUS | COMMON} TRANSACTION
Üblicherweise lief die ausgeführte SQL-Anweisung immer innerhalb der aktuellen Transaktion, und dies ist immer noch der Standardwert.
WITH AUTONOMOUS TRANSACTION
bewirkt, dass eine separate Transaktion mit denselben Parametern wie die aktuelle Transaktion gestartet wird.
Es wird festgeschrieben, wenn die Anweisung ohne Fehler ausgeführt wird und andernfalls zurückgesetzt wird.
WITH COMMON TRANSACTION
verwendet, wenn möglich, die aktuelle Transaktion.
Wenn die Anweisung in einer separaten Verbindung ausgeführt werden muss, wird eine bereits gestartete Transaktion innerhalb dieser Verbindung verwendet, sofern verfügbar. Andernfalls wird eine neue Transaktion mit den gleichen Parametern wie die aktuelle Transaktion gestartet. Alle neuen Transaktionen, die unter dem “COMMON”-Regime gestartet wurden, werden mit der aktuellen Transaktion festgeschrieben oder zurückgesetzt.
WITH CALLER PRIVILEGES
Standardmäßig wird die SQL-Anweisung mit den Berechtigungen des aktuellen Benutzers ausgeführt.
Die Angabe von WITH CALLER PRIVILEGES
fügt dazu die Privilegien der aufrufenden Prozedur oder des Triggers hinzu, so als ob die Anweisung direkt von der Routine ausgeführt würde.
WITH WITH CALLER PRIVILEGES
hat keine Auswirkung, wenn die Klausel ON EXTERNAL
ebenfalls vorhanden ist.
ON EXTERNAL [DATA SOURCE]
Mit ON EXTERNAL [DATA SOURCE]
wird die SQL-Anweisung in einer separaten Verbindung zu derselben oder einer anderen Datenbank ausgeführt, möglicherweise sogar auf einem anderen Server.
Wenn die Verbindungszeichenfolge NULL oder “''
” (leere Zeichenfolge) ist, wird die gesamte Klausel ON EXTERNAL [DATA SOURCE]
als abwesend betrachtet und die Anweisung wird für die aktuelle Datenbank ausgeführt.
Verbindungspooling
-
Externe Verbindungen, die durch Anweisungen
WITH COMMON TRANSACTION
(der Standardwert) hergestellt werden, bleiben geöffnet, bis die aktuelle Transaktion beendet wird. Sie können durch nachfolgende Aufrufe anEXECUTE STATEMENT
wiederverwendet werden, aber nur, wenn die Verbindungszeichenfolge genau gleich ist, einschließlich case -
Externe Verbindungen, die durch Anweisungen
WITH AUTONOMOUS TRANSACTION
hergestellt werden, werden geschlossen, sobald die Anweisung ausgeführt wurde -
Beachten Sie, dass Statements unter
WITH AUTONOMOUS TRANSACTION
-Verbindungen, die zuvor von Anweisungen unterWITH COMMON TRANSACTION
geöffnet wurden, wiederverwendet werden. Wenn dies geschieht, bleibt die wiederverwendete Verbindung nach der Ausführung der Anweisung offen. (Dies geschieht, da es mindestens eine nicht-abgeschlossene Transaktion gibt!)
Transaktionspooling
-
Wenn
WITH COMMON TRANSACTION
aktiviert ist, werden Transaktionen so oft wie möglich wiederverwendet. Sie werden zusammen mit der aktuellen Transaktion festgeschrieben oder zurückgesetzt -
Wenn
WITH AUTONOMOUS TRANSACTION
angegeben ist, wird immer eine neue Transaktion für die Anweisung gestartet. Diese Transaktion wird unmittelbar nach der Ausführung der Anweisung festgeschrieben oder zurückgesetzt
Ausnahmebehandlung
Ausnahmebehandlung: Wenn ON EXTERNAL
verwendet wird, erfolgt die zusätzliche Verbindung immer über einen sogenannten externen Provider, auch wenn die Verbindung zur aktuellen Datenbank besteht.
Eine der Folgen ist, dass Ausnahmen nicht auf die übliche Art und Weise abgefangen werden können.
Jede von der Anweisung verursachte Ausnahme wird entweder in einen eds_connection
- oder einen eds_statement
-Fehler enden.
Um sie in Ihrem PSQL-Code abzufangen, müssen Sie WHEN GDSCODE eds_connection
, WHEN GDSCODE eds_statement
oder WHEN ANY
verwenden.
Ohne |
AS USER
, PASSWORD
und ROLE
Die optionalen Klauseln AS USER
, PASSWORD
und ROLE
erlauben die Angabe unter welchem Benutzer und unter welcher Rolle das SQL-Statement ausgeführt wird.
Die Methode der Benutzeranmeldung und die Existenz einer separaten offenen Verbindung hängt von dem Vorhandensein und den Werten der Klauseln ON EXTERNAL [DATA SOURCE]
, AS USER
, PASSWORD
und ROLE
ab:
-
Wenn
ON EXTERNAL
verwendet wird, wird immer eine neue Verbindung aufgebaut und:-
Wenn mindestens eines von
AS USER
,PASSWORD
undROLE
vorhanden ist, wird die native Authentifizierung mit den angegebenen Parameterwerten versucht (lokal oder remote abhängig von der Verbindungszeichenfolge). Für fehlende Parameter werden keine Standardwerte verwendet -
Wenn alle drei nicht vorhanden sind und die Verbindungszeichenfolge keinen Hostnamen enthält, wird die neue Verbindung auf dem lokalen Host mit demselben Benutzer und derselben Rolle wie die aktuelle Verbindung hergestellt. Der Begriff "lokal" bedeutet hier “auf der gleichen Maschine wie der Server”. Dies ist nicht unbedingt der Standort des Clients
-
Wenn alle drei nicht vorhanden sind und die Verbindungszeichenfolge einen Hostnamen enthält, wird eine vertrauenswürdige Authentifizierung auf dem Remote-Host versucht (aus der Perspektive des Servers wiederum "Remote"). Wenn dies erfolgreich ist, gibt das Remote-Betriebssystem den Benutzernamen an (normalerweise das Betriebssystemkonto, unter dem der Firebird-Prozess ausgeführt wird).
-
-
Fehlt
ON EXTERNAL
:-
Wenn mindestens eines von
AS USER
,PASSWORD
undROLE
vorhanden ist, wird eine neue Verbindung zur aktuellen Datenbank mit den angegebenen Parameterwerten geöffnet. Für fehlende Parameter werden keine Standardwerte verwendet -
Wenn alle drei nicht vorhanden sind, wird die Anweisung innerhalb der aktuellen Verbindung ausgeführt
-
Hinweis
Wenn ein Parameterwert |
Vorsicht mit EXECUTE STATEMENT
-
Es gibt keine Möglichkeit, die Syntax der enthaltenen Anweisung zu überprüfen
-
Es gibt keine Abhängigkeitsprüfungen, um festzustellen, ob Tabellen oder Spalten gelöscht wurden
-
Obwohl die Leistung in Schleifen in Firebird 2.5 erheblich verbessert wurde, ist die Ausführung immer noch erheblich langsamer als wenn dieselben Anweisungen direkt gestartet werden
-
Rückgabewerte werden streng auf den Datentyp überprüft, um unvorhersehbare Ausnahmen für das Typcasting zu vermeiden. Beispielsweise würde die Zeichenfolge
'1234'
in eine Ganzzahl, 1234, konvertiert, aber'abc'
würde einen Konvertierungsfehler ergeben
Alles in allem sollte diese Funktion sehr vorsichtig verwendet werden und Sie sollten immer die Vorbehalte berücksichtigen. Wenn Sie das gleiche Ergebnis mit PSQL und / oder DSQL erzielen können, ist dies fast immer vorzuziehen.
7.6.11. FOR SELECT
Zeilenweises Durchlaufen einer abgefragten Ergebnismenge
PSQL
FOR <select_stmt> [AS CURSOR cursorname] DO <compound_statement>
Argument | Beschreibung |
---|---|
select_stmt |
|
cursorname |
Name des Cursors. Dieser muss eindeutig unter den Cursor-Namen im PSQL-Modul (gespeicherte Prozedur, Trigger oder PSQL-Block) sein |
single_statement |
Eine einzelne Anweisung, die mit einem Doppelpunkt abgeschlossen wird und die gesamte Verarbeitung für diese |
compound_statement |
Ein Anweisungsblock, der in |
Ein FOR SELECT
-Statement
-
ruft jede Zeile sequenziell aus der Ergebnismenge ab und führt die Anweisung oder den Anweisungsblock in der Zeile aus. In jeder Iteration der Schleife werden die Feldwerte der aktuellen Zeile in vordefinierte Variablen kopiert.
Mit der Klausel
AS CURSOR
können positionierte Löschungen und Aktualisierungen durchgeführt werden, siehe unten -
kann andere
FOR SELECT
-Anweisungen einbetten -
kann benannte Parameter enthalten, die zuvor in der
DECLARE VARIABLE
-Anweisung deklariert werden müssen, oder als Eingabe- oder Ausgabeparameter der Prozedur vorhanden sein -
erfordert eine
INTO
-Klausel, die sich am Ende derSELECT … FROM …
-Spezifikation befindet. In jeder Iteration der Schleife werden die Feldwerte in der aktuellen Zeile in die Liste der Variablen kopiert, die