Hallo Frank,
Habt ihr euch schon mal die DB-Abfragen angeschaut, die durch
eurer Datenmodell entstehen.
Natürlich habe ich das, ich verwende sowas ständig.
Da benötige ich doch tatsächlich 2 OUTERJOINS.
Outer Joins sind nicht böse.
Das mag bei 10
Datensätzen performant sein, aber bei 'ner Million, wartet man
doch schon mal ein paar Sekunden.
Nein.
Mag zwar sein, das euer Datenmodell das richtige ist… aber
performant ist was anderes… Durch den Null-Value in
dem einen Feld wird ein Full-Tablescan nötig.
Nein.
CREATE TABLE mar\_test1 AS (SELECT object\_id id FROM user\_objects);
DELETE FROM mar\_test1 WHERE id IS NULL;
ALTER TABLE mar\_test1 ADD CONSTRAINT pk1 PRIMARY KEY (id);
CREATE TABLE mar\_test2 AS (SELECT object\_id id FROM user\_objects);
DELETE FROM mar\_test2 WHERE id IS NULL;
ALTER TABLE mar\_test2 ADD CONSTRAINT pk2 PRIMARY KEY (id);
CREATE TABLE mar\_table
(id NUMBER(9) PRIMARY KEY,
fk1 NUMBER(9) REFERENCES mar\_test1 (id),
fk2 NUMBER(9) REFERENCES mar\_test2 (id),
CONSTRAINT ccFk CHECK
((fk1 IS NOT NULL AND fk2 IS NULL) OR
(fk1 IS NULL AND fk2 IS NOT NULL))
);
CREATE SEQUENCE mar\_seq;
INSERT INTO mar\_table
SELECT mar\_seq.NEXTVAL, id, NULL FROM mar\_test1;
INSERT INTO mar\_table
SELECT mar\_seq.NEXTVAL, NULL, id FROM mar\_test2;
ANALYZE TABLE mar\_table COMPUTE STATISTICS;
ANALYZE TABLE mar\_test1 COMPUTE STATISTICS;
ANALYZE TABLE mar\_test2 COMPUTE STATISTICS;
EXPLAIN PLAN FOR
SELECT \* FROM mar\_table a, mar\_test1 b, mar\_test2 c
WHERE a.fk1 = b.id(+) AND
a.fk2 = c.id(+) ORDER BY 1;
SELECT LPAD(' ',2\*(depth-1))||operation operation,
options,
object\_name
FROM plan\_table p
ORDER BY id;
Ergebnis:
OPERATION OPTIONS OBJECT\_NAME
SELECT STATEMENT
NESTED LOOPS OUTER
NESTED LOOPS OUTER
TABLE ACCESS BY INDEX ROWID MAR\_TABLE
INDEX FULL SCAN SYS\_C0089477
INDEX UNIQUE SCAN PK1
INDEX UNIQUE SCAN PK2
Da ist genau ein FTS drin (in dem Fall ein FIS, aber das können wir hier vernachlässigen), und den muss er machen, weil mar_table nicht eingeschränkt wurde. Wenn ich das etwas einschränke, z.B. so:
EXPLAIN PLAN FOR
SELECT \* FROM mar\_table a, mar\_test1 b, mar\_test2 c
WHERE a.fk1 = b.id(+) AND
a.fk2 = c.id(+) AND
a.id BETWEEN 100 AND 250;
dann sehe ich gar keinen FTS/FIS mehr:
SELECT STATEMENT
NESTED LOOPS OUTER
NESTED LOOPS OUTER
TABLE ACCESS BY INDEX ROWID MAR\_TABLE
INDEX RANGE SCAN SYS\_C0089477
INDEX UNIQUE SCAN PK1
INDEX UNIQUE SCAN PK2
Was spricht dagegen die Gebäude und Viertel in einer Tabelle
zu halten und nur durch einen Typ zu unterscheiden. Ggf falls
kann man den Typ in den Primärschlüssel aufnehmen(dann auch
FK).
- Weil es falsch ist. Gebäude und Viertel sind nicht das gleiche.
- Du musst Deinen Typ dann in allen Subtabellen mitführen, sonst kannst Du keine Foreign Keys erstellen.
- Weil es nicht intuitiv erfassbar ist. Jeder, der so ein Schema übernimmt braucht Wochen um zu verstehen, was das soll. Für den Anfang: Nenne mir einmal einen vernünftigen Namen für die Tabelle.
- Weil Composite Keys, die ein j/n Feld enthalten keinem Optimizer eine Freude machen.
- Weil es keine Vorteile hat.
Mir fallen noch ca. 50 Gründe ein, das nicht so zu machen, denke aber das reicht fürs erste.
Durch einen Selfjoin könne man die Beziehung zwischen Gebäude
und Viertel erzeugen.
Könnte man. Man könnte das aber auch richtig modellieren.
Gruß
Martin