Fehlermeldung bei Unterabfrage

Dieses Problem habe ich in den Anwendungen und Erweiterungen ->SQL geschubst…

Hallo,

ich würde gerne eine Unterabfrage erstellen. Die Funktion soll eine neue Spalte erstellen, die nur die Wasserstufe enthält mit den Zahlenwerten 7000-7500…

SQL-Abfrage:

UPDATE rd_os.neue_tab

SET wasserstufe = ( SELECT tab_2.kn3 FROM tab_2, tab_1 
                                        
                             WHERE tab_1.id = tab_2.rid 

                             AND tab_2.kn3 LIKE '%7%' 
                                                          
                             AND ST_Intersects(AsText(tab_1.position), ('POLYGON ((...))')));

Bei meiner Abfrage taucht folgende Fehlermeldung auf:

more than one row returned by a subquery used as an expression


Dieser Fehler tritt bei Anweisungen auf, bei denen die Unterabfrage mehr als einen Datensatz zurückgibt. Die Unterabfrage ist die eingebaute “SELECT-Abfrage”. Das funktioniert so leider nicht…


Kann mir jemand weiterhelfen?

Tja, ohne Deine Tabellen da zu kennen, …

Probier doch mal aus, was das Sub-Select liefert. Ganz sicherlich mehrere Zeilen. Welche davon willst Du denn da nehmen?

Und ja: ein UPDATE … ohne weitere bedingung, für welche Zeile das gelten soll, gilt für die ganze Tabelle. Möglicherweise willst Du das nicht.


Andreas

Gibt es außer ‘UPDATE’ noch eine Alternative?! Müsste mit den Werten später weiterarbeiten… das ist also kein Endresultat.

Und ja das Sub-Select liefert alle Zeilen, deren Werte eine “7” enthalten. So sollte es auch.

Kommt halt drauf an, ich kenne noch immer zu wenig zu Deinem Problem, aber ich demonstriere Dir mal eine mögliche Lösung:

test=# create table katharina (id int, value int);
CREATE TABLE
test=*# insert into katharina (id) values(1);
INSERT 0 1
test=*# update katharina set value = (select s from generate_series(1,5)s);
ERROR:  more than one row returned by a subquery used as an expression
test=*# alter table katharina alter column value type int[] using array[value];
ALTER TABLE
test=*# update katharina set value = (select array_agg(s) from generate_series(1,5)s);
UPDATE 1
test=*# select * from katharina ;
 id |    value
----+-------------
  1 | {1,2,3,4,5}
(1 row)

Vielleicht kannst es ja so verwenden. Wenn Du eine beliebige Anzahl von Elementen in einer Spalte speichern willst, brauchst halt ein ARRAY. Wobei ich nicht grade ein Freund davon bin.


Andreas

Das verstehe ich ehrlich gesagt nicht.
Ich würde gerne aus einer Spalte gezielt die werte trennen und abbilden.

Eine tab_1 und eine tab_2 ist gegeben.

Sie stehen folgendermaßen in Beziehung:

tab_1.id = tab_2.rid

Die Tabellen stehen in einer 1 zu N Beziehung zueinander. Das heißt während bei tab_1 die id = “1” einmal vorkommt, wird die rid = “1” zweimal vorkommen.

Jetzt möchte ich ganz gerne die Spalte in tab_2 (tab_2.werte) in zwei Spalten aufteilen. Die Werte mit 6100,6200,… sollen in der Spalte "tab_2.klimastufe angezeigt werden.
Die Werte mit 7100,7200,… sollen in einer Spalte tab_2.wasserstufe angezeigt werden.

Folgender Code erschließt sich mir da:


ALTER TABLE tab_neu ADD COLUMN klimastufe character varying;
ALTER TABLE tab_neu ADD COLUMN wasserstufe character varying;
 
UPDATE    tab_neu
SET       wasserstufe = (   SELECT tab_2.wert
                                    FROM tab_2      
                                    WHERE tab_1.id = tab_2.rid
                                    AND tab_2.wert LIKE '%7%'
                                    AND ST_Intersects(AsText(tab_1.position), ('POLYGON ((...))'))),
               klimastufe = (   SELECT tab_2.wert
                                    FROM tab_2      
                                    WHERE tab_1.id = tab_2.rid
                                    AND tab_2.wert LIKE '%6%'
                                    AND ST_Intersects(AsText(tab_1.position), ('POLYGON ((...))')));

Das Sub-Select liefert die richtigen Werte zurück.

