Update: Unterabfrage für eine Zeile liefert mehr als eine Zeile

joeenze

Neuer Benutzer
Beiträge
4
Hallo Zusammen.

Ich hab in SQL nur Grundkenntnisse welche ich mir selbst beigebracht habe. Deshalb mache ich wahrscheinlich Fehler, welche einen Profi nie passieren.

Mein Probleme ist ein Update einer Zelle, wo ich für den benötigten Wert über 5 Tabellen gehen muss. (Erste Unterabfrage). Die betroffenen Zellen muss ich ebenfalls über eine Unterabfrage ermitteln. Die Unterabfragen liefern eindeutige Werte. Trotzdem bekomme ich die Fehlermeldung: Unterabfrage für eine Zeile liefert mehr als eine Zeile
Wahrscheinlich mache ich bei dem Code grundsätzlich was falsch.

update tab1
set tab1.id5 =
(
select tab5.id5
from tab1, tab2, tab3, tab4, tab5
where tab1.id1 = tab2.id1
and tab2.id2 = tab3.id2
and tab3.id3 = tab4.id3
and tab4.id4 = tab5.id4
)
where tab1.id1 in
(
select tab1.id1
from tab1, tab2, tab3, tab6, tab7
where tab1.id1 = tab6.id1 (+)
and tab1.id1 = tab2.id1
and tab2.id2 = tab3.id2
and tab3.id3 = tab7.id3
and tab6.id1 is null
and tab1.id5 in ('WERT1','WERT2')
and tab1.id6 in ( 'WERT3','WERT4')
and (tab3.id7 / tab7.id7) < 10
)

Bitte um Hilfe. Vielen Dank.
 
Werbung:
Die Unterabfragen liefern eindeutige Werte.
Ohne weitere Details kann ich nur sagen: ganz offensichtlich nicht.

Ich vermute aber, Du willst in dem Sub-Select für das SET die Tabelle tab1 nicht wiederholen, sondern das als co-related Sub-query schreiben.

Code:
update tab1
set tab1.id5 =
(
select tab5.id5
from tab2, tab3, tab4, tab5
where tab1.id1 = tab2.id1
and tab2.id2 = tab3.id2
and tab3.id3 = tab4.id3
and tab4.id4 = tab5.id4
)

P.S.: Sogar Oracle empfiehlt auf den proprietären (+) Operator für outer joins zu verzichten und explizite LEFT JOIN zu verwenden - aber das ist für das Problem nicht relevant.
 
Hallo Castorp.

Deine Vermutung ist richtig
Die Basis für das Sub-Select für das SET ist tab1.id1. Mit diesen Wert komme ich über die 5 Tabellen zu den Zielwert. Dieser ist Eindeutig.
Deshalb verstehe ich die Fehlermeldung nicht.
Muss ich die tab1.id1 irgendwie extra mit dem Sub-Select verknüpfen?
 
Das Problem kann nach wie vor bestehen. Die Bedingung where tab1.id1 = tab2.id1 sorgt zwar dafür, das in dem Subselect nur Daten passend zum zu aktualisierenden Datensatz in tab1 ausgewählt werden, durch die ganzen Joins können aber mehrere Datensätze in tab2, tab3, tab4, tab5 diese Kriterien erfüllen. Das SET erwartet aber genau einen Wert aus dem Subselect, bekommt aber mehrere.

Die Unterabfragen liefern eindeutige Werte
müsste heißen
Die Unterabfragen liefert jeweils einen eindeutigen Wert pro Zeile in tab1
:-)

Ohne die Daten zu kennen kann man nicht genau wissen, was da zurück kommt. Ohne die Spalten- und Tabellennamen zu kennen, kann man sich auch nur schwer etwas vorstellen.
 
Man braucht nicht unbedingt Daten, ein Datenmodel mit den betroffenen Tabellen würde u.U. reichen.
Das Statement muss natürlich am Ende so gebaut sein, dass es immer funktioniert. Für 1:n Fremdschlüssel müssen also hinreichende Kriterien angegeben werden oder eine sichere Aggregation.
 
