Scheinbar einfaches Problem in Oracle

Hallo,

ich habe ein Problem in Oracle 8, bei dem ich mir eine View zusammenstellen möchte. Nachdem ich 3 Tage ohne brauchbares Ergebnis
herumgebastelt habe, wende ich mich nun entnervt an die absoluten Profis.

Das Problem lässt sich folgendermaßen beschreiben:

Tabelle1

Kunde
Hausanschlussnr.
Ort
usw.

Tabelle2

Hausanschlussnr.
Zaehlernummer
Datum
Stand

Wobei in Tabelle1 jeder Kunde n ID Sätze haben kann. Über die ID werden die Tabellen verbunden.

In Tabelle2 können zu jeder ID 1-n Zähler und für jeden Zähler 1-n Stände existieren.

So, jetzt kommt’s :

Ich benötige zu jedem Kunden seine Zähler und zu jedem Zähler den Stand zum JÜNGSTEN Datum.
Immerhin: das Datum ist eindeutig.

Bisher behelfe ich mir mit folgendem Kommando (verkürzt), dass ich allerdings nicht als View
verwenden kann (zumindest wüsste ich nicht wie).

Leider geht meine gesamte Strukturierung verloren:

CURSOR cKunden IS
SELECT ALL
H.KUND_KUNDEN_NR, H.KUND_NAME1,
H.KUND_NAME2, H.ORT_O_NAME,
H.STRA_ST_NAME, H.HAUS_ZUSATZHAUSNUMMER,
H.HAUS_HAUSNUMMER, Z.INDART_NUMMER1,
Z.INDART_NUMMER, Z.ZSTAND_ZAEHLERSTAND,
TO_CHAR(Z.ZSTAND_DATUM,‚YYYY-MM-DD‘) ZDATUM,
TO_CHAR(Z.ZSTAND_ERFASSUNGSTAG,‚YYYY-MM-DD‘) EDATUM,
H.HAUS_WOHNUNGSNUMMER
FROM VADA_ZAEHLERSTAENDE Z, HAUSANSCHLUESSE_KUNDEN H
WHERE H.KUND_KUNDE_STILLGELEGT_AM IS NULL
AND (Z.INDART_AKTUELL_IN_HAUSANSCHL=H.HAUS_HAUSANSCHLUSSNUMMER)
ORDER BY H.KUND_KUNDEN_NR, Z.INDART_NUMMER, Z.ZSTAND_DATUM;

BEGIN
FOR c1 in cKunden LOOP
R_COUNT := R_COUNT + 1;

IF ((c1.KUND_KUNDEN_NR != S_KDNR) OR
(c1.INDART_NUMMER != S_SZNR)) AND
(R_COUNT > 1)
THEN
INSERT INTO t_luen_webze
(T_KDNR, T_NAME1, T_NAME2, T_ORT, T_STRASSE,
T_HSNR, T_ZHSNR, T_WOHNNR, T_PZNR, T_SZNR,
T_ZDATUM, T_ZSTAND, T_ZDATUM1)
VALUES
(S_KDNR, S_NAME1, S_NAME2, S_ORT, S_STRASSE,
S_HSNR, S_ZHSNR, S_WOHNNR, S_PZNR, S_SZNR,
S_ZDATUM, S_ZSTAND, S_ZDATUM1);
END IF;

S_KDNR := c1.KUND_KUNDEN_NR;
S_NAME1 := c1.KUND_NAME1;
S_NAME2 := c1.KUND_NAME2;
S_ORT := c1.ORT_O_NAME;
S_STRASSE := c1.STRA_ST_NAME;
S_HSNR := c1.HAUS_HAUSNUMMER;
S_ZHSNR := c1.HAUS_ZUSATZHAUSNUMMER;
S_WOHNNR := c1.HAUS_WOHNUNGSNUMMER;
S_PZNR := c1.INDART_NUMMER1;
S_SZNR := c1.INDART_NUMMER;
S_ZSTAND := c1.ZSTAND_ZAEHLERSTAND;
S_ZDATUM := c1.ZDATUM;
S_ZDATUM1 := c1.EDATUM;
END LOOP;

INSERT INTO t_luen_webze
(T_KDNR, T_NAME1, T_NAME2, T_ORT, T_STRASSE, T_HSNR, T_ZHSNR,
T_WOHNNR, T_PZNR, T_SZNR, T_ZDATUM, T_ZSTAND, T_ZDATUM1)
VALUES
(S_KDNR, S_NAME1, S_NAME2, S_ORT, S_STRASSE, S_HSNR, S_ZHSNR,
S_WOHNNR, S_PZNR, S_SZNR, S_ZDATUM, S_ZSTAND, S_ZDATUM1);
END;
/

Der folgende Select ist zwar ein guter Ansatz, scheitert aber daran, dass ich die Zählerstände in die GROUP BY Klausel aufnehmen müsste, was aber wieder zu zu vielen Zeilen (alle Daten/Zählerstände) pro Kunde/Zähler führt.

SELECT
HAUSANSCHLUESSE_KUNDEN.KUND_KUNDEN_NR, HAUSANSCHLUESSE_KUNDEN.KUND_NAME1,
HAUSANSCHLUESSE_KUNDEN.KUND_NAME2, HAUSANSCHLUESSE_KUNDEN.ORT_O_NAME,
HAUSANSCHLUESSE_KUNDEN.STRA_ST_NAME, HAUSANSCHLUESSE_KUNDEN.HAUS_HAUSNUMMER,
HAUSANSCHLUESSE_KUNDEN.HAUS_ZUSATZHAUSNUMMER, HAUSANSCHLUESSE_KUNDEN.HAUS_WOHNUNGSNUMMER,
VADA_ZAEHLERSTAENDE.INDART_NUMMER1, VADA_ZAEHLERSTAENDE.INDART_NUMMER,
MAX(VADA_ZAEHLERSTAENDE.ZSTAND_DATUM),
VADA_ZAEHLERSTAENDE.ZSTAND_ZAEHLERSTAND,
MAX(VADA_ZAEHLERSTAENDE.ZSTAND_ERFASSUNGSTAG)
FROM VADA_ZAEHLERSTAENDE, HAUSANSCHLUESSE_KUNDEN
WHERE HAUSANSCHLUESSE_KUNDEN.KUND_KUNDE_STILLGELEGT_AM IS NULL
AND (VADA_ZAEHLERSTAENDE.INDART_AKTUELL_IN_HAUSANSCHL=HAUSANSCHLUESSE_KUNDEN.HAUS_HAUSANSCHLUSSNUMMER)
GROUP BY
HAUSANSCHLUESSE_KUNDEN.KUND_KUNDEN_NR, HAUSANSCHLUESSE_KUNDEN.KUND_NAME1,
HAUSANSCHLUESSE_KUNDEN.KUND_NAME2, HAUSANSCHLUESSE_KUNDEN.ORT_O_NAME,
HAUSANSCHLUESSE_KUNDEN.STRA_ST_NAME, HAUSANSCHLUESSE_KUNDEN.HAUS_ZUSATZHAUSNUMMER,
HAUSANSCHLUESSE_KUNDEN.HAUS_HAUSNUMMER, HAUSANSCHLUESSE_KUNDEN.HAUS_WOHNUNGSNUMMER,
VADA_ZAEHLERSTAENDE.INDART_NUMMER1,
VADA_ZAEHLERSTAENDE.INDART_NUMMER

Zugegeben, es dürfte sich hierbei um eine Sache für Profis handeln.
Bin gespannt ob mir jemand helfen kann.

MfG,
Sven

Ich abstrahiere dein Problem einmal etwas, da mir deine Tabellenstruktur nicht so richtig normalisiert zu sein scheint.

Gegeben eine Tabelle (verknüpfst Du deine per Join, dann ist das eine Tabelle) mit Spalten Zaehler_Id, Datum, Stand. Gesucht: Zu jeder Zaehler_Id der Stand zum letzten Datum.

Select A.Zaehler_Id, B.Datum, B.Stand
From
(SELECT X.Zaehler_Id, Max(X.Datum) As Letztes_Datum
FROM Zaehler AS X Group By Zaehler_Id) As A Inner Join Zaehler As B
ON A.Zaehler_Id = B.Zaehler_Id And A.Letztes_Datum = B.Datum

Die innere Abfrage sammelt zu jeder Zaehler_Id das letzte Datum, vorausgesetzt, daß pro Datum höchstens ein Zählerstand vorliegt. Dann gibt es eine Selbstverknüpfung auf dieselbe Tabelle, die nur die passenden Zeilen liefert und von denen den Zählerstand ausgibt.

Gruß, Jürgen Auer

hi!

wie wär’s mit einem sub-select?

das statement vereinfacht ausgedrückt:

create view staende as
select a.kunde, a.hausanschlussnr, a.blabla, b.zaehlernummer,
b.datum,b.stand
from tabelle1 a, tabelle2 b
where a.hausanschlussnr = b.hausanschlussnr
and b.datum = (select max(c.datum) from tabelle2 c
where c.hausanschlussnr = b.hausanschlussnr);

oder sehe ich das alles zu einfach?

grüße,
tomh

Hallo Jürgen,

danke erstmal für Deine Antwort. Ich SPÜRE, dass eine Lösung darin liegt, kann Deine abstrahierte Lösung aber nicht auf mein Problem übertragen - irgendwie verstehe ich es noch nicht ganz.

