Created
June 22, 2020 08:09
-
-
Save captainabap/cdd47caeee5de60f245eef177e5f4cfd to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
SELECT * FROM m_cs_tables | |
Listing 1.1: Meldung der SQL-Konsole beim Ausführen einer Anweisung | |
CREATE TABLE namen ( id INT, | |
last_name VARCHAR(30), | |
first_name VARCHAR(30) ); | |
INSERT INTO namen VALUES ( 1,'Brandeis','Jörg' ); | |
INSERT INTO namen VALUES ( 2, 'Müller', 'Peter' ); | |
SELECT * FROM namen; | |
DROP TABLE namen; | |
Listing 1.2: Beispiel für mehrere Anweisungen in der SQL-Konsole | |
1 IF lv_counter > 0 | |
2 THEN | |
3 INSERT INTO farben VALUES ('Violett'); | |
4 END IF; | |
Listing 2.1: IF-Anweisung enthält eine INSERT-Anweisung | |
SELECT col1,col2 FROM T1; | |
SELECT col1, | |
col2 | |
FROM T1 ; | |
Listing 2.2: Beispiel für unterschiedliche Formatierung | |
/* Das ist ein Blockkommentar */ | |
SELECT 'Jörg' AS string, | |
N'Jörg' AS unicode, | |
x'fff' AS binary, | |
-10 AS integer, | |
- 1.2345 AS decimal, | |
- 17.126E30 AS float, | |
0xff AS hex, | |
'2010-01-01' AS date_as_string, | |
DATE'2017-11-10' AS date, | |
'15:42:04.123' AS time_as_string, | |
TIME'15:42:04.123' AS time, | |
'2011-12-31 23:59:59' AS timestamp_string, | |
TIMESTAMP'2011-12-31 23:59:59' AS timestamp | |
FROM dummy; | |
Listing 2.3: Beispiele für Literale im Quelltext | |
SELECT id, | |
status, | |
titel | |
FROM aufgaben; | |
Listing 2.4: Beispiel für die einfache Notation | |
CREATE TABLE id_with_space("ID" int, " ID" int , "ID " int); | |
CREATE PROCEDURE get_name(IN id INT) | |
AS BEGIN | |
tmp = SELECT id, name, first_name FROM test; | |
SELECT * FROM :tmp WHERE id = :id; | |
END; | |
CALL get_name(1); | |
Listing 2.5: Zugriff auf lokale Felder und Parameter | |
Ergebnis = Zahl1 + Zahl2; | |
SELECT | |
"PURCHASEORDERID", | |
"PURCHASEORDERITEM", | |
"PRODUCT.PRODUCTID", | |
"CURRENCY", | |
case | |
when netamount <> 0 | |
--- Mit Klammerung: | |
--- then ((grossamount / netamount) * 100) - 100 | |
--- Ohne Klammerung: | |
then grossamount / netamount * 100 - 100 | |
else 0 | |
end as tax, | |
"GROSSAMOUNT", | |
"NETAMOUNT", | |
"TAXAMOUNT", | |
"QUANTITY", | |
"QUANTITYUNIT", | |
"DELIVERYDATE" | |
FROM "SAP_HANA_DEMO"."sap.hana.democontent.epm.data::PO.Item"; | |
Listing 2.6: Beispiel für Klammerung von Operatoren | |
SELECT | |
-- Feldname als Ausdruck | |
id, | |
-- Verkettungsoperation als Ausdruck | |
vorname || ' ' || nachname AS name, | |
-- CASE-Ausdruck ... | |
CASE | |
-- ... mit Funktionsaufruf als Ausdruck | |
WHEN geschlecht = 'F' THEN NCHAR('9792') | |
WHEN geschlecht = 'M' THEN NCHAR('9794') | |
ELSE '' | |
END AS MW, | |
-- Funktionsaufruf als Ausdruck | |
COALESCE(team, 0) AS team | |
FROM benutzer; | |
Listing 2.7: Ausdrücke in Feldlisten | |
DECLARE lv_count INTEGER; | |
lv_count = COALESCE((SELECT max(id) FROM aufgaben ), 0); | |
DO BEGIN | |
-- Typ einer DB-Tabelle | |
DECLARE lt_tab1 aufgaben; | |
-- Typ eines Tabellentyps | |
DECLARE lt_tab2 id_text; | |
-- Im Code mit TABLE definierter Tabellentyp | |
DECLARE lt_tab3 TABLE( id INT, | |
col1 NVARCHAR(12) ); | |
lt_tab1 = SELECT * FROM aufgaben; | |
lt_tab2 = SELECT id, titel AS text FROM :lt_tab1; | |
lt_tab3 = SELECT id, titel AS col1 FROM :lt_tab1; | |
SELECT * FROM :lt_tab1; | |
SELECT * FROM :lt_tab2; | |
SELECT * FROM :lt_tab3; | |
END; | |
Listing 2.8: Unterschiedliche Typisierung von Tabellen | |
CREATE TABLE test_null( | |
id INT, | |
name VARCHAR(10) | |
); | |
INSERT INTO test_null VALUES(1, 'Peter'); | |
INSERT INTO test_null VALUES(2, 'Paul'); | |
INSERT INTO test_null VALUES(3, 'Petra'); | |
INSERT INTO test_null VALUES(4, 'Andrea'); | |
INSERT INTO test_null(id) VALUES(5); | |
SELECT * FROM test_null WHERE name LIKE 'P%'; | |
SELECT * FROM test_null WHERE name NOT LIKE 'P%'; | |
DROP TABLE test_null; --Um die Tabelle wieder zu entfernen | |
Listing 2.9: Selektion auf eine Spalte mit NULL-Werten | |
SELECT * FROM test_null WHERE name NOT LIKE 'P%' | |
OR name IS NULL; | |
SELECT * FROM test_null WHERE name NOT LIKE 'P%' | |
OR name = NULL; | |
5 + NULL = NULL | |
NULL || 'Zeichenkette' = NULL | |
SELECT a.id, | |
COALESCE(b.nachname, | |
to_varchar(a.bearbeiter), | |
'' ) AS bearbeiter | |
FROM aufgaben AS a | |
LEFT OUTER JOIN benutzer AS b | |
ON a.bearbeiter = b.ID | |
Listing 2.10: Beispiel für die Verwendung der SQL-Funktion COALESCE | |
-- Test mit DUMMY | |
SELECT TO_DATS('2016-01-01') FROM dummy; | |
-- Der gleiche Test mit einem anonymen Block | |
DO (OUT rv_result NVARCHAR(10) =>?) | |
BEGIN | |
rv_result = TO_DATS('2016-12-31'); | |
END; | |
Listing 2.11: Die Tabelle DUMMY zum Testen von Ausdrücken | |
errorTab = SELECT '' AS ERROR_TEXT, | |
'' AS SQL__PROCEDURE__SOURCE__RECORD | |
FROM dummy | |
WHERE dummy = 'Y'; | |
Listing 2.12: Erzeugen einer leeren Tabelle mit Hilfe von DUMMY | |
SELECT a.id, | |
udf_benutzername(a.bearbeiter), | |
a.titel, | |
s.status_text | |
FROM udf_aufgaben_in_status(4) AS a | |
LEFT OUTER JOIN udf_statustexte('DE') AS s | |
ON a.STATUS = s.id; | |
Listing 2.13: SELECT-Abfrage mit UDFs | |
--Alleinstehender Block | |
BEGIN | |
<Block> | |
END; | |
--Prozedurdefinition | |
CREATE PROCEDURE <Prozedurname> <Parameterdefinition> | |
AS BEGIN | |
<Block> | |
END; | |
--Blöcke in einer IF/ELSE Bedingung | |
IF <Bedingung> | |
THEN | |
<Block1> | |
ELSE | |
<Block2> | |
END IF; | |
--Block in einer FOR-Schleife | |
FOR <Laufvariable> IN <Intervall> DO | |
<Block> | |
END FOR; | |
--Block in einer WHILE-Schleife | |
WHILE <Bedingung> DO | |
<Block> | |
END WHILE; | |
Listing 2.14: Beispiele für unterschiedliche Blöcke | |
BEGIN | |
[<Deklarationen>] | |
[<Ausnahmebehandler>] | |
[<Anweisungsliste>] | |
END; | |
Listing 2.15: Syntax von Blöcken | |
DO [(<Parameter>)] | |
BEGIN | |
[<Deklarationen>] | |
[<Ausnahmebehandler>] | |
[<Anweisungsliste>] | |
END; | |
Listing 2.16: Syntax von anonymen Blöcken | |
DO (IN iv_status INT => 1, | |
OUT ot_aufgaben TABLE ( id INT, | |
titel NVARCHAR(40) ) => ?) | |
BEGIN | |
ot_aufgaben = SELECT id, | |
titel | |
FROM aufgaben | |
WHERE status = :iv_status; | |
END; | |
Listing 2.17: Anonymer Block mit Parametern | |
CREATE [OR REPLACE] PROCEDURE <Prozedurname> | |
[(<Parameterliste>)] | |
[LANGUAGE {SQLSCRIPT|RLANG} ] | |
[SQL SECURITY {DEFINER|INVOKER} ] | |
[DEFAULT SCHEMA Defaultschema] | |
[READS SQL DATA ] | |
[WITH ENCRYPTION] | |
AS | |
BEGIN [SEQUENTIAL EXECUTION] | |
<Quellcode> | |
END | |
Listing 2.18: Bestandteile der Anweisung CREATE PROCEDURE | |
CREATE PROCEDURE <Prozedurname> | |
AS | |
BEGIN | |
<Quellcode> | |
END; | |
Listing 2.19: Pflichtteil der Anweisung CREATE PROCEDURE | |
CREATE PROCEDURE parameter_test ( | |
--Skalarer Datentyp als Eingabeparameter | |
IN iv_projekt INT, | |
--Datenbanktabelle als Typ für einen Ausgabeparameter | |
OUT ot_aufgaben aufgaben, | |
-- Ein Tabellentyp für einen Ausgabeparameter | |
OUT ot_status_text id_text, | |
-- Definition des Tabellentyps im Code: | |
OUT ot_restaufwand TABLE ( aufgabe INT, | |
restaufwand INT ) ) | |
AS | |
BEGIN | |
ot_aufgaben = SELECT * | |
FROM aufgaben | |
WHERE projekt = :iv_projekt; | |
ot_status_text = SELECT id, | |
status_text AS TEXT | |
FROM status_text; | |
ot_restaufwand = SELECT id AS aufgabe, | |
plan_aufwand - ist_aufwand | |
AS restaufwand | |
FROM :ot_aufgaben; | |
END; | |
CALL parameter_test(2, ?, ?, ?); | |
DROP PROCEDURE parameter_test; | |
Listing 2.20: Beispiel für das Anlegen, Ausführen und Löschen einer einfachen Prozedur | |
--Definition einer Prozedur mit Standardwerten | |
CREATE PROCEDURE standardwerte ( | |
IN iv_max_id INT DEFAULT 10, | |
it_table aufgaben DEFAULT aufgaben ) | |
AS | |
BEGIN | |
SELECT id, | |
titel | |
FROM :it_table | |
WHERE id <= :iv_max_id; | |
END; | |
--Aufruf der Prozedur ohne Parameter | |
CALL standardwerte(); | |
--Aufräumen | |
DROP PROCEDURE standardwerte; | |
Listing 2.21: Definition einer Prozedur mit Standardwerten für die Parameter | |
CREATE PROCEDURE add_column(IN it_any TABLE(...), | |
OUT ot_any TABLE(...)) | |
AS BEGIN | |
ot_any = SELECT *, | |
'New' || id AS new_column | |
FROM :it_any AS it; | |
END; | |
--Aufruf in der Konsole funktioniert: | |
CALL add_column(benutzer, ?); | |
--Aufruf in einer anderen Prozedur ist nicht erlaubt | |
CREATE PROCEDURE test_add_column | |
AS BEGIN | |
lt_users = SELECT * | |
FROM benutzer; | |
add_column( :lt_users, lt_tmp ); | |
SELECT * | |
FROM :lt_tmp; | |
END; | |
Listing 2.22: Beispiel für eine Prozedur mit dem Datentyp ANY TABLE | |
SAP DBTech JDBC: [7]: feature not supported: nested call on | |
procedure "SYSTEM"."ADD_COLUMN" has any table output parameter | |
CALL <Prozedurname> [(<Parameter>)] | |
DO BEGIN | |
CALL jbrandeis.statustexte(iv_sprache=>'DE', | |
et_result=> lt_statustexte) ; | |
END; | |
Listing 2.23: Interner Prozeduraufruf mit Angabe des Schemas | |
DO BEGIN | |
statustexte(iv_sprache=>'DE', | |
et_result=> lt_statustexte) ; | |
END; | |
Listing 2.24: Interner Prozeduraufruf ohne CALL | |
DO BEGIN | |
statustexte(iv_sprache=>'DE', | |
et_result=>lt_statustexte) ; | |
SELECT * FROM :lt_statustexte; | |
END; | |
Listing 2.25: Deklaration und Initialisierung einer neuen Tabellenvariable beim Prozeduraufruf | |
DO BEGIN | |
statustexte('DE',lt_statustexte) ; | |
SELECT * FROM :lt_statustexte; | |
END; | |
Listing 2.26: Prozeduraufruf über die Position | |
--Aufruf ohne Parameternamen | |
CALL parameter_test(2, ?, ?, ?); | |
--Der gleiche Aufruf mit benannten Parametern | |
CALL parameter_test(iv_projekt=>2, | |
ot_aufgaben=>?, | |
ot_status_text=>?, | |
ot_restaufwand=>?); | |
Listing 2.27: Parametrisierung aus der SQL-Konsole | |
CREATE PROCEDURE fibonacci(IN iv_value INT, | |
OUT rv_result INT) | |
AS BEGIN | |
DECLARE lv_tmp INT; | |
IF iv_value IN (1, 2) | |
THEN rv_result = 1; | |
ELSE | |
fibonacci( iv_value - 1 , rv_result ); | |
fibonacci( iv_value - 2 , lv_tmp ); | |
rv_result = rv_result + lv_tmp; | |
END IF; | |
END; | |
CALL fibonacci(20, ?); | |
Listing 2.28: Rekursive Berechnung von Fibonacci-Zahlen | |
CREATE FUNCTION <Funktionsname> | |
[(<IN-Parameterliste>)] | |
RETURNS <OUT-Parameterdefinition> | |
[LANGUAGE SQLSCRIPT] | |
[SQL SECURITY {DEFINER|INVOKER} ] | |
[DEFAULT SCHEMA Defaultschema] | |
[DETERMINISTIC] | |
AS | |
BEGIN | |
<Quellcode> | |
END | |
Listing 2.29: Syntax der CREATE FUNCTION-Anweisung | |
CREATE FUNCTION udf_name ( iv_vorname NVARCHAR(20), | |
iv_nachname NVARCHAR(20) ) | |
RETURNS rv_name NVARCHAR(42) | |
AS BEGIN | |
rv_name = :iv_nachname || ', ' || :iv_vorname; | |
END; | |
Listing 2.30: Beispiel für eine skalare UDF | |
CREATE FUNCTION udf_statustexte (iv_sprache VARCHAR(2)) | |
RETURNS TABLE ( id INT, | |
status_text VARCHAR(20) ) | |
AS BEGIN | |
RETURN SELECT id, | |
status_text | |
FROM status_text | |
WHERE sprache = :iv_sprache; | |
END; | |
Listing 2.31: Beispiel für eine Tabellen-UDF | |
DO BEGIN | |
DECLARE lv_benutzername NVARCHAR(42); | |
--Zuweisung einer skalaren UDFs zu skalaren Variablen | |
lv_benutzername = udf_name( 'Jörg', | |
'Brandeis' ); | |
SELECT lv_benutzername FROM dummy; | |
--Zuweisung einer Tabellen-UDF an eine Tabellenvariable | |
lt_statustexte = SELECT * FROM udf_statustexte('DE'); | |
SELECT * FROM :lt_statustexte; | |
END; | |
Listing 2.32: Zuweisung von UDF-Ergebnissen an lokale Variablen | |
CREATE FUNCTION jetzt | |
RETURNS uhrzeit TIME, | |
datum DATE | |
AS BEGIN | |
datum = CURRENT_DATE; | |
uhrzeit = CURRENT_TIME; | |
END; | |
SELECT jetzt().datum, | |
jetzt().uhrzeit | |
FROM dummy; | |
Listing 2.33: UDF mit mehreren Rückgabewerten | |
DO BEGIN | |
DECLARE lv_uhrzeit TIME; | |
DECLARE lv_datum DATE; | |
(lv_uhrzeit, lv_datum) = jetzt(); | |
SELECT lv_datum, lv_uhrzeit FROM dummy; | |
END; | |
Listing 2.34: Gleichzeitige Zuweisung aller Rückgabewerte einer UDF | |
CREATE LIBRARY testlib | |
AS BEGIN | |
PUBLIC VARIABLE cv_42 CONSTANT INTEGER DEFAULT 42; | |
PUBLIC PROCEDURE proc1 (OUT ov_result INTEGER) | |
AS BEGIN | |
ov_result = :cv_42; | |
END; | |
PUBLIC PROCEDURE call_proc1(OUT ov_result INTEGER) | |
AS BEGIN | |
CALL proc1(ov_result=>ov_result); | |
END; | |
END; | |
CALL testlib:call_proc1(?); | |
SELECT testlib:cv_42 FROM DUMMY; | |
DO BEGIN | |
USING testlib AS lib; --Definition des ALIAS | |
SELECT lib:cv_42 FROM DUMMY; | |
END; | |
Listing 2.35: Zugriff auf die Komponenten einer UDL | |
CREATE LIBRARY const | |
AS BEGIN | |
PUBLIC VARIABLE cv_max CONSTANT INTEGER DEFAULT 10; | |
PUBLIC VARIABLE cv_min CONSTANT INTEGER DEFAULT 3; | |
END; | |
Listing 2.36: Definition einer UDL mit Konstanten | |
SELECT * | |
FROM aufgaben | |
WHERE id < const:cv_max | |
AND id > const:cv_min; | |
Listing 2.37: Nutzung der Konstanten aus dem Listing 2.36 | |
CREATE FUNCTION udf_versandpreis ( | |
iv_laenge INT, | |
iv_breite INT, | |
iv_hoehe INT, | |
iv_gewicht INT, | |
iv_als_paeckchen VARCHAR(1), | |
iv_ist_online VARCHAR(1) | |
) | |
RETURNS rv_preis DEC(17,2), | |
rv_meldung NVARCHAR(30) | |
AS | |
BEGIN | |
--Ermitteln der Größenkategorie | |
--Ermitteln des Gurtmass | |
--Ermitteln der Preisklasse | |
--Ableiten des Preises anhand der Preisklasse | |
END; | |
Listing 2.38: Rahmen der Funktion UDF_VERSANDPREIS mit Pseudocode | |
CREATE FUNCTION udf_versandpreis ( | |
iv_laenge INT, | |
iv_breite INT, | |
iv_hoehe INT, | |
iv_gewicht INT, | |
iv_als_paeckchen VARCHAR(1), | |
iv_ist_online VARCHAR(1) | |
) | |
RETURNS rv_preis DEC(17,2), | |
rv_meldung NVARCHAR(30) | |
AS | |
BEGIN | |
DECLARE lv_groessenkategorie INT; | |
DECLARE lv_preisklasse VARCHAR(3); | |
DECLARE lv_gurtmass INT; | |
--Ermitteln der Größenkategorie | |
lv_groessenkategorie = udf_groessenkategorie(:iv_laenge, | |
:iv_breite, | |
:iv_hoehe); | |
--Ermitteln des Gurtmass | |
lv_gurtmass = udf_gurtmass(:iv_laenge, | |
:iv_breite, | |
:iv_hoehe); | |
--Ermitteln der Preisklasse | |
call pr_preisklasse(:lv_groessenkategorie, | |
:iv_gewicht, | |
:lv_gurtmass, | |
:iv_als_paeckchen, | |
lv_preisklasse, | |
rv_meldung); | |
--Ableiten des Preises anhand der Preisklasse | |
rv_preis = udf_preis(:lv_preisklasse, | |
:iv_ist_online); | |
END; | |
Listing 2.39: Ermittlung des Versandpreis per UDF | |
CREATE FUNCTION udf_groessenkategorie ( | |
iv_laenge INT, | |
iv_breite INT, | |
iv_hoehe INT ) | |
RETURNS rv_result INT | |
AS | |
BEGIN | |
SELECT CASE | |
WHEN :iv_laenge <= 30 | |
AND :iv_breite <= 30 | |
AND :iv_hoehe <= 15 | |
THEN 1 | |
WHEN :iv_laenge <= 60 | |
AND :iv_breite <= 30 | |
AND :iv_hoehe <= 15 | |
THEN 2 | |
WHEN :iv_laenge <= 120 | |
AND :iv_breite <= 60 | |
AND :iv_hoehe <= 60 | |
THEN 3 | |
ELSE 4 | |
END | |
INTO rv_result | |
FROM dummy; | |
END; | |
Listing 2.40: Ermittlung der Größenkategorie für die Pakete | |
SELECT udf_groessenkategorie(30, 30, 10) FROM dummy; | |
SELECT udf_groessenkategorie(35, 30, 15) FROM dummy; | |
SELECT udf_groessenkategorie(30, 30, 30) FROM dummy; | |
SELECT udf_groessenkategorie(121, 30, 30) FROM dummy; | |
Listing 2.41: Testfälle für die Funktion UDF_GROESSENKATEGORIE | |
CREATE FUNCTION udf_gurtmass ( iv_laenge INT, | |
iv_breite INT, | |
iv_hoehe INT ) | |
RETURNS rv_result INT | |
AS | |
BEGIN | |
rv_result = :iv_laenge | |
+ (2 * :iv_breite) | |
+ (2 * :iv_hoehe); | |
END; | |
Listing 2.42: Funktion für die Berechnung des Gurtmaßes | |
CREATE PROCEDURE pr_preisklasse ( | |
iv_groessenkategorie INT, | |
iv_gewicht INT, | |
iv_gurtmass INT, | |
iv_ist_paeckchen VARCHAR(1), | |
OUT ov_preisklasse VARCHAR(3), | |
OUT ov_meldung NVARCHAR(30) | |
) READS SQL DATA | |
AS | |
BEGIN | |
SELECT CASE | |
WHEN iv_gurtmass > 300 | |
THEN 'PT4' | |
WHEN iv_groessenkategorie <= 1 | |
AND iv_gewicht <= 1000 | |
AND iv_ist_paeckchen = 'X' | |
THEN 'PN1' | |
WHEN iv_groessenkategorie <= 2 | |
AND iv_gewicht <= 2000 | |
AND iv_ist_paeckchen = 'X' | |
THEN 'PN2' | |
WHEN iv_groessenkategorie <= 2 | |
AND iv_gewicht <= 2000 | |
THEN 'PT1' | |
WHEN iv_groessenkategorie <= 3 | |
AND iv_gewicht <= 5000 | |
THEN 'PT2' | |
WHEN iv_groessenkategorie <= 3 | |
AND iv_gewicht <= 10000 | |
THEN 'PT3' | |
WHEN iv_groessenkategorie <= 3 | |
AND iv_gewicht <= 31500 | |
THEN 'PT4' | |
ELSE 'NA' | |
END | |
INTO ov_preisklasse | |
FROM dummy; | |
SELECT CASE | |
WHEN iv_groessenkategorie = 4 | |
THEN 'Abmessungen zu groß' | |
WHEN iv_gewicht > 31500 | |
THEN 'Gewicht zu hoch' | |
WHEN iv_ist_paeckchen = 'X' | |
AND :ov_preisklasse IN ('PT1','PT2','PT3','PT4') | |
THEN 'Nicht als Päckchen möglich!' | |
ELSE '' | |
END | |
INTO ov_meldung | |
FROM dummy; | |
IF ov_meldung <> '' | |
THEN | |
ov_preisklasse = 'NA'; | |
END IF; | |
END; | |
Listing 2.43: Ableitung der Preisklasse und der Meldungen | |
CREATE FUNCTION udf_preis ( iv_preisklasse VARCHAR(3), | |
iv_ist_online VARCHAR(1) ) | |
RETURNS rv_preis DEC(17, 2) | |
AS BEGIN | |
SELECT CASE :iv_ist_online | |
WHEN 'X' THEN CASE :iv_preisklasse | |
WHEN 'PN1' THEN 3.89 | |
WHEN 'PN2' THEN 4.39 | |
WHEN 'PT1' THEN 4.99 | |
WHEN 'PT2' THEN 5.99 | |
WHEN 'PT3' THEN 8.49 | |
WHEN 'PT4' THEN 16.49 | |
ELSE 2 | |
END | |
ELSE --Filiale | |
CASE :iv_preisklasse | |
WHEN 'PN1' THEN 4 | |
WHEN 'PN2' THEN 4.5 | |
WHEN 'PT1' THEN 6.99 | |
WHEN 'PT2' THEN 6.99 | |
WHEN 'PT3' THEN 9.49 | |
WHEN 'PT4' THEN 16.49 | |
ELSE 2 | |
END | |
END | |
INTO rv_preis | |
FROM dummy; | |
END; | |
Listing 2.44: Preisermittlung anhand der Preisklasse und dem Online-Flag | |
SELECT udf_versandpreis(30,30,30,3000,'','X') | |
FROM dummy; | |
CREATE TABLE test_versandpreise ( | |
laenge INT, | |
breite INT, | |
hoehe INT, | |
gewicht INT, | |
als_paeckchen VARCHAR(1), | |
ist_online VARCHAR(1), | |
erwarteter_preis DEC(17, 2) | |
); | |
Listing 2.45: Tabelle für Testdaten | |
INSERT INTO test_versandpreise VALUES | |
(30,30,15,1000,'X','X',3.89); | |
INSERT INTO test_versandpreise VALUES | |
(30,30,15,1000,'X','',4); | |
INSERT INTO test_versandpreise VALUES | |
(60,30,15,1000,'X','X',4.39); | |
INSERT INTO test_versandpreise VALUES | |
(60,30,15,1000,'X','',4.5); | |
INSERT INTO test_versandpreise VALUES | |
(60,30,15,2000,'','X',4.99); | |
INSERT INTO test_versandpreise VALUES | |
(60,30,15,2000,'','',6.99); | |
INSERT INTO test_versandpreise VALUES | |
(61,30,15,2000,'','X',5.99); | |
INSERT INTO test_versandpreise VALUES | |
(60,30,15,10001,'','X',16.49); | |
INSERT INTO test_versandpreise VALUES | |
(120,60,60,2000,'','X',16.49); | |
INSERT INTO test_versandpreise VALUES | |
(60,30,15,2000,'','X',4.99); | |
INSERT INTO test_versandpreise VALUES | |
(61,30,15,2000,'','',6.99); | |
INSERT INTO test_versandpreise VALUES | |
(60,30,15,10001,'','',16.49); | |
INSERT INTO test_versandpreise VALUES | |
(120,60,60,2000,'','',16.49); | |
INSERT INTO test_versandpreise VALUES | |
(60,30,15,10000,'','',9.49); | |
INSERT INTO test_versandpreise VALUES | |
(60,30,15,40000,'','',0); | |
INSERT INTO test_versandpreise VALUES | |
(60,30,15,3000,'X','X',0); | |
INSERT INTO test_versandpreise VALUES | |
(121,60,60,2000,'','',0); | |
Listing 2.46: Testdaten für die Versandpreise | |
SELECT | |
erwarteter_Preis, | |
udf_versandpreis(laenge, | |
breite, | |
hoehe, | |
gewicht, | |
als_paeckchen, | |
ist_online).rv_preis | |
- erwarteter_preis AS abweichung, | |
udf_versandpreis(laenge, | |
breite, | |
hoehe, | |
gewicht, | |
als_paeckchen, | |
ist_online).rv_meldung AS meldung | |
FROM TEST_VERSANDPREISE | |
Listing 2.47: Ausführung des Tests | |
DO BEGIN | |
--Ableitung des Tabellentyps aus dem Parameter der Prozedur | |
CALL statustexte(iv_sprache => 'DE', | |
et_result => lt_statustext); | |
--Ableitung des Tabellentyps aus der Rückgabestruktur der | |
--SELECT-Abfrage | |
lt_status = SELECT * FROM STATUS; | |
SELECT * FROM :lt_statustext; | |
SELECT * FROM :lt_status; | |
END; | |
Listing 3.1: Implizite Typisierung von Tabellenvariablen | |
DECLARE <Variablenname> | |
[CONSTANT] | |
TABLE( <Spaltenname> <Typ> [,...] )|<Tabellentyp> | |
[{ DEFAULT | = } <Initialwert> ] | |
Listing 3.2: Syntax der Deklaration von Tabellenvariablen | |
lt_var2 = :lt_var1; --Nicht erlaubt | |
lt_var2 = SELECT * FROM :lt_var1; | |
SELECT <SELECT-Klausel> | |
FROM-Klausel | |
[WHERE <WHERE-Bedingung>] | |
[GROUP BY <Gruppierung> | |
[HAVING <HAVING-Bedingung>]] | |
[ORDER BY <Sortierung>] | |
[LIMIT <Begrenzung>] | |
Listing 3.3: Syntax der SELECT-Abfrage | |
SELECT * FROM procedures LIMIT 5; | |
SELECT * FROM procedures LIMIT 10 OFFSET 5; | |
SELECT DISTINCT country, city | |
FROM "sap.hana.democontent.epm.data::MD.Addresses"; | |
SELECT Feld1, Feld2, Feld3, ... | |
SELECT '1' AS counter, | |
"NAME.FIRST" AS vorname, | |
"NAME.LAST" AS nachname | |
FROM "sap.hana.democontent.epm.data::MD.Employees"; | |
Listing 3.4: Beispiel für die Verwendung von Aliasnamen für Spalten | |
SELECT tab.table_name, --Spalte table_name nicht eindeutig! | |
col.column_name | |
FROM m_cs_tables AS tab | |
INNER JOIN m_cs_columns AS col | |
ON tab.table_name = col.table_name; | |
Listing 3.5: Beispiel für die Verwendung des Korrelationsnamens in der Feldliste | |
SELECT t1.* , | |
t2.spalte1 | |
FROM tabelle1 AS t1 | |
JOIN tabelle2 AS t2 | |
ON t1.key = t2.key; | |
Listing 3.6: Beispiel für *-Sternchen in Feldlisten | |
SELECT id || ' - ' || titel AS Aufgabe, | |
plan_aufwand, | |
ist_aufwand, | |
round(ist_aufwand / plan_aufwand * 100, 0) | |
|| ' %' AS zeitverbrauch, | |
fertigstellung || ' %' AS fertigstellung | |
FROM aufgaben; | |
Listing 3.7: Operatorausdrücke in der Feldliste der SELECT-Anweisung | |
CASE <Ausdruck> | |
WHEN <Ausdruck1> THEN <Ergebnis1> | |
[WHEN <Ausdruck2> THEN <Ergebnis2>] | |
(...) | |
[ELSE <Ergebnis>] | |
END; | |
Listing 3.8: Syntax des einfachen CASE-Ausdrucks | |
CASE abteilung | |
WHEN 'IT' THEN 'EDV Abteilung' | |
WHEN 'MA' THEN 'Marketing' | |
WHEN 'VT' THEN 'Vertrieb' | |
ELSE abteilung | |
END AS "Abteilung" | |
Listing 3.9: Beispiel für einen einfachen CASE-Ausdruck | |
CASE WHEN <Bedingung1> THEN <Ergebnis1> | |
[WHEN <Bedingung2> THEN <Ergebnis2> | |
...] | |
[ELSE <Ergebnis>] | |
END | |
Listing 3.10: Syntax des komplexen CASE-Ausdrucks | |
lt_intab_with_flags = | |
SELECT *, | |
CASE WHEN stocktype IN ( 'F', 'H', 'W' ) | |
THEN 1 | |
ELSE 0 | |
END AS is_transit_stock, | |
CASE WHEN stocktype IN ( 'D', 'I', 'J' ) AND | |
stockcat NOT IN ( 'K', 'R' ) | |
THEN 1 | |
ELSE 0 | |
END AS is_blocked_stock, | |
CASE WHEN stockcat = '' OR | |
( stockcat IN ( 'E', 'Q' ) AND | |
indspecstk IN ( 'A', 'M' ) ) | |
THEN 1 | |
ELSE 0 | |
END AS is_valued_stock, | |
CASE WHEN processkey IN ( '000', '001', '002', | |
'003', '004', '005', | |
'006', '007', '010' ) | |
THEN 1 | |
ELSE 0 | |
END AS is_processkey_000_010, | |
CASE WHEN processkey IN ( '000', '001', '002', | |
'003', '004', '005', | |
'006', '007', '010', | |
'050', '051', '052' ) | |
THEN 1 | |
ELSE 0 | |
END AS is_processkey_000_052, | |
CASE WHEN processkey IN ( '100', '101', '102', | |
'103', '104', '105', | |
'106', '107', '110' ) | |
THEN 1 | |
ELSE 0 | |
END AS is_processkey_100_110, | |
CASE WHEN processkey IN ( '100', '101', '102', | |
'103', '104', '105', | |
'106', '107', '110', | |
'150', '151', '152' ) | |
THEN 1 | |
ELSE 0 | |
END AS is_processkey_100_152, | |
CASE WHEN stockrelev = 1 | |
THEN 1 | |
ELSE 0 | |
END AS is_stockrelev | |
FROM :intab; | |
Listing 3.11: Komplexe CASE-Ausdrücke zum Prüfen von unterschiedlichen Bedingungen | |
CASE WHEN wert2<>0 THEN wert1/wert2 | |
ELSE 0 | |
END AS division | |
Listing 3.12: CASE als Ersatz für eine IF-Bedingung | |
SELECT get_parcel_price(width, height, depth, weight ) | |
FROM parcels; | |
Listing 3.13: Aufruf einer skalaren UDF in der Feldliste | |
DO BEGIN | |
--Konstante 1 als Spaltenwert für COUNTER | |
lt_tmp = SELECT 1 AS counter, | |
plan_aufwand - ist_aufwand AS restaufwand | |
FROM aufgaben | |
WHERE status NOT IN (5, 6 ); | |
--Aggregation über die Spalten | |
SELECT SUM(counter), | |
SUM(restaufwand) | |
FROM :lt_tmp; | |
END; | |
Listing 3.14: Beispiel für die Verwendung von konstanten Werten und Aggregatfunktionen | |
SELECT SUM(ist_aufwand) | |
FROM aufgaben | |
WHERE projekt = 1; | |
Listing 3.15: Berechnung des Aufwands für ein Projekt | |
SELECT projekt, | |
SUM(ist_aufwand) | |
FROM aufgaben | |
GROUP BY projekt; | |
Listing 3.16: Berechnung des Aufwands für alle Projekte | |
SELECT projekt, | |
bearbeiter, | |
SUM(ist_aufwand) | |
FROM aufgaben | |
GROUP BY projekt, | |
bearbeiter | |
ORDER BY projekt, | |
bearbeiter; | |
Listing 3.17: Aggregation mit Gruppierung über zwei Spalten | |
SELECT sprache, | |
STRING_AGG(team_text, ', ') | |
FROM team_text | |
GROUP BY sprache; | |
Listing 3.18: Verkettung der Teamnamen mit der Aggregatfunktion STRING_AGG | |
SELECT STRING_AGG(team_text.team_text || '(' | |
|| cnt | |
|| ')', ', ') | |
FROM ( | |
SELECT team, | |
COUNT(*) AS cnt | |
FROM benutzer | |
GROUP BY team | |
) AS b | |
INNER JOIN team_text | |
ON team_text.id = b.team | |
WHERE sprache = 'DE' | |
Listing 3.19: Erweiterung des Beispiels um die Anzahl der Benutzer | |
SELECT | |
schema_name, | |
(SELECT | |
COUNT(*) | |
FROM tables | |
WHERE schema_name=schemas.schema_name) | |
AS table_count, | |
(SELECT | |
COUNT(*) | |
FROM procedures | |
WHERE schema_name=schemas.schema_name) | |
AS procedure_count, | |
schema_owner | |
FROM schemas; | |
Listing 3.20: Skalare Unterabfragen zur Ermittlung eines Feldes | |
--Aggregatfunktion COUNT | |
SELECT status, | |
COUNT(*) AS zaehler | |
FROM aufgaben | |
GROUP BY status | |
ORDER BY status; | |
--Window Function COUNT | |
SELECT id, | |
status, | |
COUNT(*) OVER (PARTITION BY status) AS zaehler | |
FROM aufgaben | |
ORDER BY id; | |
Listing 3.21: Vergleich zwischen COUNT als Aggregatausdruck und als Window Function | |
<Window Function> OVER ( | |
[PARTITION <Gruppierung>] | |
[ORDER BY <Sortierung>] | |
[ROWS <Ausschnitt>] ) | |
Listing 3.22: Syntax für Window Function | |
SELECT id, | |
status, | |
COUNT(*) OVER (PARTITION BY status ORDER BY id) AS zaehler | |
FROM aufgaben | |
ORDER BY status; | |
Listing 3.23: ORDER BY-Klausel in Window Functions | |
...ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING | |
SELECT id, | |
projekt, | |
LEAD(id) OVER ( PARTITION BY projekt | |
ORDER BY id ) AS naechste_aufgabe | |
FROM aufgaben | |
ORDER BY id; | |
Listing 3.24: Beispiel mit der Window Function LEAD | |
SELECT ... FROM <Tabellenausdruck> | |
SELECT * FROM viewname(wert1, wert2); | |
SELECT "S1", "in_out" | |
FROM "system-local.private.jbrandeis/zjb_colview" | |
(placeholder."$$IP_STATUS$$"=>'a'); | |
01 CREATE PROCEDURE test_anonymous_function | |
02 (IN iv_max INT, | |
03 OUT ot_result TABLE(number INTEGER, | |
04 letter VARCHAR ) ) | |
05 AS BEGIN | |
06 ot_result = | |
07 SELECT number, | |
08 letter | |
09 | |
10 FROM | |
11 SQL FUNCTION (IN iv_a INT => :iv_max) | |
12 RETURNS TABLE (number INT, | |
13 letter VARCHAR(1)) | |
14 BEGIN | |
15 DECLARE lv_cnt INT; | |
16 DECLARE lv_chars VARCHAR(30) | |
17 DEFAULT 'ABCDEFGHIJKLMNOP'; | |
18 | |
19 lt_result = SELECT 0 AS number, | |
20 ' ' AS letter | |
21 FROM dummy | |
22 WHERE dummy <> 'X'; | |
23 | |
24 FOR lv_cnt IN 1..:iv_a DO | |
25 | |
26 lt_result = SELECT * FROM :lt_result | |
27 UNION | |
28 SELECT lv_cnt AS number, | |
29 SUBSTRING(:lv_chars, | |
30 :lv_cnt, | |
31 1) AS letter | |
32 FROM dummy; | |
33 | |
34 END FOR; | |
35 | |
36 RETURN SELECT * FROM :lt_result; | |
37 END | |
38 WHERE MOD(number, 2) = 0 ; | |
39 END; | |
40 | |
41 CALL test_anonymous_function(13, ?); | |
Listing 3.25: Eingebettete Funktion in einer SQL-Abfrage | |
SELECT ... | |
FROM <Tabellenausdruck1> CROSS JOIN <Tabellenausdruck2> | |
SELECT ... | |
FROM <Tabellenausdruck1> , <Tabellenausdruck2> | |
Listing 3.26: Syntax des Cross Join | |
--Kreuzprodukt mit CROSS JOIN | |
SELECT * | |
FROM farben | |
CROSS JOIN groessen; | |
--Kreuzprodukt nur mit Komma in der FROM-Klausel | |
SELECT * | |
FROM farben, | |
groessen; | |
Listing 3.27: Cross Join über Farben und Größen | |
SELECT ... | |
FROM <Tabellenausdruck1> | |
[INNER] JOIN <Tabellenausdruck2> | |
ON <Join-Bedingung> | |
Listing 3.28: Syntax des INNER JOIN | |
CREATE TABLE belege ( belegnr INT PRIMARY KEY, | |
betrag DEC(17,2), | |
waehrung VARCHAR(3) ) ; | |
CREATE TABLE waehrungen ( waehrung VARCHAR(3) PRIMARY KEY, | |
text NVARCHAR(20) ) ; | |
INSERT INTO belege VALUES(1,34.34,'EUR') ; | |
INSERT INTO belege VALUES(2,12.53,'EUR') ; | |
INSERT INTO belege VALUES(3,234.12,'ALL'); | |
INSERT INTO belege VALUES(4,45.12,'USD') ; | |
INSERT INTO waehrungen VALUES('EUR','Europäischer Euro') ; | |
INSERT INTO waehrungen VALUES('USD','US-Dollar') ; | |
INSERT INTO waehrungen VALUES('GBP','Britisches Pfund') ; | |
SELECT * | |
FROM belege | |
INNER JOIN waehrungen | |
ON belege.waehrung = waehrungen.waehrung; | |
DROP TABLE belege; | |
DROP TABLE waehrungen; | |
Listing 3.29: Beispiel für einen Inner Join | |
SELECT * FROM belege | |
LEFT OUTER JOIN waehrungen | |
ON belege.waehrung = waehrungen.waehrung | |
where waehrungen.waehrung <> 'EUR'; | |
SELECT * FROM belege | |
LEFT OUTER JOIN waehrungen | |
ON belege.waehrung = waehrungen.waehrung | |
and waehrungen.waehrung <> 'EUR'; | |
Listing 3.30: Beispiel für die Position von Bedingungen | |
SELECT b.id as bearbeiter, | |
vorname, | |
nachname, | |
cnt | |
FROM benutzer AS b | |
CROSS JOIN LATERAL (SELECT COUNT(*) AS cnt, | |
bearbeiter | |
FROM aufgaben | |
WHERE bearbeiter = b.id | |
GROUP BY bearbeiter ) | |
Listing 3.31: Beispiel für einen Lateral Join | |
ON 1 = 1; | |
--1. Alle Aufgaben mit einem Status kleiner/gleich 3 | |
SELECT * | |
FROM aufgaben | |
WHERE status <= 3; | |
--2. Alle Aufgaben, die im Status 1, 2 oder 4 sind | |
SELECT * | |
FROM aufgaben | |
WHERE status = ANY ( 1, 2, 4 ); | |
--3. Alle Aufgaben, die in einem finalen Status sind | |
SELECT * | |
FROM aufgaben | |
WHERE status = ANY ( SELECT id | |
FROM status | |
WHERE is_final = true ); | |
--4. Alle Aufgaben, die nicht in einem finalen Status sind | |
SELECT * | |
FROM aufgaben | |
WHERE status <> ALL ( SELECT id | |
FROM status | |
WHERE is_final = true ); | |
Listing 3.32: Beispiele für die Verwendung von Vergleichsprädikaten | |
--1. Negierung des ganzen Prädikats | |
SELECT count(*) | |
FROM aufgaben | |
WHERE NOT ( titel LIKE '%Aliquam%' ); | |
--2. Äquivalente Abfrage mit NOT LIKE | |
SELECT count(*) | |
FROM aufgaben | |
WHERE titel NOT LIKE '%Aliquam%'; | |
Listing 3.33: Verwendung von NOT LIKE | |
SELECT table_name | |
FROM tables | |
WHERE table_name LIKE 'P$_%' ESCAPE '$' | |
Listing 3.34: Vergleich mit LIKE und ESCAPE | |
--1. Vergleich mit BETWEEN | |
SELECT * | |
FROM benutzer | |
WHERE id BETWEEN 5 AND 10; | |
--2. Die selbe Abfrage mit Vergleichsprädikaten | |
SELECT * | |
FROM benutzer | |
WHERE id >= 5 | |
AND id <= 10; | |
Listing 3.35: Vergleich mit BETWEEN | |
--1. Abfrage für eine Spalte mit mehreren Werten mit OR | |
SELECT * | |
FROM benutzer | |
WHERE vorname = 'Aldo' | |
OR vorname = 'Elvin' | |
OR vorname = 'Sascha'; | |
--2. Analoge Abfrage mit dem IN-Prädikat | |
SELECT * | |
FROM benutzer | |
WHERE vorname IN ( 'Aldo', 'Elvin', 'Sascha' ); | |
--3. Nutzung einer Unterabfrage mit dem IN-Prädikat: | |
-- Benutzer, denen aktuell keine Aufgabe zugeordnet ist | |
SELECT * | |
FROM benutzer | |
WHERE id NOT IN ( SELECT DISTINCT bearbeiter | |
FROM aufgaben ); | |
Listing 3.36: Beispiele für die Nutzung des IN-Prädikat | |
--1. Abfrage mit EXISTS-Prädikat | |
-- Alle Benutzer mit mindestens einer Aufgabe | |
SELECT * | |
FROM benutzer | |
WHERE EXISTS ( | |
SELECT bearbeiter | |
FROM aufgaben | |
WHERE aufgaben.bearbeiter = benutzer.id ); | |
--2. Abfrage mit NOT EXISTS | |
-- Aufgaben, deren Status nicht in der Statustabelle steht | |
SELECT * | |
FROM aufgaben | |
WHERE NOT EXISTS ( | |
SELECT id | |
FROM status | |
WHERE status.id = aufgaben.status ); | |
--3. Analoge Abfrage mit NOT IN | |
SELECT * | |
FROM aufgaben | |
WHERE status NOT IN ( | |
SELECT id | |
FROM status ); | |
Listing 3.37: Beispiele für das EXISTS-Prädikat | |
CREATE TABLE t_fiscper (fiscper3 VARCHAR(3)); | |
INSERT INTO t_fiscper VALUES ('000'); | |
INSERT INTO t_fiscper VALUES ('001'); | |
INSERT INTO t_fiscper VALUES ('002'); | |
INSERT INTO t_fiscper VALUES ('003'); | |
CREATE PROCEDURE where_clause (IN iv_fiscper3 VARCHAR(3)) | |
AS | |
BEGIN | |
SELECT * | |
FROM t_fiscper | |
WHERE fiscper3 LIKE CASE :iv_fiscper3 | |
WHEN '' | |
THEN '%' | |
ELSE :iv_fiscper3 | |
END | |
AND fiscper3 NOT NULL; | |
END; | |
CALL where_clause(''); | |
CALL where_clause('000'); | |
CALL where_clause('001'); | |
DROP TABLE t_fiscper; | |
DROP PROCEDURE where_clause; | |
Listing 3.38: CASE-Ausdruck in einer WHERE-Klausel | |
--1. Prüfung mit dem NULL-Prädikat | |
SELECT * | |
FROM benutzer | |
WHERE vorname IS NULL; | |
--2. Achtung! Diese Abfrage liefert ein anderes Ergebnis! | |
SELECT * | |
FROM benutzer | |
WHERE vorname = NULL; --Falsch! Nicht verwenden! | |
Listing 3.39: Prüfung auf NULL | |
--1. Unscharfe Suche nach einer Zeichenkette | |
SELECT * | |
FROM benutzer | |
WHERE CONTAINS ( ( vorname, nachname, email ), | |
'andy', | |
FUZZY(0.3) ); | |
--2. Unscharfe Suche nach mehreren Zeichenketten | |
SELECT * | |
FROM benutzer | |
WHERE CONTAINS ( *, | |
'andy OR anette', | |
FUZZY(0.5) ); | |
Listing 3.40: Unscharfe Suche mit dem CONTAINS-Prädikat | |
WITH <Name> [(<Spaltenliste>)] AS (<Unterabfrage>){,...} | |
SELECT ... ; | |
--Zunächst wird eine benannte Abfrage erstellt | |
WITH zu_pruefende_aufgaben AS ( | |
SELECT a.id, | |
b.vorname || ' ' || b.nachname AS bearbeiter, | |
a.titel, | |
a.faelligkeit, | |
a.status, | |
a.plan_aufwand, | |
a.ist_aufwand | |
FROM aufgaben AS a | |
INNER JOIN benutzer AS b | |
ON a.bearbeiter = b.id | |
WHERE ( a.STATUS NOT IN ( 5, 6 ) ) ) | |
--Diese benannte Abfrage wird dann verwendet | |
SELECT TO_NVARCHAR('Überfällig') AS grund, | |
* | |
FROM zu_pruefende_aufgaben | |
WHERE faelligkeit < TO_DATE('2017-12-01') | |
UNION ALL | |
SELECT TO_NVARCHAR('Aufwandsschätzung prüfen') AS grund, | |
* | |
FROM zu_pruefende_aufgaben | |
WHERE plan_aufwand <= ist_aufwand * 1.1; | |
Listing 3.41: SELECT-Anweisung mit WITH-Klausel | |
DO BEGIN | |
--Definition der Tabellenvariablen über den SELECT | |
lt_zu_pruefende_aufgaben = SELECT a.id, | |
b.vorname || ' ' || b.nachname AS bearbeiter, | |
a.titel, | |
a.faelligkeit, | |
a.status, | |
a.plan_aufwand, | |
a.ist_aufwand | |
FROM aufgaben AS a | |
INNER JOIN benutzer AS b | |
ON a.bearbeiter = b.id | |
WHERE (a.status NOT IN (5, 6)); | |
--Verwendung der Tabellenvariablen in den beiden Abfragen | |
SELECT TO_NVARCHAR('Überfällig') AS grund, | |
* | |
FROM :lt_zu_pruefende_aufgaben | |
WHERE faelligkeit < TO_DATE('2017-12-01') | |
UNION ALL | |
SELECT TO_NVARCHAR('Aufwandsschätzung prüfen') AS grund, | |
* | |
FROM :lt_zu_pruefende_aufgaben | |
WHERE plan_aufwand <= ist_aufwand * 1.1; | |
END; | |
Listing 3.42: Verwendung von Tabellenvariablen statt der WITH-Klausel | |
SELECT CASE | |
WHEN faelligkeit < TO_DATE('2017-12-01') | |
AND status NOT IN ( 5, 6 ) | |
THEN 'zu pruefen' | |
ELSE 'OK' | |
END AS "Prüfen?", | |
count(*) AS cnt | |
FROM aufgaben | |
GROUP BY CASE | |
WHEN faelligkeit < TO_DATE('2017-12-01') | |
AND status NOT IN ( 5, 6 ) | |
THEN 'zu pruefen' | |
ELSE 'OK' | |
END; | |
Listing 3.43: Beispiel für einen CASE-Ausdruck in der GROUP BY-Klausel | |
DO BEGIN | |
--1. Neue Spalte mit CASE-Ausdruck erzeugen | |
lt_select = SELECT CASE | |
WHEN faelligkeit < TO_DATE('2017-12-01') | |
AND status NOT IN ( 5, 6 ) | |
THEN 'zu pruefen' | |
ELSE 'OK' | |
END AS "Prüfen?" | |
FROM aufgaben; | |
--2. Gruppierung nach der neuen Spalte | |
SELECT "Prüfen?", | |
count(*) AS cnt | |
FROM :lt_select | |
GROUP BY "Prüfen?"; | |
END | |
Listing 3.44: Trennung von CASE und GROUP_BY | |
DO BEGIN | |
lt_aggregation = SELECT bearbeiter, | |
status, | |
count(*) AS cnt | |
FROM aufgaben | |
GROUP BY GROUPING SETS( | |
( status, bearbeiter ), | |
( bearbeiter ) ) | |
ORDER BY bearbeiter, | |
status NULLS LAST; | |
SELECT b.Vorname || ' ' || b.nachname AS Bearbeiter, | |
coalesce(s.status_text, ' Summe') AS STATUS, | |
aggr.cnt | |
FROM :lt_aggregation AS aggr | |
LEFT JOIN benutzer AS b --Hinzulesen der Benutzerdaten | |
ON aggr.bearbeiter = b.id | |
LEFT JOIN status_text AS s --Hinzulesen der Statustexte | |
ON aggr.status = s.id | |
AND s.sprache = 'DE'; | |
END; | |
Listing 3.45: Verwendung von GROUPING SETS für die Bildung von Summenzeilen | |
SELECT ... GROUP BY ... HAVING <Bedingung> | |
SELECT bearbeiter, | |
AVG(plan_aufwand) | |
FROM aufgaben | |
GROUP BY bearbeiter | |
HAVING count(*) > 5; | |
Listing 3.46: Aggregation mit den Klauseln GROUP BY und HAVING | |
--1. Sortierung nach Spaltennamen | |
SELECT * | |
FROM aufgaben | |
ORDER BY plan_aufwand, | |
ist_aufwand; | |
--2. Sortierung nach Spaltennummern | |
SELECT * | |
FROM aufgaben | |
ORDER BY 7, | |
8; | |
--3. Sortierung nach einem Operatorausdruck | |
SELECT id, | |
titel | |
FROM aufgaben | |
ORDER BY plan_aufwand - ist_aufwand DESC; | |
Listing 3.47: Sortierung mit ORDER BY | |
<Abfrage1> <Mengenoperator> <Abfrage2> | |
--Abfrage mit UNION: Duplikate werden entfernt | |
SELECT 'A' AS spalte1, | |
'B' AS spalte2 | |
FROM dummy | |
UNION | |
SELECT 'A' AS spalte1, | |
'B' AS spalte2 | |
FROM dummy; | |
--Zweite Abfrage mit UNION ALL ohne Duplikatsentfernung | |
SELECT 'A' AS spalte1, | |
'B' AS spalte2 | |
FROM dummy | |
UNION ALL | |
SELECT 'A' AS spalte1, | |
'B' AS spalte2 | |
FROM dummy; | |
Listing 3.48: Verwendung von UNION und UNION ALL | |
--Abfrage aller Benutzer, die Projektleiter sind und eine | |
--Aufgabe zur Bearbeitung zugewiesen haben. | |
--Zunächst mit INTERSECT: | |
SELECT bearbeiter | |
FROM aufgaben | |
INTERSECT | |
SELECT projektleiter | |
FROM projekte; | |
--Und als INNER JOIN: | |
SELECT DISTINCT bearbeiter | |
FROM aufgaben | |
INNER JOIN projekte | |
ON bearbeiter = projektleiter; | |
Listing 3.49: Beispiel für die Verwendung von INTERSECT | |
--Abfrage aller Bearbeiter von Aufgaben, ohne Projektleiter | |
--Zunächst mit EXCEPT | |
SELECT bearbeiter | |
FROM aufgaben | |
EXCEPT | |
SELECT projektleiter | |
FROM projekte; | |
--Und die gleiche Abfrage mit NOT EXISTS | |
SELECT DISTINCT bearbeiter | |
FROM aufgaben AS a | |
WHERE NOT EXISTS ( | |
SELECT id | |
FROM projekte AS p | |
WHERE p.projektleiter = a.bearbeiter ); | |
Listing 3.50: Subtraktion von Mengen mit EXCEPT und NOT EXISTS | |
--1. Skalare Unterabfrage | |
SELECT * | |
FROM aufgaben | |
WHERE status = ( | |
SELECT max(id) | |
FROM status ); | |
--2. Spalten-Unterabfrage | |
SELECT * | |
FROM aufgaben | |
WHERE status IN ( | |
SELECT id | |
FROM status | |
WHERE is_final = true ); | |
--3. Tabellen-Unterabfrage | |
SELECT b.vorname || ' ' || b.Nachname AS PL, | |
p.titel | |
FROM ( | |
SELECT * | |
FROM projekte | |
WHERE titel LIKE 'F%' ) AS p | |
INNER JOIN benutzer AS b | |
ON p.projektleiter = b.id; | |
Listing 3.51: Beispiele für Unterabfragen | |
SELECT aufgaben.id AS aufgabe_id, | |
aufgaben.titel AS aufgabe_titel, | |
bearbeiter.id AS bearbeiter_id, | |
bearbeiter.vorname | |
|| ' ' | |
|| bearbeiter.nachname AS bearbeiter_name, | |
projektleiter.id AS projektleiter_id, | |
projektleiter.vorname | |
|| ' ' | |
|| projektleiter.nachname AS projektleiter_name | |
FROM aufgaben | |
INNER JOIN projekte | |
ON aufgaben.projekt = projekte.id | |
INNER JOIN benutzer AS projektleiter | |
ON projekte.projektleiter = projektleiter.id | |
INNER JOIN benutzer AS bearbeiter | |
ON aufgaben.bearbeiter = bearbeiter.id; | |
Listing 3.52: Verwendung von Spalten- und Tabellenalias | |
"SAP_HANA_DEMO"."sap.hana.democontent.epm.data::MD.Addresses" | |
--Anlegen der MAP-Funktion | |
CREATE FUNCTION map_table_row_count(IN iv_tabname nvarchar(30)) | |
RETURNS TABLE (tabname NVARCHAR(30), | |
rowcount INT ) | |
AS BEGIN | |
DECLARE lv_sql NVARCHAR(100); | |
DECLARE lt_result TABLE( tabname NVARCHAR(30), | |
rowcount INT ); | |
EXEC 'SELECT ''' | |
|| :iv_tabname | |
|| ''' AS tabname, ' | |
|| 'COUNT(*) as rowcount ' | |
|| 'FROM ' | |
|| :iv_tabname | |
INTO lt_result; | |
RETURN SELECT * FROM :lt_result; | |
END; | |
--Aufruf des MAP_MERGE Operators in einem Anonymen Block | |
DO BEGIN | |
lt_table = SELECT left(table_name,30) AS tabname | |
FROM m_cs_tables | |
WHERE schema_name = 'SYSTEM'; | |
lt_result = MAP_MERGE( :lt_table, | |
map_table_row_count( | |
:lt_table.tabname) ); | |
SELECT * FROM :lt_result; | |
END; | |
Listing 3.53: Verwendung des MAP_MERGE-Operators | |
MAP_REDUCE( <Eingabetabelle>, | |
<Map-Tabellenfunktion>, | |
<Reduce-Funktion oder -Prozedur>) | |
CREATE TABLE tab1 ( | |
row_nr INT, | |
col_alphanum ALPHANUM(4), | |
col_varchar VARCHAR(4), | |
col_nvarchar NVARCHAR(4) | |
); | |
INSERT INTO tab1 VALUES ( 1,' ' , ' ' , ' ' ); | |
INSERT INTO tab1 VALUES ( 2,' ' , ' ', ' ' ); | |
INSERT INTO tab1 VALUES ( 3,'abcd' , 'abcd', 'abcd' ); | |
INSERT INTO tab1 VALUES ( 4,'Jörg' , 'Jörg', 'Jörg' ); | |
INSERT INTO tab1 VALUES ( 5,'öö' , 'öö' , 'öö' ); | |
SELECT row_nr, | |
col_alphanum, | |
col_varchar, | |
col_nvarchar, | |
TO_NVARCHAR(col_varchar) AS decoded | |
FROM tab1 ; | |
DROP TABLE tab1; | |
Listing 4.1: Beispiel für das Verhalten des Datentyps VARCHAR | |
CREATE TABLE zeichenketten ( | |
row_nr INT, | |
col_alphanum ALPHANUM(4) ); | |
INSERT INTO zeichenketten VALUES ( 1,'12'); | |
INSERT INTO zeichenketten VALUES ( 2,'012'); | |
INSERT INTO zeichenketten VALUES ( 3,'0012'); | |
INSERT INTO zeichenketten VALUES ( 4,'2'); | |
INSERT INTO zeichenketten VALUES ( 5,'20'); | |
INSERT INTO zeichenketten VALUES ( 6,'2a'); | |
INSERT INTO zeichenketten VALUES ( 7,'a2'); | |
SELECT * | |
FROM zeichenketten | |
ORDER BY col_alphanum; | |
DROP TABLE zeichenketten; | |
Listing 4.2: Beispiel für das Verhalten des Datentyps ALPHANUM | |
SELECT LENGTH('') AS length_space, | |
LENGTH('Peter') AS length_peter, | |
LENGTH('Jörg') AS length_joerg, | |
LENGTH(' ') AS length_china, | |
LENGTH(TO_VARCHAR(' ')) AS lenght_china_vc | |
FROM dummy; | |
Listing 4.3: Ermittlung der Länge von Zeichenketten | |
CREATE column TABLE tab(col1 NVARCHAR(10) , | |
col2 NVARCHAR(10)) ; | |
INSERT INTO tab VALUES(' ' , 'A' ) ; | |
INSERT INTO tab VALUES('A ', ' B') ; | |
INSERT INTO tab VALUES(' A', 'B ') ; | |
SELECT '"' || col1 || col2 || '"' AS "|| Operator" , | |
CONCAT(CONCAT('"' , col1) , | |
CONCAT(col2, '"')) AS "CONCAT Function" | |
FROM tab; | |
DROP TABLE tab; | |
Listing 4.4: Test der Verkettung von Zeichenketten | |
SELECT ABAP_UPPER('Jörg') AS "ABAP_* Function" , | |
UPPER('Jörg') AS "UPPER/LOWER Function", | |
UCASE('Jörg') AS "U/L-CASE Function" | |
FROM dummy | |
UNION | |
SELECT ABAP_LOWER('Jörg') AS "ABAP_* Function" , | |
LOWER('Jörg') AS "UPPER/LOWER Function", | |
LCASE('Jörg') AS "U/L-CASE Function" | |
FROM dummy; | |
Listing 4.5: Funktionen für Groß- und Kleinschreibung | |
SELECT LEFT('ABCDEFGHI',3) AS links, -->'ABC' | |
SUBSTRING('ABCDEFGHI',4,3) AS mitte, -->'DEF' | |
RIGHT('ABCDEFGHI',3) AS rechts -->'GHI' | |
FROM | |
dummy; | |
Listing 4.6: Zerlegung von Zeichenketten mit Positionsangaben | |
SELECT SUBSTR_BEFORE('Raus aus dem Haus','aus') AS links , | |
SUBSTR_AFTER('Raus aus dem Haus','aus') AS rechts, | |
SUBSTR_AFTER('Raus aus dem Haus','') AS alles , | |
SUBSTR_AFTER('Raus aus dem Haus','ABC') AS nichts | |
FROM | |
dummy; | |
Listing 4.7: Zeichenkettenzerlegung mit SUBSTR_BEFORE und SUBSTR_AFTER | |
SUBSTR_REGEXPR( | |
<Muster> | |
[FLAG <flag>] | |
IN <Zeichenkette> | |
[FROM <Start>] | |
[OCCURENCE <N. auftreten>] | |
[GROUP <Gruppe>]) | |
Listing 4.8: SQL-Funktion SUBSTR_REGEXPR() | |
CREATE TABLE gesellschaften( firma NVARCHAR(30) ) ; | |
INSERT INTO gesellschaften VALUES('BASF SE') ; | |
INSERT INTO gesellschaften VALUES('SAP SE') ; | |
INSERT INTO gesellschaften VALUES('Bauer GmbH') ; | |
INSERT INTO gesellschaften VALUES('Siemens AG') ; | |
SELECT SUBSTR_REGEXPR('GmbH|AG|SE' IN firma) AS rechtsform, | |
firma | |
FROM gesellschaften; | |
DROP TABLE gesellschaften; | |
Listing 4.9: Einfache Zeichenkettenzerlegung mit regulären Ausdrücken | |
CREATE TABLE logtabelle (zeile NVARCHAR(100)) ; | |
INSERT INTO logtabelle VALUES | |
('Fehlende Berechtigungen für elbert@brandeis.de') ; | |
INSERT INTO logtabelle VALUES | |
('Login fehlgeschlagen: sibrylle@tiergarten-ma.com.') ; | |
INSERT INTO logtabelle VALUES | |
('Benutzer ele.fant@tiergarten-ma.de wurde angemeldet.') ; | |
INSERT INTO logtabelle VALUES | |
('Server neu gestartet.') ; | |
SELECT SUBSTR_REGEXPR( | |
'\b([A-Z0-9._%+-]+)@([A-Z0-9.-]+\.[A-Z]{2,})\b' | |
FLAG 'i' | |
IN zeile | |
GROUP 1 ) AS name, | |
SUBSTR_REGEXPR( | |
'\b([A-Z0-9._%+-]+)@([A-Z0-9.-]+\.[A-Z]{2,})\b' | |
FLAG 'i' | |
IN zeile | |
GROUP 2 ) AS host, | |
SUBSTR_REGEXPR( | |
'\b([A-Z0-9._%+-]+)@([A-Z0-9.-]+\.[A-Z]{2,})\b' | |
FLAG 'i' | |
IN zeile ) AS email, | |
zeile | |
FROM logtabelle; | |
DROP TABLE logtabelle; | |
Listing 4.10: Beispiel für die Extraktion von E-Mail-Adressen aus einer Log-Tabelle | |
LOCATE_REGEXPR( | |
[START|AFTER] | |
<Muster> | |
[FLAG <flag>] | |
IN <Zeichenkette> | |
[FROM <Start>] | |
[OCCURENCE <N. auftreten>] | |
[GROUP <Gruppe>] ) | |
Listing 4.11: Syntax der SQL-Funktion LOCATE_REGEXPR() | |
OCCURRENCES_REGEXPR( | |
<Muster> | |
[FLAG <flag>] | |
IN <Zeichenkette> | |
[FROM <Startposition>] ) | |
Listing 4.12: Syntax der SQL-Funktion OCCURRENCES_REGEXPR() | |
REPLACE_REGEXPR( | |
<Muster> | |
[FLAG <flag>] | |
IN <Zeichenkette> | |
[WITH <Ersetzung>] | |
[FROM <Start>] | |
[OCCURENCE <N. auftreten>] ) | |
Listing 4.13: Syntax der SQL-Funktion REPLACE_REGEXPR() | |
CREATE TABLE mitarbeiter (namen NVARCHAR(60)); | |
INSERT INTO mitarbeiter VALUES ('Joerg Brandeis'); | |
INSERT INTO mitarbeiter VALUES ('Peter Mueller'); | |
INSERT INTO mitarbeiter VALUES ('Michael Maier'); | |
INSERT INTO mitarbeiter VALUES ('Superman'); | |
SELECT REPLACE_REGEXPR( | |
'([[:graph:]]+)[[:blank:]]+([[:graph:]]+)' IN namen | |
WITH 'Nachname: \2 Vorname: \1') | |
FROM mitarbeiter; | |
DROP TABLE mitarbeiter; | |
Listing 4.14: Beispiel für Suchen und Ersetzen mit regulären Ausdrücken | |
SELECT 'LPAD(''ABC'',8,''anfang'')' AS "Funktion", | |
LPAD('ABC',8,'anfang') AS "Ergebnis" | |
FROM dummy | |
UNION | |
SELECT 'RPAD(''ABC'',8,''ende'')' AS "Funktion", | |
RPAD('ABC',8,'ende') AS "Ergebnis" | |
FROM dummy; | |
Listing 4.15: Beispiel für die PAD-Funktionen | |
SELECT | |
TRIM(both 'HIA' FROM 'ABCDEFGHI') AS trim_both, | |
TRIM(leading 'HIA' FROM 'ABCDEFGHI') AS trim_leading, | |
TRIM(trailing 'HIA' FROM 'ABCDEFGHI') AS trim_trailing, | |
LTRIM('ABCDEFGHI','HIA') AS "LTRIM()", | |
RTRIM('ABCDEFGHI','HIA') AS "RTRIM()" | |
FROM dummy; | |
Listing 4.16: Beispiel für die TRIM-Funktionen | |
SELECT * | |
FROM benutzer | |
WHERE HAMMING_DISTANCE(vorname, 'Mandy') BETWEEN 0 AND 1; | |
Listing 4.18: Beispiel für die SQL-Funktion HAMMING_DISTANCE() | |
SELECT * | |
FROM benutzer | |
WHERE nachname LIKE_REGEXPR 'M(a|e)(i|y)(a|er)'; | |
Listing 4.19: Suche mit regulären Ausdrücken | |
SELECT ASCII('A') AS char2ascii, | |
CHAR(65) AS asci2char, | |
UNICODE(' ') AS nchar2unicode, | |
NCHAR('30908') AS unicode2nchar | |
FROM dummy; | |
Listing 4.20: Konvertierung von Zeichen in ASCII bzw. Unicode und umgekehrt | |
<N-Tupel> = SPLIT(<Zeichenkette>, <Trenner>[,<# Trennungen>]) | |
DO BEGIN | |
USING SQLSCRIPT_STRING AS STRING_LIB; | |
DECLARE A1 NVARCHAR(100); | |
DECLARE A2 NVARCHAR(100); | |
DECLARE A3 NVARCHAR(100); | |
-- Split funktioniert sauber: | |
(A1, A2, A3) = STRING_LIB:SPLIT('ABC-DEF-GHI', '-'); | |
-- Zu wenige Abschnitte: | |
(A1, A2, A3) = STRING_LIB:SPLIT('ABC-DEF', '-'); | |
-- Zu viele Abschnitte: | |
(A1, A2, A3) = STRING_LIB:SPLIT('ABC-DEF-GHI-JKL', '-'); | |
-- Begrenzt auf 3 Abschnitte funktioniert: | |
(A1, A2, A3) = STRING_LIB:SPLIT('ABC-DEF-GHI-JKL', '-', 2); | |
SELECT A1, | |
A2, | |
A3 FROM dummy; | |
END; | |
Listing 4.21: Beispiele für die SPLIT-Funktion | |
DO BEGIN | |
USING SQLSCRIPT_STRING AS STRING_LIB; | |
DECLARE A1 NVARCHAR(100); | |
DECLARE A2 NVARCHAR(100); | |
(A1, A2) = STRING_LIB:SPLIT_REGEXPR('ABC-DEF', | |
'[A-Z]-[A-Z]'); | |
SELECT A1, | |
A2 FROM dummy; --('AB', 'EF') | |
END; | |
Listing 4.22: Beispiel für die Funktion SPLIT_REGEXPR | |
DO BEGIN | |
USING SQLSCRIPT_STRING AS STRING_LIB; | |
DECLARE lt_split TABLE(RESULT NVARCHAR(5000)); | |
lt_split = STRING_LIB:SPLIT_REGEXPR_TO_TABLE('ABC-DEF', | |
'[A-Z]-[A-Z]'); | |
SELECT * FROM :lt_split; | |
END; | |
Listing 4.23: Beispiel für die Tabellenfunktion SPLIT_REGEXPR_TO_TABLE | |
DO BEGIN | |
USING sqlscript_string AS string_lib; | |
DECLARE lv_string NVARCHAR(100) ; | |
lv_string = string_lib:format('{1} {0} ist {2:f} Jahre alt', | |
'Müller', | |
'Peter', | |
21.3 ); | |
SELECT :lv_string FROM dummy; | |
END; | |
Listing 4.24: Zeichenkettenformatierung mit FORMAT | |
DO BEGIN | |
USING sqlscript_string AS string_lib; | |
SELECT * | |
FROM string_lib:format_to_table( | |
'Aufgabe Nr. {id} - {titel:.10}', | |
aufgaben); | |
END | |
Listing 4.25: Formatierung einer Tabelle mit FORMAT_TO_TABLE | |
DO BEGIN | |
USING SQLSCRIPT_STRING AS STRING_LIB; | |
DECLARE lv_string nvarchar(5000); | |
lt_tasks = SELECT id, | |
titel | |
FROM aufgaben; | |
lv_string = string_lib:table_summary( :lt_tasks , 10); | |
SELECT :lv_string FROM dummy; | |
END; | |
Listing 4.26: Beispiel für die Verwendung der Funktion TABLE_SUMMARY | |
Listing 4.27: Ergebnis des Aufrufs von Listing 4.26 | |
TABLE_SUMMARY(<Tabellenvariable>[, <Anzahl Zeilen>]) | |
CREATE TABLE datum(line INT,datum DATE) ; | |
INSERT INTO datum VALUES(1,'1.12.2017') ; | |
INSERT INTO datum VALUES(2,'2017.12.02') ; | |
SELECT line, | |
datum, | |
TO_VARCHAR(datum) AS as_char | |
FROM datum; | |
DROP TABLE datum; | |
Listing 4.28: Ausgabe eines Datums als Datentyp DATE und VARCHAR | |
CREATE TABLE datum(line INT,datum DATE) ; | |
INSERT INTO datum VALUES(1,'0') ; | |
INSERT INTO datum VALUES(2,'') ; | |
INSERT INTO datum VALUES(3,'0000-00-00') ; | |
INSERT INTO datum VALUES(4,'00000000') ;--ABAP | |
INSERT INTO datum VALUES(5,'0001-01-01') ; | |
SELECT line, | |
datum, | |
TO_VARCHAR(datum) AS as_char, | |
DAYS_BETWEEN(datum, '00001-01-10') AS Delta | |
FROM datum; | |
DROP TABLE datum; | |
Listing 4.29: Erzeugung und Darstellung des leeren Datums | |
CREATE TABLE dates ( datum DATE ) ; | |
INSERT INTO dates VALUES('1.12.2017') ; | |
INSERT INTO dates VALUES('2017.12.01') ; | |
INSERT INTO dates VALUES('20171201') ; | |
INSERT INTO dates VALUES('2017/12/1') ; | |
SELECT * FROM dates; | |
DROP TABLE dates; | |
Listing 4.30: Verschiedene Datumsformate | |
Could not execute 'INSERT INTO tab_datum VALUES('01=02=2017')' | |
Error: (dberror) 303 - invalid DATE, TIME or TIMESTAMP value: Error while parsing | |
01=02=2017 as DATE | |
INSERT INTO tab_datum VALUES(TO_DATE('01=02=2017', | |
'DD=MM=YYYY')) ; | |
CREATE TABLE tab_format ( formatierung VARCHAR(10), | |
beschreibung NVARCHAR(40) ) ; | |
INSERT INTO tab_format VALUES('YY', | |
'Jahr 2-Stellig') ; | |
INSERT INTO tab_format VALUES('YYYY', | |
'Jahr 4-Stellig') ; | |
INSERT INTO tab_format VALUES('MM', | |
'Monat als zwei Ziffern') ; | |
INSERT INTO tab_format VALUES('DD', | |
'Tag im Monat') ; | |
INSERT INTO tab_format VALUES('Q', | |
'Quartal') ; | |
INSERT INTO tab_format VALUES('WW', | |
'Kalenderwoche') ; | |
INSERT INTO tab_format VALUES('RM', | |
'Monat in römischer Schreibweise') ; | |
INSERT INTO tab_format VALUES('MON', | |
'3-Stellige Abkürzung des Monats') ; | |
INSERT INTO tab_format VALUES('MONTH', | |
'Monat ausgeschrieben, in Englisch') ; | |
INSERT INTO tab_format VALUES('DDD', | |
'Tag im Jahr') ; | |
INSERT INTO tab_format VALUES('YYYY=MM=DD', | |
'Beispiel für individuelle Muster') ; | |
SELECT | |
formatierung, | |
TO_VARCHAR( TO_DATS( '08.12.2017'), | |
formatierung ) AS "Beispiel", | |
beschreibung | |
FROM tab_format ; | |
DROP TABLE tab_format ; | |
Listing 4.31: Beispiel für die Formatierung von Datumsfeldern | |
CREATE TABLE tab_format ( formatierung VARCHAR(10), | |
beschreibung NVARCHAR(40) ) ; | |
INSERT INTO tab_format VALUES('HH24', | |
'Stunde im 24-Stunden Format') ; | |
INSERT INTO tab_format VALUES('HH', | |
'Stunde im 12-Stunden Format') ; | |
INSERT INTO tab_format VALUES('HH12', | |
'Stunde im 12-Stunden Format') ; | |
INSERT INTO tab_format VALUES('AM', | |
'AM (=Vormittag) oder PM (=Nachmittag)') ; | |
INSERT INTO tab_format VALUES('PM', | |
'AM (=Vormittag) oder PM (=Nachmittag)') ; | |
INSERT INTO tab_format VALUES('MI', | |
'Minuten') ; | |
INSERT INTO tab_format VALUES('SS', | |
'Sekunden') ; | |
INSERT INTO tab_format VALUES('SSSSS', | |
'Sekunden seit Mitternacht') ; | |
SELECT | |
formatierung, | |
TO_VARCHAR( TO_TIME( '09:50:13'), | |
formatierung ) AS "Beispiel 09:50:13", | |
TO_VARCHAR( TO_TIME( '13:07:38'), | |
formatierung ) AS "Beispiel 13:07:38", | |
beschreibung | |
FROM tab_format ; | |
DROP TABLE tab_format ; | |
Listing 4.33: Konvertierung von Zeit nach Zeichenkette | |
SELECT CURRENT_DATE FROM dummy; | |
SELECT ADD_MONTHS('2017-01-31',1) FROM dummy; --> 28.02.2017 | |
SELECT ADD_MONTHS_LAST('2017-02-28', 1) FROM dummy; | |
--> 31.03.2017 | |
SELECT ADD_MONTHS('2017-02-28', 1) FROM dummy; | |
--> 28.03.2017 | |
Listing 4.34: Unterschiede zwischen den SQL-Funktionen ADD_MONTHS() und ADD_MONTHS_LAST() | |
SELECT | |
1 AS row, | |
YEAR(CURRENT_DATE) AS "Jahr", | |
MONTH(CURRENT_DATE) AS "Monat", | |
DAYOFMONTH(CURRENT_DATE) AS "Tag", | |
HOUR(CURRENT_TIME) AS "Stunde", | |
MINUTE(CURRENT_TIME) AS "Minute", | |
SECOND(CURRENT_TIME) AS "Sekunde" | |
FROM dummy | |
UNION ALL | |
SELECT | |
2 AS row, | |
EXTRACT(YEAR FROM CURRENT_DATE) AS "Jahr", | |
EXTRACT(MONTH FROM CURRENT_DATE) AS "Monat", | |
EXTRACT(DAY FROM CURRENT_DATE) AS "Tag", | |
EXTRACT(HOUR FROM CURRENT_TIME) AS "Stunde", | |
EXTRACT(MINUTE FROM CURRENT_TIME) AS "Minute", | |
EXTRACT(SECOND FROM CURRENT_TIME) AS "Sekunde" | |
FROM dummy; | |
Listing 4.35: Zerlegung eines Datums oder einer Zeit in ihre Komponenten | |
SELECT | |
WEEK('2017-12-08'), -->50 | |
ISOWEEK('2017-12-08') -->2017-W49 | |
FROM dummy; | |
Listing 4.36: Bestimmung der Kalenderwoche | |
SELECT * FROM timezones; | |
SELECT * | |
FROM m_host_information | |
WHERE key LIKE '%timezone%'; | |
Listing 4.37: Ermittlung der Zeitzone eines SAP-HANA-Systems | |
CREATE TABLE abap_laufzeit(von_datum VARCHAR(8), | |
von_zeit VARCHAR(6), | |
bis_datum VARCHAR(8), | |
bis_zeit VARCHAR(6)); | |
INSERT INTO abap_laufzeit VALUES ( '20180101', '060000', | |
'20180101', '065900' ); | |
INSERT INTO abap_laufzeit VALUES ( '20180101', '060000', | |
'20180101', '140000' ); | |
INSERT INTO abap_laufzeit VALUES ( '20180101', '060000', | |
'20180103', '050000' ); | |
INSERT INTO abap_laufzeit VALUES ( '20180101', '060000', | |
'20180201', '060000' ); | |
SELECT FLOOR( | |
SECONDS_BETWEEN( | |
TO_SECONDDATE( | |
TO_DATE(von_datum) | |
||' '|| | |
TO_TIME(von_zeit)), | |
TO_SECONDDATE( | |
TO_DATE(bis_datum) | |
||' '|| | |
TO_TIME(bis_zeit))) | |
/ 60 /*Sekunden in Minuten*/ | |
/ 60 /*Minuten in Stunden*/) | |
AS laufzeit_in_stunden | |
FROM abap_laufzeit; | |
DROP TABLE abap_laufzeit; | |
Listing 4.38: Berechnung der Maschinenlaufzeit in Stunden | |
CREATE TABLE bw_daten(calday VARCHAR(8)); | |
INSERT INTO bw_daten VALUES (TO_DATS('10.06.2018')); | |
INSERT INTO bw_daten VALUES (TO_DATS('11.06.2018')); | |
INSERT INTO bw_daten VALUES (TO_DATS('13.05.2018')); | |
INSERT INTO bw_daten VALUES (TO_DATS('14.05.2018')); | |
SELECT * | |
FROM bw_daten | |
WHERE calday <= TO_DATS(ADD_DAYS(CURRENT_DATE, UMINUS( | |
WEEKDAY(CURRENT_DATE) + 1))) | |
AND calday >= TO_DATS(ADD_DAYS(CURRENT_DATE, UMINUS( | |
WEEKDAY(CURRENT_DATE) + 28))); | |
DROP TABLE bw_daten; | |
Listing 4.39: Beispiel für die Selektion der vier letzten abgeschlossenen Wochen | |
SELECT * | |
FROM bw_daten | |
WHERE calday <= TO_DATS(UDF_ADD_DAYS_TO_LAST_SUNDAY(0)) | |
AND calday >= TO_DATS(UDF_ADD_DAYS_TO_LAST_SUNDAY(27)); | |
Listing 4.40: Auslagerung der Datumsberechnung in eine Funktion | |
SELECT TO_DOUBLE(1.2)-TO_DOUBLE(0.1) FROM dummy; | |
SELECT TO_SMALLDECIMAL(1.2)-TO_SMALLDECIMAL(0.1) FROM dummy; | |
Listing 4.41: Vergleich von dezimaler und binärer Gleitkommazahl | |
CREATE TABLE calc( wert1 INTEGER, | |
wert2 INTEGER ) ; | |
INSERT INTO calc VALUES(1,1) ; | |
INSERT INTO calc VALUES(2,0) ; | |
INSERT INTO calc VALUES(0,2) ; | |
INSERT INTO calc VALUES(3,2) ; | |
SELECT | |
wert1-wert2 AS differenz, | |
wert1+wert2 AS summe, | |
wert1*wert2 AS produkt, | |
CASE | |
WHEN wert2<>0 | |
THEN wert1/wert2 | |
ELSE 0 | |
END AS division, | |
CASE | |
WHEN wert2<>0 | |
THEN MOD(wert1,wert2) | |
ELSE 0 | |
END AS modulo | |
FROM calc; | |
Listing 4.42: Grundrechenarten in SQL | |
CREATE TABLE calc( wert double) ; | |
INSERT INTO calc VALUES(1) ; | |
INSERT INTO calc VALUES(1.1) ; | |
INSERT INTO calc VALUES(-1.1) ; | |
INSERT INTO calc VALUES(1.499999) ; | |
INSERT INTO calc VALUES(1.500000) ; | |
INSERT INTO calc VALUES(-1.499999) ; | |
INSERT INTO calc VALUES(-1.500000) ; | |
INSERT INTO calc VALUES(1.749999) ; | |
INSERT INTO calc VALUES(1.750000) ; | |
INSERT INTO calc VALUES(175) ; | |
SELECT | |
wert, | |
CEIL(wert) AS aufrunden, | |
FLOOR(wert) AS abrunden, | |
ROUND(wert, 0, ROUND_HALF_UP) AS rhu_0, | |
ROUND(wert, 0, ROUND_UP) AS ru_0, | |
ROUND(wert, 1, ROUND_HALF_UP) AS rhu_1, | |
ROUND(wert, 1, ROUND_UP) AS ru_1, | |
ROUND(wert, -1) AS rhu_m1 | |
FROM calc; | |
DROP TABLE calc; | |
Listing 4.43: Beispiele für die Rundungsfunktionen | |
CREATE TABLE mengen ( | |
menge DECIMAL(15,2), | |
einheit VARCHAR(3) ) ; | |
INSERT INTO mengen VALUES(1.0,'MG') ; | |
INSERT INTO mengen VALUES(1.0,'G') ; | |
INSERT INTO mengen VALUES(1.0,'KG') ; | |
INSERT INTO mengen VALUES(1.0,'TO') ; | |
INSERT INTO mengen VALUES(1.0,'XYZ') ; | |
SELECT | |
menge AS "Menge", | |
einheit AS "Einheit", | |
CONVERT_UNIT( | |
QUANTITY => menge, | |
SOURCE_UNIT => einheit, | |
SCHEMA => 'JBRANDEIS', | |
TARGET_UNIT => 'KG', | |
ERROR_HANDLING => 'set to null', | |
CLIENT => '000') | |
AS "Menge in Kg", | |
'Kg' AS "Kg" | |
FROM mengen; | |
Listing 4.44: Beispiel für die Mengenumrechnung in eine feste Einheit | |
CREATE TABLE betraege( | |
betrag DEC(17,2), | |
waehrung VARCHAR(3) ) ; | |
INSERT INTO betraege VALUES(89.90,'EUR') ; | |
INSERT INTO betraege VALUES(17.20,'USD') ; | |
INSERT INTO betraege VALUES(4.54,'GBP') ; | |
SELECT | |
betrag AS "Betrag", | |
waehrung AS "Währung", | |
CONVERT_CURRENCY( AMOUNT => betrag, | |
SOURCE_UNIT => waehrung, | |
SCHEMA => 'JBRANDEIS', | |
TARGET_UNIT => 'USD', | |
REFERENCE_DATE => '2013-09-23', | |
CLIENT => '000') | |
AS "Betrag in USD", 'USD' | |
AS "USD" | |
FROM betraege; | |
DROP TABLE betraege; | |
Listing 4.45: Beispiel für eine einfache Währungsumrechnung | |
SELECT BINTOSTR( X'48616C6C6F2057656C74' ) FROM dummy; | |
CREATE COLUMN TABLE test_varbin( | |
ascii_int INT, | |
ascii_char VARCHAR(1), | |
ascii_hex VARBINARY(1)) ; | |
DO BEGIN | |
DECLARE i INT; | |
FOR i IN 33..127 DO | |
INSERT INTO test_varbin VALUES(:i, | |
CHAR(:i), | |
BINTONHEX(CHAR(:i))) ; | |
END FOR; | |
END; | |
SELECT ascii_int AS "Int", | |
ascii_char AS "Char", | |
ascii_hex AS "Hex" FROM test_varbin; | |
DROP TABLE test_varbin; | |
Listing 4.46: Konvertierung von ASCII-Dezimal nach VARBINARY | |
CREATE FUNCTION udf_int_as_bit_string(iv_value INT) | |
RETURNS rv_value VARCHAR(8) | |
AS BEGIN | |
DECLARE lv_value INT default :iv_value; | |
DECLARE lv_mod INT; | |
rv_value = ''; | |
WHILE lv_value > 0 DO | |
lv_mod = MOD(:lv_value, 2); | |
IF lv_mod = 1 THEN | |
rv_value = '1' || :rv_value; | |
ELSE | |
rv_value = '0' || :rv_value; | |
END IF; | |
lv_value = lv_value / 2; | |
END WHILE; | |
rv_value = LPAD(rv_value, 8, '0'); | |
END; | |
Listing 4.47: Funktion zur Darstellung von Ganzzahlen als Bits | |
CREATE TABLE binaer(text NVARCHAR(50), | |
wert INT); | |
INSERT INTO binaer VALUES ('Zahl 248', 248 ); | |
INSERT INTO binaer VALUES ('Zahl 31', 31 ); | |
INSERT INTO binaer VALUES ('XOR(248, 31)', BITXOR(248, 31)); | |
INSERT INTO binaer VALUES ('AND(248, 31)', BITAND(248, 31)); | |
INSERT INTO binaer VALUES ('OR(248, 31)' , BITOR(248, 31)); | |
SELECT text, | |
udf_int_as_bit_string(wert) | |
FROM binaer; | |
DROP TABLE binaer; | |
Listing 4.48: Beispiel für die bitweise Verarbeitung von Binärdaten | |
Could not execute 'select to_tinyint( 1000) from dummy' | |
[314]: numeric overflow: 1000 at function to_tinyint() | |
SELECT * FROM SERIES_GENERATE_INTEGER( 2, 3, 20); | |
SELECT * FROM SERIES_GENERATE_DATE( 'INTERVAL 1 MONTH', | |
date'2019-01-01', | |
date'2020-01-01'); | |
Listing 4.49: Zeitreihe mit den Monaten des Jahres 2017 | |
CREATE TYPE tt_word AS TABLE( word nvarchar(30) ); | |
CREATE FUNCTION word2char( it_word tt_word ) | |
RETURNS TABLE (val nvarchar(1)) | |
AS BEGIN | |
lt_tmp = SELECT words.word, | |
pos.element_number AS pos | |
FROM :it_word AS words | |
CROSS JOIN SERIES_GENERATE_INTEGER(1, 0, 30) AS pos; | |
lt_tmp2 = SELECT SUBSTR(word, pos, 1) AS val | |
FROM :lt_tmp; | |
RETURN SELECT * FROM :lt_tmp2 | |
WHERE val <> ''; | |
END; | |
--Aufruf der Funktion | |
DO BEGIN | |
lt_vornamen = SELECT vorname AS word | |
FROM benutzer; | |
SELECT count(*), | |
val | |
FROM word2char( it_word => :lt_vornamen ) | |
GROUP BY val | |
ORDER BY COUNT(*) DESC; | |
END; | |
Listing 4.50: Zerlegung von Wörtern in Buchstaben mit Hilfe der SERIES-Funktionen | |
CREATE TABLE tabelle_1( | |
key1 INT DEFAULT 5, | |
key2 INT, | |
wert1 NVARCHAR(200), | |
PRIMARY KEY ( | |
key1, | |
key2 ) | |
); | |
CREATE TABLE tabelle_2( | |
key1 INT PRIMARY KEY, | |
wert1 NVARCHAR(200), | |
wert2 NVARCHAR(200) | |
); | |
Listing 5.1: Tabellen für die Beispiele dieses Kapitels | |
INSERT INTO <Tabellenname> VALUES (<Spaltenwerte>, ...); | |
INSERT INTO tabelle_1 VALUES (1, 1, 'Erste Zeile'); | |
INSERT INTO tabelle_1 VALUES (1, 2, 'Datum:'||CURRENT_DATE ); | |
INSERT INTO <Tabellenname> <Spaltenreihenfolge> | |
VALUES (<Spaltenwerte>) | |
INSERT INTO tabelle_1 (key2, wert1) | |
VALUES ( 2, 'Defaultwert'); | |
INSERT INTO tabelle_1 (wert1, key1, key2) | |
VALUES ('Vertauschte Spalten', 2, 3); | |
Listing 5.2: Beispiel für das Einfügen mit Spaltenreihenfolge | |
INSERT INTO <Tabellenname> <Abfrage>; | |
INSERT INTO tabelle_2 | |
SELECT key1 + key2 * 100, | |
wert1, | |
'Einfügen aus einer Abfrage' | |
FROM tabelle_1; | |
Listing 5.3: Beispiel für eine INSERT-Anweisung mit mehreren Datensätzen | |
INSERT INTO <Tabellenname> | |
<Spaltenreihenfolge> | |
<Abfrage>; | |
Listing 5.4: Syntax der INSERT-Anweisung für mehrere Datensätze mit Spaltenreihenfolge | |
DO BEGIN | |
lt_tmp = SELECT key1 + 10 AS key1, | |
key2, | |
'Kopie: '|| wert1 AS wert1 | |
FROM tabelle_1; | |
INSERT INTO tabelle_1 | |
(wert1, key1, key2) | |
SELECT wert1, | |
key1, | |
key2 | |
FROM :lt_tmp; | |
END; | |
Listing 5.5: INSERT mit Abfrage auf eine lokale Tabellenvariable und mit veränderter Spaltenreihenfolge | |
UPDATE tabelle_2 | |
SET wert1 = 'Aktualisiert', | |
wert2 = 'Zeile ' || key1 | |
WHERE key1 < 300; | |
Listing 5.6: Beispiel für eine einfache UPDATE-Anweisung | |
UPDATE tabelle_2 AS t2 --Alternativ: UPDATE t2 | |
SET t2.wert1 = t1.wert1 | |
FROM tabelle_2 AS t2, | |
tabelle_1 AS t1 | |
WHERE t2.key1 = t1.key1 + t1.key2 * 100; | |
Listing 5.7: Beispiel für eine UPDATE-Anweisung mit Bezug auf eine andere Tabelle | |
DELETE FROM tabelle_1; | |
DELETE FROM tabelle_2; | |
--Einfügen einzelner Sätze | |
UPSERT tabelle_1 VALUES(1, 1, 'Eingefügt mit UPSERT') | |
WITH PRIMARY KEY; | |
UPSERT tabelle_1 VALUES(1, 2, 'Eingefügt mit UPSERT') | |
WITH PRIMARY KEY; | |
UPSERT tabelle_1 VALUES(1, 3, 'Eingefügt mit UPSERT') | |
WITH PRIMARY KEY; | |
--Aktualisieren eines Datensatzes über den Primärschlüssel | |
UPSERT tabelle_1 VALUES(1, 2, 'Geändert mit UPSERT') | |
WITH PRIMARY KEY; | |
SELECT * FROM tabelle_1; | |
Listing 5.8: Beispiel für das Einfügen und Aktualisieren über den Primärschlüssel | |
UPSERT tabelle_1 SELECT key1+10, | |
key2, | |
'Eingefügt aus Unterabfrage' | |
FROM tabelle_1; | |
UPSERT tabelle_1 (key1, key2, wert1) | |
SELECT key1+10, | |
key2, | |
'Geändert durch Unterabfrage' | |
FROM tabelle_1 | |
WHERE key2 = 2; | |
Listing 5.9: Beispiel für UPSERT aus Unterabfrage | |
MERGE INTO <Zieltabelle> [AS <Zielalias>] | |
USING <Quelltabelle> [AS <Quellalias>] | |
ON (<Bedingungen>) | |
[WHEN MATCHED THEN | |
UPDATE SET <Spaltenaktualisierungen>] | |
[WHEN NOT MATCHED THEN | |
INSERT [<Spaltenliste>] VALUES( <Werte>)]; | |
Listing 5.10: Syntax der Anweisung MERGE INTO | |
DELETE FROM tabelle_1; | |
DELETE FROM tabelle_2; | |
INSERT INTO tabelle_2 VALUES(1, '', 'Erste Zeile'); | |
INSERT INTO tabelle_2 VALUES(2, '', 'Zweite Zeile'); | |
INSERT INTO tabelle_2 VALUES(3, '', 'Dritte Zeile'); | |
INSERT INTO tabelle_2 VALUES(4, '', 'Vierte Zeile'); | |
MERGE INTO tabelle_1 | |
USING tabelle_2 | |
ON (tabelle_1.key1 = tabelle_2.key1) | |
WHEN MATCHED THEN | |
UPDATE SET tabelle_1.key1 = tabelle_2.key1, | |
tabelle_1.key2 = 1, | |
tabelle_1.wert1 = 'UPDATE: ' | |
|| tabelle_1.wert1 | |
WHEN NOT MATCHED THEN | |
INSERT VALUES( tabelle_2.key1, | |
1, | |
'INSERT: ' || tabelle_2.wert2); | |
MERGE INTO tabelle_1 | |
USING tabelle_2 | |
ON (tabelle_1.key1 = tabelle_2.key1) | |
WHEN MATCHED THEN | |
UPDATE SET tabelle_1.key1 = tabelle_2.key1, | |
tabelle_1.key2 = 1, | |
tabelle_1.wert1 = 'UPDATE: ' | |
|| tabelle_1.wert1 | |
WHEN NOT MATCHED THEN | |
INSERT VALUES( tabelle_2.key1, | |
1, | |
'INSERT: ' || tabelle_2.wert2); | |
SELECT * FROM tabelle_1; | |
Listing 5.11: Beispiel für die Anweisung MERGE INTO | |
DO BEGIN | |
lt_tmp = SELECT * FROM tabelle_2; | |
MERGE INTO tabelle_1 | |
USING :lt_tmp AS t2 | |
ON (tabelle_1.key1 = t2.key1) | |
WHEN MATCHED THEN | |
UPDATE SET tabelle_1.key1 = t2.key1, | |
tabelle_1.key2 = 1, | |
tabelle_1.wert1 = 'UPDATE: ' | |
|| tabelle_1.wert1 | |
WHEN NOT MATCHED THEN | |
INSERT VALUES( t2.key1, | |
1, | |
'INSERT: ' || t2.wert2); | |
END; | |
Listing 5.12: MERGE INTO mit lokaler Tabellenvariable | |
DELETE FROM <Tabellenname> [WHERE <Bedingungen>] | |
DECLARE <Variablenname> | |
[CONSTANT] | |
<Datentyp> | |
[NOT NULL] | |
[ '=' | DEFAULT) <Initialwert>; | |
Listing 6.1: Definition einer lokalen skalaren Variable | |
CREATE FUNCTION udf_statistik | |
RETURNS rv_projekte INT, | |
rv_bearbeiter INT AS | |
BEGIN | |
--Zuweisung von Variablen durch eine SELECT-Anweisung | |
SELECT | |
COUNT( DISTINCT projekt), | |
COUNT( DISTINCT bearbeiter) | |
INTO rv_projekte, | |
rv_bearbeiter | |
FROM | |
aufgaben; | |
END; | |
DO BEGIN | |
DECLARE lv_projekte INTEGER; | |
DECLARE lv_bearbeiter INTEGER; | |
DECLARE lv_div DEC; | |
--Zuweisung mehrerer Variablen durch skalare UDF | |
(lv_projekte, lv_bearbeiter) = udf_statistik( ); | |
--Zuweisung durch einen Ausdruck | |
lv_div = :lv_bearbeiter / :lv_projekte; | |
SELECT | |
lv_projekte AS projekte, | |
lv_bearbeiter AS bearbeiter, | |
lv_div AS bearbeiter_pro_projekt | |
FROM | |
dummy; | |
END ; | |
DROP FUNCTION udf_statistik; | |
Listing 6.3: Beispiel für die Zuweisung von Werten zu lokalen skalaren Variablen | |
DO BEGIN | |
DECLARE lv_bearbeiter INT; | |
DECLARE lv_status INT; | |
DECLARE EXIT HANDLER | |
SELECT bearbeiter, | |
status | |
INTO lv_bearbeiter, | |
lv_status | |
FROM aufgaben | |
WHERE id = -1; | |
SELECT lv_bearbeiter, | |
lv_status FROM dummy; | |
END; | |
Listing 6.4: Beispiel für SELECT INTO in mehrere Variablen | |
CREATE PROCEDURE get_bearbeiter_status | |
(IN iv_id INT, | |
OUT ov_bearbeiter INT, | |
OUT ov_status INT ) | |
AS BEGIN | |
--Bis HANA 2.0 SPS03: | |
DECLARE EXIT HANDLER FOR SQLEXCEPTION | |
BEGIN | |
ov_bearbeiter = -1; | |
ov_status = -1; | |
END; | |
SELECT bearbeiter, | |
status | |
INTO ov_bearbeiter, | |
ov_status | |
DEFAULT -1, --Ab SAP HANA 2.0 SPS03: Standardwerte | |
-1 --für Variablen bei der Zuweisung | |
FROM aufgaben | |
WHERE id = iv_id; | |
END; | |
CALL get_bearbeiter_status(1, ?, ?); | |
CALL get_bearbeiter_status(-1, ?, ?); | |
Listing 6.5: Beispiel für die Fehlerverarbeitung bei SELECT INTO | |
DO | |
BEGIN --Begin des äusseren Blocks | |
DECLARE var1 VARCHAR(10) default 'Aussen'; | |
DECLARE var2 VARCHAR(10) default 'Aussen'; | |
BEGIN --Begin des inneren Blocks | |
DECLARE var1 VARCHAR(10);--Var1 wird neu deklariert | |
var1 = 'Innen'; | |
var2 = 'Innen'; | |
SELECT 'Innerer Block:' AS COMMENT, | |
:var1, | |
:var2 | |
FROM dummy; | |
END;-- Ende des inneren Blocks | |
SELECT 'Aeusserer Block' AS COMMENT, | |
:var1, | |
:var2 | |
FROM dummy; | |
END;--Ende des äusseren Blocks | |
Listing 6.6: Verschachtelte Blöcke und Überdeckung von Variablen | |
<Tabelle>.<Spalte>[<Zeilennummer>] | |
DO (OUT ot_result TABLE( id INT, | |
titel NVARCHAR(50))=>?) | |
BEGIN | |
ot_result = SELECT TOP 10 id, titel FROM aufgaben; | |
ot_result.titel[1] = 'Titel: ' || :ot_result.titel[1]; | |
END; | |
Listing 6.7: Indexbasierter Zugriff auf Tabellenvariablen | |
DO (OUT ot_result TABLE(text NVARCHAR(100))=>?) | |
BEGIN | |
DECLARE indx integer; | |
lt_aufgaben = SELECT TOP 10 * | |
FROM aufgaben; | |
lt_status = SELECT * | |
FROM status_text | |
WHERE sprache = 'DE' | |
ORDER BY id ASC; | |
FOR indx IN 1..10 DO | |
ot_result.text[:indx] = | |
:lt_status.status_text[:lt_aufgaben.status[:indx]] | |
|| ' - ' | |
|| :lt_aufgaben.titel[:indx]; | |
END FOR; | |
END; | |
Listing 6.8: Indexbasierter Zugriff mit skalaren Ausdrücken als Index | |
DO (OUT ot_result TABLE( id INT, | |
titel NVARCHAR(50))=>?) | |
BEGIN | |
ot_result = SELECT TOP 10 id, titel FROM aufgaben; | |
ot_result.titel[30] = :ot_result.titel[1]; | |
ot_result.titel[1] = :ot_result.titel[31]; | |
END; | |
Listing 6.9: Zugriff auf nicht vorhandene Zeilen | |
:<Tabellenvariable>[.<Spaltenliste>].INSERT( | |
(<Werteliste>)[, <Position>]); | |
DO (OUT rt_result TABLE( id INT, | |
text1 NVARCHAR(50), | |
text2 NVARCHAR(50))=>? ) | |
BEGIN | |
DECLARE lv_index INT; | |
FOR lv_index IN 1..5 | |
DO | |
-- Einfügen aller Spalten an der Position 1 | |
:rt_result.INSERT((lv_index, | |
'Einfügen '||lv_index, '') | |
, 1); | |
END FOR; | |
FOR lv_index IN 1..5 | |
DO | |
-- Anhängen an die Tabelle, nur für zwei Spalten | |
:rt_result.(id, text2).INSERT((lv_index, | |
'Anhängen '||lv_index)); | |
END FOR; | |
END; | |
Listing 6.10: Beispiel für das zeilenweise Einfügen in eine Tabellenvariable mit INSERT | |
:<Tabellenvariable>[.<Spaltenliste>].INSERT( | |
<Tabellenvariable2[, <Position>]) | |
DO (OUT ot_result TABLE( id INT, | |
text1 NVARCHAR(50), | |
text2 NVARCHAR(50))=>? ) | |
BEGIN | |
DECLARE lv_index INT; | |
DECLARE lt_tmp TABLE (spalte1 INT, | |
spalte2 NVARCHAR(50) ); | |
-- Tabelle LT_TMP zeilenweise aufbauen | |
:lt_tmp.INSERT((1, 'TMP_1')); | |
:lt_tmp.INSERT((2, 'TMP_2')); | |
-- Tabelle RT_RESULT zeilenweise aufbauen | |
:ot_result.INSERT((1, 'Erster Datensatz ', 'A')); | |
:ot_result.INSERT((2, 'Zweiter Datensatz', 'B')); | |
:ot_result.INSERT((3, 'Dritter Datensatz', 'C')); | |
-- Tabelle LT_TMP in RT_RESULT an Position 2 einfügen | |
:ot_result.(id, text1).INSERT(:lt_tmp, 2); | |
END; | |
Listing 6.11: Tabellenweises Einfügen mit der INSERT-Operation | |
:<Tabellenvariable>[.<Spaltenliste>].UPDATE((<Werteliste>), | |
<Position>); | |
<Tabellenvariable>[.<Spaltenliste>].[<Position>] = | |
(<Werteliste>); | |
DO BEGIN | |
DECLARE lt_tmp TABLE(text NVARCHAR(100)); | |
-- Zeilenweiser Aufbau der Tabelle | |
:lt_tmp.INSERT(('Einfügen 1'), 1); | |
:lt_tmp.INSERT(('Einfügen 2'), 2); | |
:lt_tmp.INSERT(('Einfügen 3'), 3); | |
-- Update der Zeilen | |
:lt_tmp.UPDATE(('Aktualisieren 2'),2); | |
:lt_tmp.UPDATE(('Aktualisieren 4'),4); | |
-- Update mit indexbasiertem Zugriff | |
lt_tmp[6] = ('Aktualisieren 6'); | |
END; | |
Listing 6.12: Zeilenweises Aktualisieren einer Tabellenvariablen mit UPDATE und indexbasiertem Zugriff | |
<Tabellenvariable>.DELETE([<Zeilen>]); | |
DO (OUT ot_result TABLE(text NVARCHAR(100))=>?) | |
BEGIN | |
DECLARE lv_index INT; | |
DECLARE lv_del INT ARRAY = ARRAY(1, 3); | |
-- Aufbau einer Tabelle mit den Zeilen 1 bis 10 | |
FOR lv_index IN 1..10 DO | |
:ot_result.INSERT(('Zeile '||lv_index), lv_index); | |
END FOR; | |
-- Löschen der Zeile 9 | |
:ot_result.DELETE(9); | |
-- Löschen der Zeilen 5 bis 7 | |
:ot_result.DELETE(5..7); | |
-- Löschen der Zeilen 1 und 3 aus dem ARRAY LV_DEL | |
:ot_result.DELETE(:lv_del); | |
END; | |
Listing 6.13: Beispiele für die unterschiedlichen Varianten des Löschens von Zeilen aus einer Tabellenvariablen | |
:<Tabellenvariabe>.SEARCH((<Spaltenliste>), | |
(<Werteliste>), | |
[<Startindex>]); | |
Listing 6.14: Syntax des SEARCH-Operators | |
DO (OUT ot_result TABLE( id INT, | |
status INT, | |
bearbeiter INT, | |
titel NVARCHAR(50))=>? ) | |
BEGIN | |
DECLARE lv_index INT = 0; | |
ot_result = SELECT id, | |
status, | |
bearbeiter, | |
titel | |
FROM aufgaben | |
ORDER BY id; | |
-- Suche mit SEARCH | |
lv_index = :ot_result.SEARCH((status, bearbeiter), | |
(3,3)); | |
:ot_result.DELETE(lv_index); | |
-- Direkte Verwendung des SEARCH-Operators im DELETE-Operator | |
:ot_result.DELETE(:ot_result.SEARCH((status, bearbeiter), | |
(3,3))); | |
END; | |
Listing 6.15: Suche in Tabellen mit dem SEARCH-Operator | |
DECLARE lt_dml TABLE (vorname NVARCHAR(30), | |
nachname NVARCHAR(30)); | |
INSERT INTO :lt_dml (SELECT vorname, | |
nachname | |
FROM benutzer); | |
SELECT * | |
FROM :lt_dml; | |
lt_normal = SELECT * FROM :lt_dml; | |
DECLARE <Tabellenvariable> TABLE(<Spaltendefinition>) | |
SEARCH KEY(<Suchschlüssel>) | |
DO BEGIN | |
DECLARE lv_index INTEGER; | |
DECLARE lt_task TABLE (id INTEGER, | |
bearbeiter INTEGER, | |
titel NVARCHAR(130)) | |
SEARCH KEY (bearbeiter); | |
lt_task = SELECT id, | |
bearbeiter, | |
titel | |
FROM aufgaben; | |
--Erste Zeile mit SEARCH finden | |
lv_index = :lt_task.SEARCH(bearbeiter, 4); | |
--Schleife über die Tabelle, solange die Bedingung erfüllt ist | |
WHILE :lv_index > 0 | |
AND :lt_task.bearbeiter[:lv_index] = 4 | |
DO | |
if :lt_task.bearbeiter[:lv_index] = 4 THEN | |
lt_task.titel[:lv_index] = '>>' | |
|| :lt_task.titel[:lv_index]; | |
end if; | |
lv_index = :lv_index + 1; | |
END WHILE; | |
SELECT * | |
FROM :lt_task; | |
END; | |
Listing 6.16: Suche und Schleife mit einer sortierten Tabelle | |
SET <Variablenname> = <Wert>; | |
DO BEGIN | |
SET 'TESTVARIABLE' = 'TESTWERT'; | |
SELECT session_context( 'TESTVARIABLE' ) FROM dummy; | |
END; | |
Listing 6.17: Verwendung von Sessionvariablen | |
CREATE GLOBAL TEMPORARY TABLE test_gtt (id INT, | |
text NVARCHAR(40)); | |
INSERT INTO test_gtt ( SELECT id, titel FROM aufgaben); | |
SELECT * FROM test_gtt; | |
Listing 6.18: Beispiel für eine globale temporäre Tabelle | |
CREATE LOCAL TEMPORARY TABLE #test_ltt (id INT, | |
text NVARCHAR(40)); | |
INSERT INTO #test_ltt ( SELECT id, titel FROM aufgaben); | |
SELECT * FROM #test_ltt; | |
Listing 6.19: Beispiel für eine lokale temporäre Tabelle | |
IF <Bedingung_1> THEN <Block_1> | |
[ELSEIF <Bedingung_2> THEN <Block_2>] | |
... | |
[ELSE <Block_N>] | |
END IF; | |
Listing 6.20: Syntax der IF-Anweisung | |
IF EXISTS (SELECT id | |
FROM aufgaben | |
WHERE status = 2 | |
AND faelligkeit < CURRENT_DATE) | |
THEN | |
<do something> | |
ELSEIF :lv_status IN (SELECT id | |
FROM status | |
WHERE is_final = true) | |
THEN | |
<do something else> | |
END IF; | |
Listing 6.21: Prädikate mit Unterabfragen | |
CREATE FUNCTION udf_anzeigename(lv_vorname NVARCHAR(20), | |
lv_nachname NVARCHAR(20), | |
lv_zwischenname NVARCHAR(20)) | |
RETURNS rv_name NVARCHAR(65) | |
AS BEGIN | |
IF lv_vorname IS NOT NULL AND | |
lv_nachname IS NOT NULL AND | |
lv_zwischenname IS NOT NULL | |
THEN | |
DECLARE lv_initiale NVARCHAR(1); | |
lv_initiale = LEFT(lv_zwischenname,1) ; | |
IF lv_initiale<>'' | |
THEN | |
rv_name = lv_vorname || ' ' || lv_initiale | |
|| '. ' || lv_nachname; | |
ELSE | |
rv_name = lv_vorname || ' ' || lv_nachname; | |
END IF; | |
ELSEIF lv_vorname IS NOT NULL AND | |
lv_nachname IS NOT NULL | |
THEN | |
rv_name = lv_vorname || ' ' || lv_nachname; | |
ELSEIF lv_nachname IS NOT NULL | |
THEN | |
rv_name = lv_nachname; | |
ELSEIF lv_vorname IS NOT NULL | |
THEN | |
rv_name = lv_vorname; | |
ELSE | |
rv_name = ''; | |
END IF; | |
END; | |
SELECT udf_anzeigename('Jörg','Brandeis','Roland') | |
FROM dummy | |
UNION SELECT udf_anzeigename('Jörg','Brandeis',NULL) | |
FROM dummy | |
UNION SELECT udf_anzeigename(NULL,'Brandeis',NULL) | |
FROM dummy; | |
DROP FUNCTION udf_anzeigename; | |
Listing 6.22: Beispiel für verschachtelte IF-Anweisungen | |
CREATE FUNCTION udf_sort_2(iv_wert1 INT, | |
iv_wert2 INT ) | |
RETURNS rv_klein INT, | |
rv_gross INT | |
AS BEGIN | |
IF iv_wert1 >= iv_wert2 THEN | |
rv_klein = iv_wert2; | |
rv_gross = iv_wert1; | |
ELSE | |
rv_klein = iv_wert1; | |
rv_gross = iv_wert2; | |
END IF; | |
END; | |
Listing 6.23: Funktion UDF_SORT_2 zur Sortierung von zwei Zahlen | |
CREATE FUNCTION udf_sort_3(iv_wert1 INT, | |
iv_wert2 INT, | |
iv_wert3 INT ) | |
RETURNS rv_klein INT, | |
rv_mittel INT, | |
rv_gross INT | |
AS BEGIN | |
--Anfangszuweisung (wahrscheinlich falsch) | |
rv_klein = :iv_wert1; | |
rv_mittel = :iv_wert2; | |
rv_gross = :iv_wert3; | |
--Tauschen, bis die Sortierung richtig ist | |
(rv_klein, rv_mittel) = udf_sort_2(rv_klein, rv_mittel); | |
(rv_klein, rv_gross) = udf_sort_2(rv_klein, rv_gross); | |
(rv_mittel, rv_gross) = udf_sort_2(rv_mittel, rv_gross); | |
END; | |
Listing 6.24: Funktion UDF_SORT_3 für die Sortierung von drei Zahlen | |
DO (OUT ov_klein INT=>?, | |
OUT ov_mittel INT=>?, | |
OUT ov_gross INT=>?) | |
BEGIN | |
(ov_klein, ov_mittel, ov_gross) = udf_sort_3(1,11,9); | |
END; | |
Listing 6.25: Anonymer Block zum Testen der Funktion UDF_SORT_3 | |
FOR <Variable> IN [REVERSE] <Ausgangswert>..<Endwert> DO | |
<Block> | |
END FOR; | |
Listing 6.26: Syntax der FOR-Schleife | |
DO BEGIN | |
DECLARE lv_sum INT = 0; | |
DECLARE lv_indx INT; | |
FOR lv_indx IN 1..10 DO | |
DECLARE lv_tmp INT = lv_sum; | |
lv_tmp = :lv_tmp + 10; | |
lv_sum = lv_tmp; | |
END FOR; | |
SELECT :lv_indx, :lv_sum FROM dummy; | |
END; | |
Listing 6.27: Beispiel für eine FOR-Schleife | |
WHILE <Bedingung> DO | |
<Block> | |
END WHILE; | |
Listing 6.28: Syntax der WHILE-Schleife | |
CREATE FUNCTION udf_fakultaet( IN iv_value INT) | |
RETURNS rv_result INT | |
AS BEGIN | |
DECLARE lv_counter INT = 1; | |
rv_result = 1; | |
WHILE lv_counter <= iv_value DO | |
rv_result = rv_result * lv_counter; | |
lv_counter = lv_counter + 1; | |
END WHILE; | |
END; | |
SELECT udf_fakultaet( 10 ) FROM dummy; | |
DROP FUNCTION udf_fakultaet; | |
Listing 6.29: Beispiel für eine WHILE-Schleife | |
CREATE FUNCTION udf_ggt(iv_zahl1 INT, | |
iv_zahl2 INT ) | |
RETURNS rv_ggt INT | |
AS BEGIN | |
DECLARE lv_zahl1 INT DEFAULT :iv_zahl1; | |
DECLARE lv_zahl2 INT DEFAULT :iv_zahl2; | |
DECLARE lv_cnt INT DEFAULT 0; | |
WHILE lv_zahl1 <> lv_zahl2 DO | |
--FOR lv_cnt IN 0..100 DO | |
DECLARE lv_lower INT DEFAULT 0; | |
DECLARE lv_higher INT DEFAULT 0; | |
--IF lv_zahl1 = lv_zahl2 THEN BREAK; | |
--END IF; | |
(lv_lower, lv_higher) = udf_sort_2(:lv_zahl1, | |
:lv_zahl2); | |
lv_zahl1 = :lv_higher - :lv_lower; | |
lv_zahl2 = :lv_lower; | |
END WHILE; | |
--END FOR; | |
rv_ggt = :lv_zahl1; | |
END; | |
Listing 6.31: Übungsbeispiel für das Berechnen des ggT | |
DECLARE CURSOR <Cursorname> (<Parameterdefinition>) | |
FOR <SELECT-Abfrage>; | |
FOR <Zeile> AS <Cursorname>[(<Parameter>)] | |
DO | |
<Block> | |
END FOR; | |
Listing 6.32: FOR-Schleife über Cursor | |
CREATE PROCEDURE projekte_abschliessen | |
AS | |
BEGIN | |
DECLARE CURSOR lc_projekte (iv_status INT) | |
FOR SELECT id | |
FROM projekte | |
WHERE STATUS = :iv_status; | |
FOR ls_projekte AS lc_projekte(5) --Status = Geschlossen | |
DO CALL projektaufgaben_schliessen(ls_projekte.id); | |
END FOR; | |
END; | |
Listing 6.33: Beispiel für eine FOR-Schleife über einen Cursor mit Parameter | |
OPEN <Cursorname>(<Parameter>); | |
FETCH <Cursorname> INTO <Variablenliste>; | |
DO ( OUT ov_alle_titel NVARCHAR(900) => ?) | |
BEGIN | |
lt_aufgaben = SELECT TOP 10 titel FROM aufgaben; | |
ov_alle_titel = ''; | |
BEGIN | |
DECLARE CURSOR lc_aufgaben | |
FOR SELECT titel FROM :lt_aufgaben; | |
DECLARE lv_indx INT; | |
DECLARE lv_titel NVARCHAR(40); | |
OPEN lc_aufgaben; | |
WHILE 1 <> 2 DO | |
FETCH lc_aufgaben INTO lv_titel; | |
IF lc_aufgaben::NOTFOUND THEN | |
BREAK; | |
END IF; | |
ov_alle_titel = ov_alle_titel || ' - ' || lv_titel; | |
END WHILE; | |
CLOSE lc_aufgaben; | |
END; | |
END; | |
Listing 6.34: Beispiel für die Verwendung eines Cursors | |
DECLARE CURSOR <Cursorname> (<Parameterdefinition>) | |
FOR <SELECT-Abfrage> FOR UPDATE [OF <Spaltenliste>]; | |
UPDATE <DB-Tabelle> | |
SET <Set-Klausel> | |
WHERE CURRENT OF <Cursor> | |
Listing 6.35: Syntax der UPDATE-Anweisung | |
DELETE FROM <DB-Tabelle> | |
WHERE CURRENT OF <Cursor> | |
Listing 6.36: Syntax der DELETE-Anweisung | |
CREATE COLUMN TABLE array_demo( | |
keycol INT , | |
namen NVARCHAR(30) ARRAY | |
); | |
INSERT INTO array_demo VALUES( 1, | |
ARRAY ('Jörg', | |
'Roland', | |
'Brandeis') | |
); | |
INSERT INTO array_demo VALUES( 2, | |
ARRAY ('Peter', | |
'Maier') | |
); | |
INSERT INTO array_demo VALUES( 3, | |
ARRAY ('Rotkäpchen') | |
); | |
SELECT keycol, | |
namen[1], --Erstes Element | |
namen[cardinality(namen)] --Letztes Element | |
FROM array_demo; | |
DROP TABLE array_demo; | |
Listing 6.37: Beispiel für den Zugriff auf ein Array in einer Datenbanktabelle | |
DO BEGIN | |
DECLARE i INT; | |
DECLARE demo VARCHAR(20) ARRAY; | |
-- Einfügen von zehn Elementen in das ARRAY | |
FOR i IN 1..10 DO | |
demo[:i] = concat ('Wert Nr.', :i); | |
END FOR; | |
SELECT :demo[10] FROM dummy; | |
-- Entfernen der hinteren fünf Elemente | |
demo = TRIM_ARRAY( :demo, 5 ); | |
SELECT :demo[1], | |
:demo[5], | |
:demo[10] | |
FROM dummy; | |
-- Einfügen eines Elements an Position 20 | |
demo[20] = 'Letztes Element'; | |
SELECT :demo[5], | |
:demo[10], | |
:demo[20] | |
FROM dummy; | |
END; | |
Listing 6.38: Arrays in SQLScript-Code | |
DO BEGIN | |
DECLARE tiere VARCHAR(10) ARRAY = ARRAY('Hund', 'Katze', | |
'Maus'); | |
DECLARE farben VARCHAR(10) ARRAY = ARRAY('rot', 'gelb', | |
'blau', 'orange', 'grün', 'violett'); | |
tabelle = UNNEST(:tiere, :farben) | |
WITH ORDINALITY AS ( | |
"Tier", | |
"Farbe", | |
"Nr" | |
); | |
SELECT * | |
FROM :tabelle; | |
END; | |
Listing 6.39: Umwandlung von ARRAYs in Tabellen mit UNNEST() | |
DO BEGIN | |
DECLARE lv_cardinality INT; | |
DECLARE la_bearbeiter INT ARRAY; | |
lt_aufgaben = SELECT DISTINCT bearbeiter FROM aufgaben; | |
la_bearbeiter = ARRAY_AGG(:lt_aufgaben.bearbeiter); | |
lv_cardinality = CARDINALITY(:la_bearbeiter); | |
SELECT :lv_cardinality | |
FROM dummy; | |
END; | |
Listing 6.40: Konvertierung von einer Tabellenspalte in ein Array und Abfrage der Kardinalität | |
--Tabellentyp TT_WERT anlegen | |
CREATE TYPE tt_wert AS TABLE(wert NVARCHAR(30)); | |
--Funktion anlegen | |
CREATE FUNCTION udf_sort(in it_table tt_wert) | |
RETURNS tt_wert | |
AS BEGIN | |
DECLARE la_werte NVARCHAR(30) ARRAY; | |
DECLARE lv_zeilen INT; --Anzahl Zeilen | |
DECLARE lv_runde INT; --Aktuelle Runde | |
DECLARE lv_zeile INT; --Aktuelle Zeile | |
DECLARE lv_tmp NVARCHAR(30); --Für die Vertauschung | |
--Umwandeln der Tabelle in ein Array: | |
la_werte = ARRAY_AGG(:it_table.wert); | |
--Anzahl der Zeilen ermitteln: | |
SELECT COUNT(*) INTO lv_zeilen FROM :it_table; | |
FOR lv_runde IN 1..:lv_zeilen DO | |
FOR lv_zeile IN 2..:lv_zeilen DO | |
IF :la_werte[:lv_zeile-1] > :la_werte[:lv_zeile] | |
THEN --Vertauschung: | |
lv_tmp = :la_werte[:lv_zeile-1]; | |
la_werte[:lv_zeile-1] = :la_werte[:lv_zeile]; | |
la_werte[:lv_zeile] = :lv_tmp; | |
END IF; | |
END FOR; --Zeilen | |
END FOR; --Runden | |
--Umwandlung des sortierten Arrays in eine Tabelle: | |
rt_result = UNNEST(:la_werte) AS (wert); | |
RETURN :rt_result; | |
END; | |
Listing 6.41: Der Bubblesort-Algorithmus in SQLScript | |
DO BEGIN | |
DECLARE lt_testdaten tt_wert; | |
DECLARE la_testdaten NVARCHAR(30) ARRAY ; | |
la_testdaten = ARRAY('Z', 'Y', 'B', 'A'); | |
lt_testdaten = UNNEST(:la_testdaten) AS ( wert ); | |
SELECT * FROM udf_sort(:lt_testdaten); | |
END; | |
Listing 6.42: Test des Sortieralgorithmus mit einfachen Daten | |
BEGIN AUTONOMOUS TRANSACTION | |
... | |
END; | |
Listing 6.43: Syntax einer autonomen Transaktion | |
CREATE PROCEDURE write_error_log ( | |
IN iv_error_code INT, | |
IN iv_error_message NVARCHAR(500) ) | |
AS BEGIN | |
BEGIN AUTONOMOUS TRANSACTION | |
INSERT INTO error_log ( zeitstempel, | |
fehlercode, | |
nachricht ) | |
VALUES ( current_timestamp, | |
:iv_error_code, | |
:iv_error_message ); | |
END; | |
END; | |
Listing 6.44: Beispiel für eine Log-Prozedur mit einer autonomen Transaktion. | |
DO BEGIN | |
DECLARE lv_statement VARCHAR(1000); | |
DECLARE cursor lr_tables | |
FOR SELECT table_columns.table_name | |
FROM table_columns | |
INNER JOIN tables | |
ON table_columns.table_Name = tables.table_name | |
AND tables.is_user_defined_type = 'FALSE' | |
WHERE table_columns.schema_name = 'JBRANDEIS' | |
AND column_name = 'ID'; | |
--Anlegen der LTT | |
CREATE LOCAL TEMPORARY TABLE #max_id(table_name NVARCHAR(256), | |
max_id INT ); | |
FOR ls_tables AS lr_tables DO | |
--Zusammenbau der INSERT-Anweisung | |
lv_statement = 'INSERT INTO #max_id SELECT ''' | |
|| ls_tables.table_name | |
|| ''' AS table_name, max(ID) AS max_id FROM ' | |
|| ls_tables.table_name || ';'; | |
--Ausführung des dynamischen SQLs | |
EXEC :lv_statement ; | |
END FOR; | |
--Ergebnis aus der LTT abholen | |
SELECT * FROM #max_id ORDER BY max_id DESC; | |
END; | |
Listing 6.45: Beispiel für dynamisches SQL mit einer lokalen temporären Tabelle | |
EXECUTE IMMEDIATE <SQL-Anweisung> INTO <Variablenliste> | |
USING <Ausdrucksliste> | |
CREATE PROCEDURE get_column_values | |
(IN iv_column NVARCHAR(30), | |
IN iv_table NVARCHAR(30), | |
OUT ot_result TABLE(value NVARCHAR(100))) | |
AS BEGIN | |
DECLARE lv_sql NVARCHAR(1000) DEFAULT ''; | |
lv_sql = 'SELECT DISTINCT ' | |
|| :iv_column | |
|| ' AS value FROM ' | |
|| :iv_table; | |
EXEC lv_sql INTO ot_result; | |
END; | |
CALL get_column_values( 'TITEL', 'AUFGABEN', ?); | |
Listing 6.46: Ergebnistabelle einer dynamischen SQL-Abfrage in SQLScript übernehmen | |
CREATE PROCEDURE get_min_max | |
(IN iv_column NVARCHAR(30), | |
IN iv_table NVARCHAR(30), | |
OUT ev_min NVARCHAR(100), | |
OUT ev_max NVARCHAR(100) ) | |
AS BEGIN | |
DECLARE lv_sql NVARCHAR(1000) DEFAULT ''; | |
lv_sql = 'SELECT MIN(' || :iv_column | |
||'),MAX(' || :iv_column | |
||') FROM '|| :iv_table; | |
EXEC lv_sql INTO ev_min, ev_max; | |
END; | |
CALL get_min_max( 'TITEL', 'AUFGABEN', ?, ?); | |
Listing 6.47: Mehrere skalare Variablen aus einer dynamischen SQL-Abfrage übernehmen | |
CREATE PROCEDURE count_id( IN iv_maxid INT, | |
OUT ov_count INT ) | |
AS BEGIN | |
EXEC 'SELECT COUNT(*) FROM aufgaben WHERE id < ?' | |
INTO ov_count | |
USING :iv_maxid; | |
-- Alternative mit benannten Parametern | |
-- EXEC 'SELECT COUNT(*) FROM aufgaben WHERE id < :iv_maxid' | |
-- INTO ov_count | |
-- USING :iv_maxid; | |
-- Alternative mit dynamischer Generierung der SQL Anweisung | |
-- EXEC 'SELECT COUNT(*) FROM aufgaben WHERE id <' | |
-- || :iv_maxid | |
-- INTO ov_count; | |
END; | |
CALL count_id( 10, ?); | |
Listing 6.48: Beispiel für Eingabeparameter in dynamischem SQL | |
CREATE PROCEDURE dynsel_from_local_table | |
( OUT ov_count INT ) | |
AS BEGIN | |
lt_tmp = SELECT id FROM aufgaben; | |
EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM :lt_tmp' | |
INTO ov_count | |
USING :lt_tmp; | |
END; | |
CALL dynsel_from_local_table(?); | |
Listing 6.49: Dynamische Selektion aus einer Tabellenvariablen | |
CREATE PROCEDURE gefilterte_aufgaben ( | |
IN iv_where VARCHAR(100), | |
OUT rt_ergebnis TABLE ( id INT, | |
status INT, | |
titel NVARCHAR(40) ) ) | |
AS BEGIN | |
lt_tmp = APPLY_FILTER(aufgaben, :iv_where); | |
rt_ergebnis = SELECT id, | |
status, | |
titel | |
FROM :lt_tmp; | |
END; | |
CALL gefilterte_aufgaben('status in ( 1, 2)', ?); | |
Listing 6.50: Dynamisches Filtern mit APPLY_FILTER | |
DO BEGIN | |
DECLARE lv_where NVARCHAR(1000) = ''; | |
lv_where = 'titel LIKE (''v%'') '; | |
lt_tmp = SELECT * FROM aufgaben; | |
lt_tmp = apply_filter(:lt_tmp, :lv_where); | |
SELECT * FROM :lt_tmp; | |
END | |
Listing 6.51: Verwendung von Literalen in dynamischem Code | |
SELECT * FROM m_error_codes ORDER BY code; | |
DECLARE <Fehlername> CONDITION [FOR SQL_ERROR_CODE <Fehlercode>]; | |
DECLARE unbekannte_aufgabe CONDITION; | |
DECLARE Division_durch_0 CONDITION SQL_ERROR_CODE 304; | |
--Nur mit Fehlercode und Text: | |
SIGNAL SQL_ERROR_CODE 10000 SET MESSAGE_TEXT = 'Mein Fehler'; | |
--oder mit definiertem Fehlername und Text: | |
DECLARE my_error CONDITION FOR SQL_ERROR_CODE 10000; | |
... | |
SIGNAL my_error SET MESSAGE_TEXT = 'Mein Fehler'; | |
--oder auch ohne Fehlercode und Text: | |
DECLARE my_error CONDITION; | |
... | |
SIGNAL my_error; | |
Listing 6.52: Beispiele für das Auslösen von benutzerdefinierten Ausnahmen | |
DECLARE EXIT HANDLER FOR <Fehlerliste> <Anweisung>; | |
WRITE_ERROR_LOG( <Fehlercode>, <Nachricht>) | |
und | |
SHOW_ERROR_LOG([<Zeitraum_in_Sekunden>]) | |
CREATE PROCEDURE test_fehlerbehandlung( | |
IN iv_aktion VARCHAR(10)) | |
AS BEGIN | |
/* Deklarationen*/ | |
DECLARE lv_tmp INT; | |
DECLARE fehler1 CONDITION ; | |
DECLARE fehler2 CONDITION FOR SQL_ERROR_CODE 10001 ; | |
DECLARE division_durch_0 CONDITION FOR SQL_ERROR_CODE 304; | |
/* Fehlerbehandler*/ | |
DECLARE EXIT HANDLER FOR fehler1 | |
write_error_log( ::sql_error_code, | |
::sql_error_message ); | |
DECLARE EXIT HANDLER FOR fehler2 | |
write_error_log( ::sql_error_code, 'Fehler2' ); | |
DECLARE EXIT HANDLER FOR division_durch_0 | |
write_error_log( ::sql_error_code, | |
'SQL-Fehler: ' || ::sql_error_message ); | |
/* Anweisungen*/ | |
IF iv_aktion='F1' THEN | |
SIGNAL fehler1 ; | |
ELSEIF iv_aktion='F2' THEN | |
SIGNAL fehler2; | |
ELSEIF iv_aktion='D0' THEN | |
SELECT 1/0 FROM dummy ; | |
END IF; | |
END; | |
CALL test_fehlerbehandlung('F1'); | |
CALL test_fehlerbehandlung('F2'); | |
CALL test_fehlerbehandlung('D0'); | |
CALL show_error_log(); | |
DROP PROCEDURE test_fehlerbehandlung; | |
Listing 6.53: Beispiele für die Fehlerbehandlung von SQL-Fehlern und benutzerdefinierten Fehlern | |
CREATE PROCEDURE fehler_ausloesen | |
AS | |
BEGIN | |
--Deklaration | |
DECLARE mein_fehler CONDITION FOR SQL_ERROR_CODE 10001; | |
--Anweisungen | |
SIGNAL mein_fehler; | |
END; | |
CREATE PROCEDURE fehler_fangen | |
AS | |
BEGIN | |
--Deklaration | |
DECLARE mein_Fehler CONDITION FOR SQL_ERROR_CODE 10001; | |
--Fehlerbehandlung | |
DECLARE EXIT HANDLER FOR mein_fehler | |
CALL write_error_log(::SQL_ERROR_CODE, 'Hab ihn!'); | |
--Anweisungen | |
CALL fehler_ausloesen(); | |
END; | |
CALL fehler_fangen; | |
CALL show_error_log(); | |
DROP PROCEDURE fehler_ausloesen; | |
DROP PROCEDURE fehler_fangen; | |
Listing 6.54: Beispiel für die Fehlerbehandlung bei Prozeduraufrufen | |
CREATE PROCEDURE demo_resignal | |
AS BEGIN | |
DECLARE EXIT HANDLER FOR SQLEXCEPTION | |
BEGIN --Block für die Fehlerbehandlung | |
IF ::sql_error_code > 219 --Die könnten interessieren | |
THEN | |
CALL write_error_log(::sql_error_code, | |
::sql_error_message); | |
ELSE | |
RESIGNAL;--Bei den Fehlern besser abbrechen | |
END IF; | |
END;--Ende der Fehlerbehandlung | |
... | |
END; | |
Listing 6.55: Weitergabe von Fehlern mit RESIGNAL | |
DECLARE EXIT HANDLER FOR SQLEXCEPTION | |
RESIGNAL SET MESSAGE_TEXT = 'Prozedur' | |
|| ::CURRENT_OBJECT_SCHEMA | |
|| '.' | |
|| ::CURRENT_OBJECT_NAME | |
|| ::SQL_ERROR_MESSAGE; | |
Listing 6.56: Fehlerbehandler mit Ergänzung von Datenbankschema und Prozedur im Fehlertext | |
CREATE TABLE status (id INT , | |
text NVARCHAR(30) ); | |
<Spaltenname> | |
<Datentyp> | |
[DEFAULT <Standardwert>] | |
[<Einschränkung>] | |
Listing 7.1: Spaltendefinition | |
CREATE TABLE status (id INT PRIMARY KEY, | |
sortiernr INT NOT NULL UNIQUE, | |
text NVARCHAR(30) ); | |
Listing 7.2: Beispiel für Einschränkungen auf einzelnen Spalten | |
CREATE TABLE test_unique (a INT, | |
b INT, | |
c INT, | |
UNIQUE(a, b), | |
UNIQUE(b, c)); | |
Listing 7.3: Beispiel für mehrere UNIQUE-Einschränkungen auf mehreren Spalten | |
CREATE TABLE test_composite_key (a INT, | |
b INT, | |
c INT, | |
PRIMARY KEY(a, b)); | |
Listing 7.4: Beispiel für einen zusammengesetzten Primärschlüssel | |
CREATE [<Typ>] TABLE | |
CREATE COLUMN TABLE test_identity ( | |
a INT GENERATED BY DEFAULT AS IDENTITY, | |
b VARCHAR(10)); | |
INSERT INTO test_identity (b) VALUES ('Eins'); | |
INSERT INTO test_identity (b) VALUES ('Zwei'); | |
INSERT INTO test_identity (a,b) VALUES (3, 'Drei'); | |
INSERT INTO test_identity (b) VALUES ('Vier'); | |
SELECT * FROM test_identity; | |
Listing 7.5: Automatische Nummernvergabe | |
CREATE TABLE <Tabellenname> | |
LIKE <Originaltabelle> | |
[WITH DATA]; | |
CREATE TABLE tmp_aufgaben AS ( | |
SELECT a.id, | |
b.vorname, | |
b.nachname, | |
t.team_text | |
FROM aufgaben AS a | |
LEFT OUTER JOIN benutzer AS b | |
ON a.bearbeiter = b.id | |
LEFT OUTER JOIN team_text AS t | |
ON b.team = t.id | |
) | |
WITH DATA; | |
Listing 7.6: Anlegen einer neuen Tabelle auf Basis einer SQL-Abfrage | |
CREATE ROW TABLE demo_table( | |
col1 INT, | |
col2 INT | |
); | |
--Hinzufügen einer Spalte | |
ALTER TABLE demo_table ADD (col3 VARCHAR(20)); | |
--Ändern von Spalteneigenschaften, z. B. Standardwert setzen: | |
ALTER TABLE demo_table ALTER (col1 INT DEFAULT 42); | |
--Hinzufügen der Primärschlüsseldefinition: | |
ALTER TABLE demo_table | |
ADD CONSTRAINT pk PRIMARY KEY (col1, col2); | |
--Ändern des Typs: | |
ALTER TABLE demo_table COLUMN; | |
Listing 7.7: Beispiel für die Änderung von Tabelleneigenschaften | |
CREATE TYPE TESTTYP AS TABLE( | |
col1 INT, | |
col2 VARCHAR(10) ); | |
CREATE PROCEDURE TESTPROC( | |
IN it_data TESTTYP ) | |
... | |
Listing 7.8: Beispiel für die Definition eines Tabellentyps und die Verwendung in einer Prozedurdefinition | |
CREATE TABLE rechnungspositionen( | |
rechnungsnr INT, | |
position INT, | |
produkt NVARCHAR(30), | |
menge INT, | |
betrag DEC(17,2), | |
waehrung VARCHAR(5), | |
PRIMARY KEY (rechnungsnr, position) | |
); | |
CREATE VIEW rechnungen | |
AS SELECT rechnungsnr, | |
SUM(betrag*menge) AS betrag, | |
waehrung | |
FROM rechnungspositionen | |
GROUP BY rechnungsnr, | |
waehrung; | |
INSERT INTO rechnungspositionen | |
VALUES (1, 10, 'Fernseher', 1, 765.23, 'EUR'); | |
INSERT INTO rechnungspositionen | |
VALUES (1, 20, 'Kabel' , 1, 12.99 , 'EUR'); | |
INSERT INTO rechnungspositionen | |
VALUES (1, 30, 'Batterien', 4, 1.99 , 'EUR'); | |
INSERT INTO rechnungspositionen | |
VALUES (2, 10, 'Maus ', 1, 23.99 , 'EUR'); | |
INSERT INTO rechnungspositionen | |
VALUES (3, 10, 'Kabel' , 2, 12.99 , 'EUR'); | |
INSERT INTO rechnungspositionen | |
VALUES (3, 20, 'Swich' , 1, 27.99 , 'USD'); | |
--SELECT-Abfrage auf die Tabelle | |
SELECT rechnungsnr, | |
SUM(betrag*menge) AS betrag, | |
waehrung | |
FROM rechnungspositionen | |
GROUP BY rechnungsnr, | |
waehrung; | |
--SELECT-Abfrage auf den VIEW | |
SELECT * FROM rechnungen; | |
Listing 7.9: Beispiel für einen View als gespeicherte SELECT-Abfrage | |
CREATE VIEW <Viewname> AS <Abfrage>; | |
CREATE VIEW rechnungen (IN iv_waehrung VARCHAR(5)) | |
AS SELECT rechnungsnr, | |
SUM(betrag*menge) AS betrag, | |
waehrung | |
FROM rechnungspositionen | |
WHERE waehrung = :iv_waehrung | |
GROUP BY rechnungsnr, | |
waehrung; | |
Listing 7.10: Definition von parametrisierten Views | |
SELECT * FROM rechnungen( 'USD' ); | |
SELECT * FROM rechnungen( iv_waehrung=>'USD' ); | |
CREATE SEQUENCE <Sequenzname> <Parameter> | |
CREATE SEQUENCE countdown INCREMENT BY-1 | |
MAXVALUE 10 | |
MINVALUE 0 | |
CYCLE; | |
DO BEGIN | |
DECLARE lv_counter INT; | |
DECLARE lv_tmp INT; | |
DECLARE la_array INT ARRAY; | |
FOR lv_counter IN 1..100 DO | |
SELECT countdown.nextval | |
INTO lv_tmp | |
FROM dummy; | |
la_array[:lv_counter] = lv_tmp; | |
END FOR; | |
lt_output = UNNEST(:la_array); | |
SELECT * | |
FROM :LT_OUTPUT; | |
END; | |
Listing 7.11: Beispiel für die Nutzung einer Sequenz | |
CREATE SEQUENCE lognr; --Technischer Schlüssel des Log | |
CREATE TABLE rechnungspositionen( | |
rechnungsnr INT, | |
position INT, | |
produkt NVARCHAR(30), | |
menge INT, | |
betrag DEC(17,2), | |
waehrung VARCHAR(5), | |
PRIMARY KEY (rechnungsnr, position) | |
); | |
CREATE TABLE rp_log( | |
log_nr INT, | |
rechnungsnr INT, | |
position INT, | |
benutzer VARCHAR(80), | |
datum DATE, | |
zeit TIME, | |
aktion VARCHAR(1) | |
); | |
--Trigger für das Einfügen | |
CREATE TRIGGER rp_insert AFTER INSERT | |
ON rechnungspositionen | |
REFERENCING NEW ROW ls_new | |
FOR EACH ROW | |
BEGIN | |
INSERT INTO rp_log VALUES( lognr.nextval, | |
:ls_new.rechnungsnr, | |
:ls_new.position, | |
CURRENT_USER, | |
CURRENT_DATE, | |
CURRENT_TIME, | |
'I'); | |
END; | |
--Trigger für das Aktualisieren von Daten | |
CREATE TRIGGER rp_update AFTER UPDATE | |
ON rechnungspositionen | |
REFERENCING NEW ROW ls_new | |
FOR EACH ROW | |
BEGIN | |
INSERT INTO rp_log VALUES( lognr.nextval, | |
:ls_new.rechnungsnr, | |
:ls_new.position, | |
CURRENT_USER, | |
CURRENT_DATE, | |
CURRENT_TIME, | |
'U'); | |
END; | |
INSERT INTO rechnungspositionen | |
VALUES (1, 10, 'Schokolade', 1, 1.99, 'EUR'); | |
INSERT INTO rechnungspositionen | |
VALUES (1,20, 'Espresso Kaffeebohnen', 1, 13.99, 'EUR'); | |
UPDATE rechnungspositionen | |
SET betrag = '12.99' | |
WHERE rechnungsnr = 1 | |
AND position = 20; | |
SELECT * FROM rp_log; | |
Listing 7.12: Beispiel für einen Trigger für eine automatische Änderungsprotokollierung | |
CREATE TRIGGER rp_update AFTER UPDATE | |
ON rechnungspositionen | |
REFERENCING NEW ROW ls_new | |
OLD ROW ls_old | |
FOR EACH ROW | |
... | |
Listing 7.13: Zuweisung von OLD und NEW an lokale Variablen | |
CREATE TRIGGER rp_insert AFTER INSERT | |
ON rechnungspositionen | |
REFERENCING NEW ROW ls_new | |
FOR EACH ROW | |
... | |
Listing 7.14: Parametrisierung für die zeilenweise Verarbeitung | |
CREATE TRIGGER rp_insert AFTER INSERT | |
ON rechnungspositionen | |
REFERENCING NEW TABLE AS lt_new | |
FOR EACH STATEMENT | |
... | |
Listing 7.15: Parametrisierung für die Verarbeitung pro Anweisung mit Tabellenparametern | |
CLASS zcl_amdp_demo DEFINITION | |
PUBLIC | |
CREATE PUBLIC. | |
PUBLIC SECTION. | |
INTERFACES if_amdp_marker_hdb. | |
TYPES gty_tt_countries TYPE TABLE OF t005t. | |
METHODS get_countries | |
IMPORTING | |
VALUE(iv_langu) TYPE langu | |
EXPORTING | |
VALUE(et_country) TYPE gty_tt_countries | |
CHANGING | |
VALUE(cv_subrc) TYPE sy-subrc. | |
ENDCLASS. | |
CLASS zcl_amdp_demo IMPLEMENTATION. | |
METHOD get_countries | |
BY DATABASE PROCEDURE FOR HDB LANGUAGE SQLSCRIPT | |
USING t005t. | |
et_country = select * | |
FROM t005t | |
WHERE spras = :iv_langu; | |
SELECT CASE | |
WHEN COUNT(*) > 0 | |
THEN 0 | |
ELSE 4 | |
END AS subrc | |
INTO cv_subrc | |
FROM :et_country; | |
ENDMETHOD. | |
ENDCLASS. | |
Listing 8.1: Beispiel für eine einfache AMDP-Methode | |
1 METHOD get_countries | |
2 BY DATABASE PROCEDURE FOR HDB | |
3 LANGUAGE SQLSCRIPT | |
4 OPTIONS READ-ONLY | |
5 USING t005t. | |
6 | |
7 <SQLScript-Code> | |
8 ENDMETHOD. | |
Listing 8.2: Schlüsselwörter bei der Implementierung einer AMDP-Methode | |
CREATE PROCEDURE | |
"ZCL_AMDP_DEMO=>GET_COUNTRIES" | |
( | |
IN "IV_LANGU" NVARCHAR (000001), | |
OUT "EV_SUBRC" INTEGER, | |
IN "CT_COUNTRY__IN__" "ZCL_AMDP_DEMO=>GET_COUNTRIES=>P00000#ttyp", | |
OUT "CT_COUNTRY" "ZCL_AMDP_DEMO=>GET_COUNTRIES=>P00000#ttyp" | |
) | |
LANGUAGE sqlscript SQL SECURITY INVOKER | |
AS BEGIN | |
"CT_COUNTRY" = select * from :CT_COUNTRY__IN__ ; | |
BEGIN | |
lt_countries = SELECT land1 FROM :CT_COUNTRY; | |
et_country = SELECT t5.* | |
FROM "ZCL_AMDP_DEMO=>T005T#covw" AS t5 | |
INNER JOIN :LT_COUNTRIES AS countries | |
ON t5.land1 = countries.land1 | |
WHERE t5.spras = :IV_LANGU; | |
SELECT CASE | |
WHEN COUNT(*) > 0 | |
THEN 0 | |
ELSE 4 | |
END AS subrc | |
INTO ev_subrc | |
FROM :CT_COUNTRY; | |
END; | |
END; | |
Listing 8.3: Quellcode der Prozedur ZCL_AMDP_DEMO=>GET_COUNTRIES | |
CREATE PROCEDURE | |
"ZCL_AMDP_DEMO=>GET_COUNTRIES#stb2#20180301173139" | |
( | |
IN "IV_LANGU" NVARCHAR (000001), | |
OUT "EV_SUBRC" INTEGER | |
) | |
LANGUAGE sqlscript SQL SECURITY INVOKER AS BEGIN | |
"CT_COUNTRY__IN__" = SELECT * FROM "ZCL_AMDP_DEMO=>GET_COUNTRIES=>P00000#tft#20180301173139" ; | |
CALL "ZCL_AMDP_DEMO=>GET_COUNTRIES" ( | |
"CT_COUNTRY__IN__" => :CT_COUNTRY__IN__ , | |
"IV_LANGU" => :IV_LANGU , | |
"EV_SUBRC" => :EV_SUBRC , | |
"CT_COUNTRY" => :CT_COUNTRY | |
); | |
SELECT * FROM :CT_COUNTRY; | |
END; | |
Listing 8.4: Quellcode der Stub-Prozedur | |
METHOD get_instance. | |
DATA lv_classname TYPE classname. | |
CASE sy-dbsys. | |
WHEN 'HDB'. | |
lv_classname = 'ZCL_READ_XYZ_AMDP'. | |
WHEN OTHERS. | |
lv_classname = 'ZCL_READ_XYZ_OSQL'. | |
ENDCASE. | |
CREATE OBJECT ro_instance TYPE (lv_classname). | |
ENDMETHOD. | |
Listing 8.5: Statische Factory-Methode GET_INSTANCE in ABAP | |
CLASS zcl_amdp_call DEFINITION PUBLIC. | |
PUBLIC SECTION. | |
TYPES: BEGIN OF ty_s_price, | |
item TYPE numc4, | |
netto TYPE wertv9, | |
brutto TYPE wertv9, | |
mwst TYPE wertv9, | |
waehrs TYPE waers, | |
END OF ty_s_price. | |
TYPES ty_t_price TYPE STANDARD TABLE OF ty_s_price. | |
INTERFACES if_amdp_marker_hdb. | |
METHODS calculate_mwst | |
IMPORTING | |
VALUE(iv_mwst) TYPE int1 | |
CHANGING | |
VALUE(ct_price) TYPE ty_t_price. | |
METHODS calculate_brutto | |
IMPORTING | |
VALUE(iv_mwst) TYPE int1 | |
CHANGING | |
VALUE(ct_price) TYPE ty_t_price. | |
ENDCLASS. | |
CLASS zcl_amdp_call IMPLEMENTATION. | |
METHOD calculate_brutto BY DATABASE PROCEDURE | |
FOR HDB LANGUAGE SQLSCRIPT | |
USING zcl_amdp_call=>calculate_mwst. | |
CALL "ZCL_AMDP_CALL=>CALCULATE_MWST"( | |
iv_mwst => :iv_mwst, | |
ct_price => :ct_price, | |
ct_price__in__ => :ct_price ); | |
ct_price = SELECT item, | |
netto, | |
netto + mwst as brutto, | |
mwst, | |
waehrs | |
FROM :ct_price; | |
ENDMETHOD. | |
METHOD calculate_mwst BY DATABASE PROCEDURE | |
FOR HDB LANGUAGE SQLSCRIPT. | |
ct_price = SELECT item, | |
netto, | |
brutto, | |
netto * :iv_mwst / 100 as mwst, | |
waehrs | |
FROM :ct_price; | |
ENDMETHOD. | |
ENDCLASS. | |
Listing 8.6: Aufruf einer AMDP-Prozedur aus einer anderen AMDP-Methode | |
METHOD <Methodenname> BY DATABASE FUNCTION | |
FOR HDB LANGUAGE SQLSCRIPT | |
OPTIONS READ-ONLY | |
[USING <Verwendungen>]. | |
<SQLScript-Code> | |
ENDMETHOD. | |
Listing 8.7: Grundgerüst der Definition einer CDS-Tabellenfunktion | |
CLASS zjb_cl_country DEFINITION PUBLIC. | |
PUBLIC SECTION. | |
INTERFACES if_amdp_marker_hdb. | |
CLASS-METHODS get_country_text | |
FOR TABLE FUNCTION z_country_text. | |
ENDCLASS. | |
CLASS zjb_cl_country IMPLEMENTATION. | |
METHOD get_country_text BY DATABASE FUNCTION | |
FOR HDB LANGUAGE SQLSCRIPT | |
OPTIONS READ-ONLY | |
USING t005t. | |
RETURN SELECT mandt, | |
land1 AS country, | |
landx50 AS text | |
FROM t005t | |
WHERE spras = :sy_langu | |
AND mandt = :mandt; | |
ENDMETHOD. | |
ENDCLASS. | |
SELECT * | |
FROM z_country_text | |
INTO TABLE @DATA(lt_country) | |
##db_feature_mode[amdp_table_function]. | |
cl_demo_output=>display( lt_country ). | |
Listing 8.8: Aufbau der Implementierung einer AMDP-Funktion | |
@EndUserText.label: 'Country texts' | |
define table function z_country_text | |
with parameters | |
@Environment.systemField: #CLIENT mandt:mandt, | |
@Environment.systemField: #SYSTEM_LANGUAGE sy_langu:langu | |
returns { | |
mandt:mandt; | |
country:land1; | |
text:landx50; | |
} | |
implemented by method zjb_cl_country=>get_country_text; | |
Listing 8.9: Beispiel für die Definition einer CDS-Tabellenfunktion | |
CLASS zjb_cl_country DEFINITION PUBLIC. | |
PUBLIC SECTION. | |
INTERFACES if_amdp_marker_hdb. | |
CLASS-METHODS get_country_text | |
FOR TABLE FUNCTION z_country_text. | |
ENDCLASS. | |
CLASS zjb_cl_country IMPLEMENTATION. | |
METHOD get_country_text BY DATABASE FUNCTION | |
FOR HDB LANGUAGE SQLSCRIPT | |
OPTIONS READ-ONLY | |
USING t005t. | |
RETURN SELECT mandt, | |
land1 AS country, | |
landx50 AS text | |
FROM t005t | |
WHERE spras = :sy_langu | |
AND mandt = :mandt; | |
ENDMETHOD. | |
ENDCLASS. | |
Listing 8.10: Beispiel für die Implementierung einer CDS-Tabellenfunktion | |
REPORT zjb_test. | |
SELECT * | |
FROM z_country_text | |
INTO TABLE @DATA(lt_country) | |
##db_feature_mode[amdp_table_function]. | |
cl_demo_output=>display( lt_country ). | |
Listing 8.11: ABAP-Programm zum Test der CDS-Tabellenfunktion aus dem Beispiel | |
CREATE FUNCTION | |
"ZJB_CL_COUNTRY=>GET_COUNTRY_TEXT" | |
( | |
"MANDT" NVARCHAR (000003), | |
"SY_LANGU" NVARCHAR (000001) | |
) | |
RETURNS TABLE | |
( | |
"MANDT" NVARCHAR (000003) , | |
"COUNTRY" NVARCHAR (000003) , | |
"TEXT" NVARCHAR (000050) | |
) | |
LANGUAGE sqlscript SQL SECURITY INVOKER AS BEGIN | |
RETURN SELECT mandt, | |
land1 AS country, | |
landx50 AS text | |
FROM "ZJB_CL_COUNTRY=>T005T#covw" | |
WHERE spras = :SY_LANGU | |
AND mandt = :MANDT; | |
END; | |
Listing 8.13: Generierte UDF-Funktion zu der AMDP-Funktion | |
CLASS zcl_amdp_func DEFINITION PUBLIC. | |
PUBLIC SECTION. | |
TYPES: BEGIN OF ty_s_country, | |
mandt TYPE mandt, | |
country TYPE land1, | |
text TYPE landx50, | |
END OF ty_s_country. | |
TYPES ty_t_country TYPE STANDARD TABLE OF ty_s_country | |
WITH DEFAULT KEY. | |
INTERFACES if_amdp_marker_hdb. | |
METHODS test_amdp_table_function | |
IMPORTING VALUE(iv_langu) TYPE langu | |
VALUE(iv_mandt) TYPE mandt | |
EXPORTING VALUE(et_country) TYPE ty_t_country. | |
PRIVATE SECTION. | |
METHODS get_country_text | |
IMPORTING VALUE(iv_langu) TYPE langu | |
VALUE(iv_mandt) TYPE mandt | |
RETURNING VALUE(rt_country) TYPE ty_t_country. | |
ENDCLASS. | |
CLASS zcl_amdp_func IMPLEMENTATION. | |
METHOD test_amdp_table_function BY DATABASE PROCEDURE | |
FOR HDB LANGUAGE SQLSCRIPT | |
OPTIONS READ-ONLY | |
USING zcl_amdp_func=>get_country_text. | |
et_country = SELECT * | |
FROM "ZCL_AMDP_FUNC=>GET_COUNTRY_TEXT" | |
( iv_langu => :iv_langu, | |
iv_mandt => :iv_mandt); | |
ENDMETHOD. | |
METHOD get_country_text BY DATABASE FUNCTION | |
FOR HDB LANGUAGE SQLSCRIPT | |
OPTIONS READ-ONLY | |
USING t005t. | |
RETURN SELECT mandt, | |
land1 AS country, | |
landx50 AS text | |
FROM t005t | |
WHERE spras = :iv_langu | |
AND mandt = :iv_mandt; | |
ENDMETHOD. | |
ENDCLASS. | |
Listing 8.14: Beispiel für die Verwendung einer AMDP-Funktion in einer anderem AMDP-Methode | |
CLASS zjb_bw_tools_amdp DEFINITION | |
PUBLIC | |
FINAL | |
CREATE PUBLIC . | |
PUBLIC SECTION. | |
INTERFACES if_amdp_marker_hdb. | |
METHODS replace_unallowed_characters | |
IMPORTING VALUE(iv_input) TYPE rschavl60 | |
RETURNING VALUE(rv_output) TYPE rschavl60. | |
ENDCLASS. | |
CLASS zjb_bw_tools_amdp IMPLEMENTATION. | |
METHOD replace_unallowed_characters BY DATABASE FUNCTION | |
FOR HDB LANGUAGE SQLSCRIPT | |
OPTIONS READ-ONLY. | |
rv_output = CASE WHEN LEFT( :iv_input, 1 ) = '!' | |
THEN replace( iv_input ,'!' , '$') | |
WHEN :iv_input = '#' | |
THEN '' | |
ELSE :iv_input | |
END; | |
rv_output = replace_regexpr( '[[:cntrl:]]' | |
IN :rv_output WITH '' ); | |
rv_output = replace( :rv_output, nchar( '0130'), ''); | |
ENDMETHOD. | |
ENDCLASS. | |
Listing 8.15: Beispiel für eine skalare AMDP-Funktion | |
outtab = SELECT ... | |
LPAD( ROW_NUMBER() OVER (ORDER BY <Sortierung>), 56, '0') | |
AS "RECORD", | |
SQL__PROCEDURE__SOURCE__RECORD | |
FROM :intab ; | |
Listing 9.1: Vergabe einer laufenden Nummer für das Feld RECORD für die Sortierung | |
METHOD procedure BY DATABASE PROCEDURE | |
FOR HDB | |
LANGUAGE SQLSCRIPT | |
OPTIONS READ-ONLY. | |
OUTTAB = SELECT * FROM :INTAB; | |
ERRORTAB = SELECT '' AS ERROR_TEXT, | |
'' AS SQL__PROCEDURE__SOURCE__RECORD | |
FROM dummy | |
WHERE dummy <> 'X'; | |
ENDMETHOD. | |
Listing 9.2: Minimale Implementierung einer Endroutine | |
METHOD procedure BY DATABASE PROCEDURE | |
FOR HDB | |
LANGUAGE SQLSCRIPT | |
OPTIONS READ-ONLY | |
USING /bic/AZJB_TEST2. | |
OUTTAB = SELECT | |
... | |
FROM :INTAB AS T1 | |
LEFT OUTER JOIN "/BIC/AZJB_TEST2" AS T2 | |
ON | |
... ; | |
ERRORTAB = SELECT * FROM :ERRORTAB; | |
ENDMETHOD. | |
Listing 9.3: USING-Klausel in AMDP-Routinen | |
outtab = SELECT ... | |
COALESCE( pplant.plantcat, ' ' ) AS plantcat, | |
it.record , | |
it.sql__procedure__source__record | |
FROM :intab as it | |
LEFT OUTER JOIN "/BI0/PPLANT" AS pplant | |
ON it.plant = pplant.plant | |
Listing 9.4: Absicherung gegen NULL-Werte mit COALESCE | |
METHOD GLOBAL_EXPERT BY DATABASE PROCEDURE FOR HDB LANGUAGE SQLSCRIPT | |
OPTIONS READ-ONLY. | |
outtab = SELECT * FROM :outtab; | |
errortab = SELECT * FROM :errortab; | |
ENDMETHOD. | |
Listing 9.5: Dummy-Implementierung einer Expertenroutine | |
METHOD GLOBAL_EXPERT BY DATABASE PROCEDURE | |
FOR HDB LANGUAGE SQLSCRIPT OPTIONS READ-ONLY. | |
lt_tmp = SELECT *, | |
fiscyear || '001' AS fiscper, | |
amount_01 AS amount | |
FROM :intab | |
UNION ALL | |
SELECT *, | |
fiscyear || '002' AS fiscper, | |
amount_02 AS amount | |
FROM :intab | |
UNION ALL | |
<… Für alle Perioden 1-12 einen SELECT> | |
UNION ALL | |
SELECT *, | |
fiscyear || '012' AS fiscper, | |
amount_12 AS amount | |
FROM :intab ; | |
--Ausgangsprojektion, um die Felder in die richtige Reihenfolge | |
-- zu bringen und FISCYEAR und AMOUNT_XX-Felder zu entfernen. | |
outtab = SELECT COMPCODE, | |
ACCOUNT, | |
CURR, | |
FISCPER, | |
RECORDMODE, | |
AMOUNT, | |
RECORD, | |
SQL__PROCEDURE__SOURCE__RECORD | |
FROM :lt_tmp; | |
ENDMETHOD. | |
Listing 9.6: Expertenroutine zum Transponieren von Kennzahlen | |
errortab = SELECT 'Währung ist leer!' AS ERROR_TEXT, | |
SQL__PROCEDURE__SOURCE__RECORD | |
FROM :intab | |
WHERE currency = ' ' | |
AND amount <> 0; | |
Listing 9.7: Einfache Fehlerermittlung aus den Eingangsdaten der Tabelle INTAB | |
outtab = SELECT it.plant, | |
pplant.plantcat | |
FROM :intab AS it | |
LEFT OUTER JOIN "/BI0/PPLANT" AS pplant | |
ON it.plant = pplant.plant; | |
ERRORTAB = SELECT 'Fehler' AS error_text, | |
SQL__PROCEDURE__SOURCE__RECORD | |
FROM :outtab | |
WHERE plant = ' ' --Fehler aus INTAB | |
OR plantcat IS NULL --Join erfolglos | |
Listing 9.8: Fehlerermittlung aus der OUTTAB | |
outtab = SELECT * FROM :intab; | |
errortab = SELECT 'Feldinhalt prüfen!' AS ERROR_TEXT, | |
SQL__PROCEDURE__SOURCE__RECORD | |
FROM :intab | |
WHERE text LIKE_REGEXPR '.*[[cntrl]].*' | |
OR text LIKE '!%' | |
OR text = '#'; | |
Listing 9.9: Prüfung auf Steuerzeichen | |
SELECT id, | |
-- Name formatieren | |
nachname || ', ' || vorname AS name, | |
-- Symbol für das Geschlecht | |
CASE | |
WHEN geschlecht = 'F' | |
THEN NCHAR('9792') | |
WHEN geschlecht = 'M' | |
THEN NCHAR('9794') | |
ELSE '' | |
END AS MW, | |
-- Text für das Team hinzufügen | |
COALESCE(( | |
SELECT team_text | |
FROM team_text | |
WHERE id = team | |
AND sprache = 'DE' | |
), ( | |
SELECT team_text | |
FROM team_text | |
WHERE id = team | |
AND sprache = 'EN' | |
), '') AS team | |
FROM benutzer; | |
Listing 10.1: Beispiel für Komplexität in einfachen Abfragen | |
SELECT id, | |
udf_name_formatieren(vorname, nachname) AS name, | |
udf_symbol_fuer_geschlecht(geschlecht) AS mw, | |
(SELECT text FROM udf_team_text() WHERE id = team) | |
AS team | |
FROM benutzer ; | |
Listing 10.2: Die gleiche Abfrage mit ausgelagertem Coding | |
CREATE FUNCTION udf_name_formatieren( | |
iv_vorname NVARCHAR(20), | |
iv_nachname NVARCHAR(20) | |
) | |
RETURNS rv_name NVARCHAR(42) | |
AS BEGIN | |
rv_name = iv_nachname || ', ' || iv_vorname; | |
END; | |
CREATE FUNCTION udf_symbol_fuer_geschlecht( | |
iv_geschlecht NVARCHAR(1) | |
) | |
RETURNS rv_symbol NVARCHAR(1) | |
AS BEGIN | |
rv_symbol = CASE WHEN iv_geschlecht = 'F' | |
THEN NCHAR('9792') | |
WHEN iv_geschlecht = 'M' | |
THEN NCHAR('9794') | |
ELSE '' END; | |
END; | |
CREATE FUNCTION udf_team_text( ) | |
RETURNS TABLE(id INT, text NVARCHAR(20)) | |
AS BEGIN | |
lt_team_id = SELECT DISTINCT id FROM team_text; | |
RETURN | |
SELECT input.id, | |
COALESCE(de.team_text, | |
en.team_text, | |
'') AS text | |
FROM :lt_team_id AS input | |
LEFT OUTER JOIN team_text AS de | |
ON de.id = input.id | |
AND de.sprache = 'DE' | |
LEFT OUTER JOIN team_text AS en | |
ON en.id = input.id | |
AND en.sprache = 'EN'; | |
END; | |
Listing 10.3: Die drei Funktionen, die in Listing 10.2 verwendet werden | |
--Deklaration einer Variable lv_a vom Typ Integer: | |
DECLARE lv_a INT; | |
--Zuweisung des Wertes 0 zu der Variable lv_a | |
lv_a = 0; | |
Listing 10.4: Redundante Kommentare sind ohne Mehrwert | |
-- Ursprüngliche SELECT-Anweisung | |
SELECT projekt, | |
projekte.titel, | |
SUM(aufgaben.plan_aufwand) AS plan_aufwand, | |
SUM(aufgaben.ist_aufwand) AS ist_aufwand, | |
ROUND(SUM(aufgaben.ist_aufwand) | |
/SUM(aufgaben.plan_aufwand)*100,0) | |
|| '%' AS perc, | |
projekte.aufwandsschaetzung | |
FROM aufgaben | |
LEFT OUTER JOIN projekte | |
ON projekte.id=aufgaben.projekt | |
WHERE aufgaben.status IN (SELECT id | |
FROM status | |
WHERE is_final = true) | |
GROUP BY projekt, | |
projekte.titel, | |
projekte.aufwandsschaetzung | |
ORDER BY ROUND(SUM(aufgaben.ist_aufwand) | |
/SUM(aufgaben.plan_aufwand)*100,0); | |
-- Zerlegt in Schritte: | |
DO BEGIN | |
-- Eingangsprojektion STATUS | |
lt_status_in = | |
SELECT id | |
FROM status | |
WHERE is_final = true; | |
-- Eingangsprojektion AUFGABEN | |
lt_aufgaben_in = | |
SELECT projekt, | |
plan_aufwand, | |
ist_aufwand | |
FROM aufgaben | |
WHERE status in (SELECT * FROM :lt_status_in); | |
-- Aggregation der Aufgaben auf Projekte | |
lt_aufgaben_aggr = | |
SELECT projekt, | |
SUM(plan_aufwand) AS plan_aufwand, | |
SUM(ist_aufwand) AS ist_aufwand | |
FROM :lt_aufgaben_in | |
GROUP BY projekt; | |
-- Nachberechnung AUFGABEN | |
lt_aufgaben_calc = | |
SELECT *, | |
ROUND(ist_aufwand/plan_aufwand*100) AS perc | |
FROM :lt_aufgaben_aggr; | |
-- Eingangsprojektion PROJEKTE | |
lt_projekte_in = | |
SELECT id, | |
titel, | |
aufwandsschaetzung | |
FROM projekte; | |
-- JOIN-Schritt | |
lt_projekte_aufgaben = | |
SELECT aufgaben.*, | |
projekte.* | |
FROM :lt_aufgaben_calc AS aufgaben | |
LEFT OUTER JOIN :lt_projekte_in AS projekte | |
ON aufgaben.projekt = projekte.id | |
ORDER BY aufgaben.perc; | |
-- Ausgangsprojektion | |
SELECT projekt, | |
titel, | |
plan_aufwand, | |
ist_aufwand, | |
perc || '%' AS perc, | |
aufwandsschaetzung | |
FROM :lt_projekte_aufgaben; | |
END; | |
Listing 10.5: Beispiel für das Zerlegen einer Abfrage in mehrere Schritte | |
SELECT * | |
FROM farben groessen; | |
SELECT * | |
FROM farben, groessen; | |
Listing 10.6: Beispiel für Kurzformen in SQLScript | |
SELECT * | |
FROM farben AS groessen; | |
SELECT * | |
FROM farben CROSS JOIN groessen; | |
Listing 10.7: Das gleiche Beispiel ohne Kurzform | |
SET 'ABAPVARCHARMODE' = 'FALSE'; | |
SELECT 'Peter' || ' ' || 'Mueller' FROM dummy; | |
SET 'ABAPVARCHARMODE' = 'TRUE'; | |
SELECT 'Peter' || ' ' || 'Mueller' FROM dummy; | |
Listing 10.8: Code zum Testen | |
SELECT 'Peter' || CHAR(32) || 'Mueller' FROM dummy; | |
--Datenbankobjekte löschen | |
DROP TABLE test_quadrat; | |
DROP FUNCTION udf_quadrat; | |
--Funktion anlegen | |
CREATE FUNCTION udf_quadrat(IN iv_wert INT) | |
RETURNS rv_ergebnis INT | |
AS BEGIN | |
rv_ergebnis = iv_wert * iv_wert; | |
END; | |
--Testfälle anlegen | |
CREATE TABLE test_quadrat(wert INT, quadrat INT); | |
INSERT INTO test_quadrat VALUES (0,0); | |
INSERT INTO test_quadrat VALUES (1,1); | |
INSERT INTO test_quadrat VALUES (2,4); | |
INSERT INTO test_quadrat VALUES (-1,1); | |
INSERT INTO test_quadrat VALUES (-2,4); | |
--Testfälle ausführen | |
SELECT wert, | |
quadrat AS erwartungswert, | |
udf_quadrat(wert) AS ergebnis, | |
CASE WHEN udf_quadrat(wert) = quadrat | |
THEN 'OK' | |
ELSE 'Fehler' | |
END AS testergebnis | |
FROM test_quadrat; | |
Listing 11.1: Entwicklungszyklus in einem Skript | |
CREATE TABLE mylog LIKE sys.sqlscript_logging_table_type; | |
CREATE PROCEDURE do_something (IN iv_text nvarchar(15) ) | |
READS SQL DATA | |
AS BEGIN | |
USING sqlscript_logging AS logger; | |
logger:log(logger:level_debug, | |
'c1', | |
'Procedure parameter: ' || :iv_text ); | |
END; | |
CREATE TABLE mylog LIKE sys.sqlscript_logging_table_type; | |
DO BEGIN | |
USING sqlscript_logging AS logger; | |
logger:create_configuration('c1'); | |
logger:add_sqlscript_object('c1', | |
current_schema, | |
'DO_SOMETHING'); | |
logger:set_output_table('c1', | |
current_schema, | |
'MYLOG'); | |
logger:set_level('c1', 'debug'); | |
logger:start_logging('c1'); | |
do_something( 'Hello Log!' ); | |
END; | |
SELECT * FROM mylog; | |
Listing 11.2: Beispiel für die Nutzung der Logging-Bibliothek | |
CREATE LIBRARY test_demo_dm | |
LANGUAGE SQLSCRIPT TEST | |
AS BEGIN | |
@test() | |
PUBLIC PROCEDURE t_aufgaben_cnt | |
AS BEGIN | |
USING sqlscript_test AS test; | |
test:expect_eq((SELECT COUNT(*) FROM aufgaben), | |
1000 ); | |
END; | |
END; | |
Listing 11.3: Testbibliothek | |
CALL SQLSCRIPT_RUN_TESTS_ON_ORIGINAL_DATA('{"schema":"SYSTEM", | |
"library":"TEST_DEMO_DM"}', ?, ?, ?); | |
CREATE LIBRARY TEST_DEMO_DM | |
LANGUAGE SQLSCRIPT TEST | |
AS BEGIN | |
PRIVATE PROCEDURE check_table_count | |
(IN iv_tabname NVARCHAR(30), | |
IN iv_expected_count INTEGER) | |
AS BEGIN | |
USING sqlscript_test AS test; | |
DECLARE lv_query NVARCHAR(5000); | |
DECLARE lv_count INTEGER; | |
lv_query = 'SELECT COUNT(*) FROM ' || :iv_tabname; | |
EXEC lv_query INTO lv_count; | |
test:expect_eq( :lv_count, :iv_expected_count ); | |
END; | |
@test() | |
PUBLIC PROCEDURE t_table_count | |
AS BEGIN | |
check_table_count( 'AUFGABEN', 1000); | |
check_table_count( 'BENUTZER', 30); | |
check_table_count( 'STATUS', 6); | |
check_table_count( 'STATUS_TEXT', 12); | |
--check_table_count( 'STATUS_TEXT', 13); | |
END; | |
END; | |
Listing 11.4: Auslagern der Tests in eine private Prozedur | |
DROP PROCEDURE A;DROP PROCEDURE B; | |
CREATE PROCEDURE A (IN iv_id INT, OUT ot_benutzer benutzer) | |
AS BEGIN | |
ot_benutzer = SELECT * | |
FROM benutzer | |
WHERE id >= :iv_id | |
AND id <= :iv_id + 5; | |
END; | |
CREATE PROCEDURE B (OUT ot_benutzer benutzer) | |
AS BEGIN | |
CALL A(iv_id=>'1', ot_benutzer=>lt_tmp1); | |
CALL A(iv_id=>'20', ot_benutzer=>lt_tmp20); | |
ot_benutzer = SELECT * FROM :lt_tmp1 | |
UNION | |
SELECT * FROM :lt_tmp20; | |
END; | |
CALL B(?); | |
Listing 11.5: Zwei Prozeduren zum Debuggen | |
SELECT a.id AS aufgabe, | |
a.titel, | |
b.vorname, | |
b.nachname, | |
b.email, | |
t.team_text | |
FROM aufgaben AS a | |
LEFT OUTER JOIN benutzer AS b | |
ON a.bearbeiter = b.id | |
LEFT OUTER JOIN team_text AS t | |
ON b.team = t.id; | |
Listing 11.6: Einfache SQL-Abfrage für die Performanceanalyse | |
EXPLAIN PLAN FOR | |
SELECT a.id AS aufgabe, | |
a.titel, | |
b.vorname, | |
b.nachname, | |
b.email, | |
t.team_text | |
FROM aufgaben AS a | |
LEFT OUTER JOIN benutzer AS b | |
ON a.bearbeiter = b.id | |
LEFT OUTER JOIN team_text AS t | |
ON b.team = t.id; | |
SELECT * FROM explain_plan_table; | |
DELETE FROM explain_plan_table; | |
Listing 11.7: Beispiel für die Verwendung der Anweisung EXPLAIN PLAN | |
SELECT * FROM sys.sqlscript_analyzer_rules; | |
DO BEGIN | |
lt_rules = | |
SELECT rule_namespace, | |
rule_name, | |
category | |
FROM sqlscript_analyzer_rules; | |
lt_objects = | |
SELECT schema_name , | |
procedure_name AS object_name, | |
definition | |
FROM procedures | |
WHERE schema_name LIKE 'SHINE%'; | |
CALL analyze_sqlscript_objects( :lt_objects, | |
:lt_rules, | |
lt_result_objects, | |
lt_result_findings ); | |
SELECT objects.*, | |
findings.* | |
FROM :lt_result_objects AS objects | |
INNER JOIN :lt_result_findings AS findings | |
ON objects.object_definition_id | |
= findings.object_definition_id; | |
END; | |
Listing 11.8: Aufruf des SQLScript Code Analyzers für bestehende Prozeduren | |
DO BEGIN | |
lt_rules = | |
SELECT rule_namespace, | |
rule_name, | |
category | |
FROM sqlscript_analyzer_rules; | |
CALL analyze_sqlscript_definition( | |
' | |
CREATE procedure check_me(IN iv_value INT, | |
OUT ov_value INT) | |
AS BEGIN | |
DECLARE lv_unused VARCHAR(3) default '' ''; | |
ov_value = iv_value; | |
END; | |
', | |
:lt_rules, | |
lt_result_findings ); | |
SELECT * FROM :lt_result_findings; | |
END; | |
Listing 11.9: Aufruf des SQLScript Code Analyzers für eine neu anzulegende Prozedur | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment