Befüllen und Abfragen einer Entscheidungstabelle mit Defaultwerten und Ausnahmen. Dieser Artikel basiert auf einem OTN-Post:
Im Laufe der Zeit entwickelte sich Code, der für Entscheidungen im Programm zuständig ist, zu einem Wust aus mehrfach geschachtelten IF Anweisungen. Wir entschieden uns dazu es als Entscheidungstabelle zu implementieren.
Weil es mehr als 20.000 mögliche Kombinationen an Attributen gibt, wäre eine Tabelle, die alle Ausprägungen enthält nicht wartbar.
Hier ein vereinfachtes Beispiel mit Kunden, die in verschiedenen Regionen sind, ein Attrinut kann Y oder N sein und ein Attribut ist von anderen Eigenschaften abgeleitet und es gibt keine Tabelle, die alle möglichen Werte enthält; natürlich sollte es dafür eine Tabelle geben, aber das ist eine andere Geschichte.
Die Tabelle ist hierarchisch aufgebaut, d.h. dass jede Spalte mehr Gewicht hat als die rechts davon liegenden.
Das Ergebnis ist entweder Y (YES) oder N (NO)
DROP VIEW ranked_decisions; DROP TABLE decisions; DROP TABLE clients; DROP TABLE regions; CREATE TABLE clients ( client_id INTEGER NOT NULL ,client_name VARCHAR2 (100) ) ; ALTER TABLE clients ADD CONSTRAINT cli_pk PRIMARY KEY ( client_id ) ; CREATE TABLE regions ( region_id INTEGER NOT NULL ,region_name VARCHAR2 (100) ) ; ALTER TABLE regions ADD CONSTRAINT reg_pk PRIMARY KEY ( region_id ) ; CREATE TABLE decisions ( client_id INTEGER NOT NULL ,region_id INTEGER ,dec_yn_1 VARCHAR2 (1 CHAR) ,dec_attr_1 VARCHAR2 (10 CHAR) ,dec_result VARCHAR2 (1 CHAR) ) ; ALTER TABLE decisions ADD CONSTRAINT dec_cli_fk FOREIGN KEY ( client_id ) REFERENCES clients ( client_id ) ; ALTER TABLE decisions ADD CONSTRAINT dec_reg_fk FOREIGN KEY ( region_id ) REFERENCES regions ( region_id ) ; INSERT INTO clients (client_id,client_name) VALUES (1,'A'); INSERT INTO clients (client_id,client_name) VALUES (2,'B'); INSERT INTO clients (client_id,client_name) VALUES (3,'C'); INSERT INTO clients (client_id,client_name) VALUES (4,'D'); INSERT INTO clients (client_id,client_name) VALUES (5,'E'); INSERT INTO regions (region_id,region_name) VALUES (10,'BY'); INSERT INTO regions (region_id,region_name) VALUES (11,'BW'); INSERT INTO regions (region_id,region_name) VALUES (12,'HE'); INSERT INTO regions (region_id,region_name) VALUES (13,'TH'); INSERT INTO regions (region_id,region_name) VALUES (14,'HH'); --Default for each client INSERT INTO decisions (client_id,region_id,dec_yn_1,dec_attr_1,dec_result) VALUES ( 1, NULL, NULL, NULL, 'N'); INSERT INTO decisions (client_id,region_id,dec_yn_1,dec_attr_1,dec_result) VALUES ( 2, NULL, NULL, NULL, 'N'); INSERT INTO decisions (client_id,region_id,dec_yn_1,dec_attr_1,dec_result) VALUES ( 3, NULL, NULL, NULL, 'Y'); INSERT INTO decisions (client_id,region_id,dec_yn_1,dec_attr_1,dec_result) VALUES ( 4, NULL, NULL, NULL, 'N'); INSERT INTO decisions (client_id,region_id,dec_yn_1,dec_attr_1,dec_result) VALUES ( 5, NULL, NULL, NULL, 'Y');
CREATE OR REPLACE VIEW ranked_decisions AS WITH all_combinations AS ( --Generiere alle möglichen Kombinationen der Attribute SELECT cli.client_id ,reg.region_id ,yn.yn_1 ,attr.attr_1 ,dc.dec_result -- Wertung der Ergebnisse -- Gibt es ein Ergebnis mit dc.client_id <> NULL, wird ein Wert von 8 genommen -- Entsprechend für dc.region_id 4, dc.dec_yn_1 2 und für dc.dec_attr_1 1 -- Dadurch kann man sicherstellen, dass Kombinationen, die einen Treffer in der Tabelle haben, -- höher gewertet werden, als generierte Kombinationen ,ROW_NUMBER () OVER ( PARTITION BY cli.client_id,reg.region_id,yn.yn_1,attr.attr_1 ORDER BY CASE WHEN dc.client_id IS NULL THEN 0 ELSE 8 END + CASE WHEN dc.region_id IS NULL THEN 0 ELSE 4 END + CASE WHEN dc.dec_yn_1 IS NULL THEN 0 ELSE 2 END + CASE WHEN dc.dec_attr_1 IS NULL THEN 0 ELSE 1 END DESC ) r FROM clients cli CROSS JOIN regions reg CROSS JOIN ( SELECT 'Y' yn_1 FROM dual UNION ALL SELECT 'N' yn_1 FROM dual ) yn CROSS JOIN ( SELECT 'LA' attr_1 FROM dual UNION ALL SELECT 'M' attr_1 FROM dual UNION ALL SELECT 'SR' attr_1 FROM dual UNION ALL SELECT 'N' attr_1 FROM dual ) attr JOIN decisions dc ON ( (cli.client_id = dc.client_id OR dc.client_id IS NULL) AND (reg.region_id = dc.region_id OR dc.region_id IS NULL) AND (yn.yn_1 = dc.dec_yn_1 OR dc.dec_yn_1 IS NULL) AND (attr.attr_1 = dc.dec_attr_1 OR dc.dec_attr_1 IS NULL) ) ) SELECT ac.client_id ,ac.region_id ,ac.yn_1 ,ac.attr_1 ,ac.dec_result FROM all_combinations ac WHERE r = 1; SELECT dec_result FROM ranked_decisions WHERE client_id = 4 AND region_id = 14 AND yn_1 = 'N' AND attr_1 = 'LA';
Natürlich bekommen wir ein 'N'.
Fügen wir ein paar Ausnahmen hinzu
--Default für die Region HH ist Y für die Kunden A und D INSERT INTO decisions (client_id,region_id,dec_yn_1,dec_attr_1,dec_result) VALUES ( 1, 14, NULL, NULL, 'Y'); INSERT INTO decisions (client_id,region_id,dec_yn_1,dec_attr_1,dec_result) VALUES ( 4, 14, NULL, NULL, 'Y');
Jetzt bekommen wir für dieselbe Abfrage ein 'Y'
Weitere Ausnahmen könnten sein
-- Kunde A ist Y wenn die Region BY ist INSERT INTO decisions (client_id,region_id,dec_yn_1,dec_attr_1,dec_result) VALUES ( 1, 10, NULL, NULL, 'Y'); --außer wenn Attribut 1 = LA INSERT INTO decisions (client_id,region_id,dec_yn_1,dec_attr_1,dec_result) VALUES ( 1, 10, NULL, 'LA', 'N'); -- Kunde D ist Y wenn das YN-attribute Y ist INSERT INTO decisions (client_id,region_id,dec_yn_1,dec_attr_1,dec_result) VALUES ( 4, NULL, 'Y', NULL, 'Y');
Unsere Entscheidungstabelle hat nun folgende Daten
CLIENT_ID REGION_ID D DEC_ATTR_1 D ----------- ----------- - ---------- - 1 N 2 N 3 Y 4 N 5 Y 1 14 Y 4 14 Y 1 10 Y 1 10 LA N 4 Y Y