Ich versuche es mal auf einem anderen Weg. Das folgende Statement liefert mir ALLE Kunden mit ALLEN zugehörigen Zählern und zu diesen jeweils wieder die Zählerstände zu JEDEM Datum.

SELECT
HA.KUND_KUNDEN_NR, HA.KUND_NAME1,
HA.KUND_NAME2, HA.ORT_O_NAME,
HA.STRA_ST_NAME, HA.HAUS_HAUSNUMMER,
HA.HAUS_ZUSATZHAUSNUMMER, HA.HAUS_WOHNUNGSNUMMER,
Z.INDART_NUMMER1, Z.INDART_NUMMER,
Z.ZSTAND_DATUM,
Z.ZSTAND_ZAEHLERSTAND,
Z.ZSTAND_ERFASSUNGSTAG
FROM VADA_ZAEHLERSTAENDE Z, HAUSANSCHLUESSE_KUNDEN HA
WHERE HA.KUND_KUNDE_STILLGELEGT_AM IS NULL
AND Z.INDART_AKTUELL_IN_HAUSANSCHL=HA.HAUS_HAUSANSCHLUSSNUMMER;

Die Zähler werden über Z.INDART_NUMMER und Z.INDART_NUMMER1 zusammen sicher identifiziert (z.B. Zähler BR0815 - BR = INDART_NUMMER, 0815 = INDART_NUMMER1).

Ziel ist es, aus der Treffermenge die Sätze rauszufischen, die für die Zählernummer Z.INDART_NUMMER + Z.INDART_NUMMER1 jeweils das höchste Erfassungsdatum Z.ZSTAND_DATUM haben.

Könntest Du mir den Inner Join anhand des vorliegenden Beispiels noch zu erklären versuchen? Wäre extrem nett.

Schöne Grüße,
Sven Lünenbach

Die Schwierigkeit bei deiner Version ist zunächst, daß die Tabellen nicht vollständig normalisiert sind. Es gibt drei Einheiten - Kunden, Häuser und Zähler, eigentlich genügt es, Kunden zu Zählern und Zähler zu Häusern zuzuordnen, für Zähler hat man dann nur eine ID.

Dann verwendest Du die eigentlich längst veraltete Form, einen Join in eine Komma-Liste aufzulösen und die Join-Bedingungen als nicht mehr optimierbare Where-Verknüpfungen zu behandeln. Zieht man die Verknüpfung als Join heraus, kann der Optimierer zusätzliche Where-Bedingungen verwenden, um die Tabellen vor der Verknüpfung zu verkleinern.

Die innere Abfrage von mir ermittelt einfach zu jedem Zähler das letzte Datum - ob der Zähler nun durch einen oder zwei Schlüssel eindeutig beschrieben ist, spielt keine Rolle.

Ob man diese nun per Join oder der alten Where-Form mit der detaillierten Abfrage kombiniert, ist ebenfalls eher nebensächlich. Hat man innen zwei Primärschlüssel, muß man diese halt mit der anderen Tabelle in Form von zwei Bedingungen, insgesamt damit drei (+ Datum), kombinieren.


Gruß, Jürgen Auer

Hallo,

die Tabellen scheinen nicht normalisiert, da es sich bereits um VIEWs handelt, die ihrerseits einige Tabellen miteinander verknüpfen. Da ich auf eine (undokumentierte) DB einer Fremdfirma zugreife mit >4500 Tabellen zugreife, musste ich diesen Weg gehen, um überhaupt mal an eine Datenmenge zu kommen, mit der ich etwas anfangen kann. Auf die Originaltabellen zuzugreifen wäre (für mich) zu komplex.

Wenn ich Deinen JOIN hinter der FROM Anweisung einfüge, dann erhalte ich im Tool T.O.A.D. die Meldung „rechte Klammer fehlt“, obwohl alle Klammern ordnungsgemäß geschlossen sind. Oracle scheint mit dem Komamndo in dieser Form nicht klar zu kommen. Kann es sein, dass Du normalerweise auf anderen DBs arbeitest? Ich müsste mir dann mal eine „Übersetzung“ besorgen.

Vielen Dank für Deine Mühe und

schöne Grüße
Sven Lünenbach

Wenn Access 97 und MSSQL damit klarkommen, dann wird Oracle damit auch klarkommen.

Test: Select A.Spalte1, B.Spalte2 From
(Select Spalte1, Spalte2, Spalte3 From Tabelle As X) As A
Inner Join (Select Spalte1, Spalte2, Spalte3 From Tabelle As X) As B
ON A.Spalte1 = B.Spalte1

Spalte1 kann bsp. ein Primärschlüssel sein

Einfach anstatt einer Tabelle einen Select-Ausdruck in Klammern mit Aliasnamen.


Gruß, Jürgen Auer