Deine tab_1 brauchst Du doch gar nicht für das Update. Wenn ich Dich richtig verstehe, willst Du sowas machen:

test=# create table tab_2(rid int, werte int);
CREATE TABLE
test=*# copy tab_2 from stdin;
Enter data to be copied followed by a newline.
End with a backslash and a period on a line by itself.
>> 1    6100
>> 1    6200
>> 1    7100
>> 1    7200
>> \.
test=*# alter table tab_2 add column klima int, add column wasser int;
ALTER TABLE
test=*# update tab_2 set klima = werte where werte between 6000 and 6999;
UPDATE 2
test=*# update tab_2 set wasser = werte where werte between 7000 and 7999;
UPDATE 2
test=*# select * from tab_2;
 rid | werte | klima | wasser
-----+-------+-------+--------
   1 |  6100 |  6100 |
   1 |  6200 |  6200 |
   1 |  7100 |       |   7100
   1 |  7200 |       |   7200
(4 rows)

Davon abgesehen, daß ich das so für ungeschickt halte, sollte es das sein, was Du suchst (bzw. was ich aus Deinen Angaben so interpretiere…)


Andreas

Das Datenvolumen der Datenbank ist ziemlich mächtig und die Tabellen wie die tab_1 sind unter Einhaltung der geforderten Redundanz entstanden.
Leider läuft mir die Abfrage zu lange mit dieser gedachten Lösung. Da muss ich weiter probieren… Trotzdem Danke!

Ich sagte ja, daß mir das selber nicht gefällt, auch und gerade in Hinblick auf Performance. Es macht nicht viel Sinn, die Werte einer Spalte so auf 2 Spalten zu verteilen, das bläht die Tabelle nur unnötig auf.

Um, wie gezeigt, da 2 Spalten hinzuzufügen und mit Werten zu füllen ist relativ viel Aufwand nötig, man kann das alles im Select handeln, ohne die 2 extra Spalten.


Andreas

Das ist ja alles wahr. Das Endresultat soll aber diese verschlüsselten Werte in den Spalten entschlüsselt aufzeigen. Das UPDATE muss also stattfinden. So lautet der Auftrag :wink:



test=# select * from tab_2;
 rid | werte | klima | wasser
-----+-------+-------+--------
   1 |  6100 |  6100 |
   1 |  6200 |  6200 |
   1 |  7100 |       |   7100
   1 |  7200 |       |   7200
(4 rows)

test=*# alter table tab_2 drop column klima, drop column wasser;
ALTER TABLE
test=*# select rid, werte, case when werte between 6000 and 6900 then werte else null end as klima, case when werte between 7000 and 7900 then werte else null end as wasser from tab_2;
 rid | werte | klima | wasser
-----+-------+-------+--------
   1 |  6100 |  6100 |
   1 |  6200 |  6200 |
   1 |  7100 |       |   7100
   1 |  7200 |       |   7200
(4 rows)

so what?


Andreas

Loblich, in der Theorie alles gut… leider immer noch kein ‘UPDATE’ dabei. Wenn man es genau nehmen will, dann ist der Datentyp kein integer sondern character varying.
Durch eine Million Datensätze und ein festgelegtes Suchfeld das in Beziehung mit einer anderen Tabelle steht, ist es oftmals nicht leicht eine performante Abfrage zu erstellen. Aber wenn du eine Lösung für mich hast, wie zum Beispiel das Erstellen von Indizen… immer her damit. Aber frag nicht nach Sinn des Ganzen… sonst muss ich dir damit kommen, das die Abfrage so bequem wie möglich gestellt werden sollte, da die Aktualität der Daten gewährleistet werden muss.

Das UPDATE von einer Spalte ist gelaufen nach ganzen 2.22 std. by the way.

Indexe müssen zur Abfrage passen. Schau Dir EXPLAIN (ANALYSE) an, schau, wo da Sequentielle Scans laufen, das sind gute Kandidaten für Indexe.

Warum Du eine VARCHAR-Spalte nochmals in 2 weitere VARCHAR-Spalten duplizieren willst habe ich noch immer nicht verstanden, zumal Du eh schon Performance-Probleme hast. Durch Deine Aktion verballerst Du die Länge dieser Spalte MAL Anzahl der Zeilen an unnötigen Speicher. Von weiteren Problemen, die solche Redundanzen mitbringen, mal ganz zu schweigen (Aktualität der Werte z.B.)

Das so ein Update Zeit braucht, ist auch klar, die komplette Tabelle ist neu zu schreiben.


Andreas