Vielleicht sollte man noch explizit sagen, dass es nicht um Eindeutigkeit allein geht, sondern darum einen einzigen Wert zurück zu bekommen.
Eine Handvoll Records, die alle 5 als Wert ausgeben, reicht nicht, auch wenn kein anderer Wert als 5 auftaucht.
 
Entschuldigung.
Das habe ich mir zu wenig genau angesehen.
Ich habe die tab1 nach dem from vom Sub-Select gelöscht.

Das ergibt jetzt eine neue Fehlermeldung.
ORA-02291: Integritäts-Constraint (Tabelle gibt es nicht?!?) verletzt - übergeordneter Schlüssel nicht gefunden
 
Also du solltest vielleicht etwas vorsichtiger sein mit dem Update.
Ein Update, das alle Datensätze ändert, (außer es tritt ein Fehler auf), ist vielleicht nicht gewollt?

Man würde wohl eine Bedingung hinzufügen, die prüft, ob alter und neuer Wert überhaupt unterschiedlich sind.

Integritäts Konstraints sind kernfeatures einer DB, das musst du dir anschauen! Eine fehlende Tabelle ergibt andere Fehlermeldungen.

Du versuchst einen Wert einzutragen, der nicht erlaubt ist. Die DB lehnt das ab.
 
Solange ich nicht Commite führt der SQL-Developer das Update nicht aus.

Ich habe den Fehler gefunden.
Der tab1.id5 besteht aus 2 eindeutigen Werten. Es gibt in tab5 einen zweiten Wert den wir zwar nicht verwenden, aber es gibt in.

Das funktioniert jetzt!

update tab1
set (tab1.id5, tab1.id6) =
(select tab5.id5, tab5.id6
from tab2, tab3, tab4, tab5
where tab1.id1 = tab2.id1
and tab2.id2 = tab3.id2
and tab3.id3 = tab4.id3
and tab4.id4 = tab5.id4
)
where tab1.id5 in
(select tab1.id1
from tab1, tab2, tab3, tab6, tab7
where tab1.id1 = tab6.id1 (+)
and tab1.id1 = tab2.id1
and tab2.id2 = tab3.id2
and tab3.id3 = tab7.id3
and tab6.id1 is null
and tab1.id5 in ('WERT1','WERT2')
and tab1.id6 in ( 'WERT3','WERT4')
and (tab3.id7 / tab7.id7) < 10
)

Auf das bin ich vorher nicht gekommen. Vielen Dank für die Hilfe.
 
Das Thema LEFT JOIN oder expliziter Join sollte man auch noch mal angehen. Ich kenne das mit dem + nicht aber gut leserlich finde ich das nicht. Das es hier knallt sobald mehr als ein Wert aus dem Subselect kommt ist vermutlich eher gut. Sonst vergisst man ein Kriterium und hat gleich die Daten geschrieben.

Bei MSSQL kann man auch einen Join direkt im Update machen, eventuell kann Oracle das auch. Aktuell hast du etwas das hier:

Beispiel:
Code:
UPDATE zieltabelle
SET zieltabelle.zielspalte = (
SELECT quelltabelle.quellspalte
FROM quelltabelle
WHERE quelltabelle.bedingung = zieltabelle.bedingung
)

In MSSQL könnte man das auch so formulieren:
Code:
UPDATE zieltabelle
SET zieltabelle.zielspalte = quelltabelle.quellspalte
FROM zieltabelle
INNER JOIN quelltabelle
ON quelltabelle.bedingung = zieltabelle.bedingung
 
Werbung:
Das UPDATE + FROM hat aber auch noch den Vorteil das man z.B. einfacher prüfen kann, ob überhaupt ein Unterschied vorliegt und man macht nicht einen Subselect per row. Es verhält sich auch anders, wenn mehr Werte aus dem Subselect kommen. Es gäbe dann mehrere Updates auf die selbe Zeile. Das ist eher schlecht, wenn man nicht so genau weiß, was man tut, aber es gibt keinen Fehler :-)
 
Zurück
Oben