Wie füllt man mit SQL eine Spalte mit

… Spaltenwerten einer anderen Tabelle so wie es bei Excel die Funktion SVERWEIS macht?

Gegeben sind zwei Tabellen:

Die Tabelle „leute“ enthält Personendaten und eine Spalte, die nach der SQL-Operation einen Beitragssatz gefülklt bekommen soll, und zwar in Abhängigkeit von Alter und Geschlecht.

Die Tabelle Tarif enthält drei Spalten: Alter (wg. reserviertem SQL-Begriff „LebAlter“ genannt), Beitragssatz für Mann und Beitragssatz für Frau.

Der gesuchte SQL-Befehl soll die Spalte Beitragssatz in der Tabelle „leute“ mit den passenden Werten aus der Tabelle „tarif“ füllen.

Zur Vereinfachung reicht es, wenn das Geschlecht vernachlässigt wird und immer der alterspassende BeitragsSatzM verwendet wird.

So werden die Beispieltabellen in sqlite3 aufgebaut:

DROP table leute;
CREATE TABLE leute
(
Vorname text,
Geburtsjahr integer,
Geschlecht text,
Beitragssatz integer );

DROP table tarif;
CREATE TABLE tarif
(
LebAlter integer ,
BtgSatzM integer ,
BtgSatzF integer
);

INSERT INTO leute
(Vorname, Geburtsjahr, Geschlecht)
VALUES („Anke“, 1944, „F“);
INSERT INTO leute
(Vorname, Geburtsjahr, Geschlecht)
VALUES („Markus“, 1973, „M“);

INSERT INTO tarif
(LebAlter, BtgSatzM, BtgSatzF)
VALUES
(1, 345, 444);

INSERT INTO tarif
(LebAlter, BtgSatzM, BtgSatzF)
VALUES
(39, 845, 944);

INSERT INTO tarif
(LebAlter, BtgSatzM, BtgSatzF)
VALUES
(68, 1235, 1484);

INSERT INTO tarif
(LebAlter, BtgSatzM, BtgSatzF)
VALUES
(101, 2845, 2544);

Das sieht dann so aus:

sqlite> select * from leute;
Vorname Geburtsjahr Geschlecht Beitragssatz


Anke 1944 F
Markus 1973 M
sqlite> select * from tarif;
LebAlter BtgSatzM BtgSatzF


1 345 444
39 845 944
68 1235 1484
101 2845 2544
sqlite>

Ich stelle auf das Alter im Jahr 2012 ab und habe folgenden Befehl gebastelt:

UPDATE leute SET beitragssatz = (SELECT BtgSatzM FROM tarif WHERE (LebAlter = (SELECT (2012 - Geburtsjahr) FROM leute)));

Das Ergbnis ist falsch:

sqlite> select * from leute;
Vorname Geburtsjahr Geschlecht Beitragssatz


Anke 1944 F 1235
Markus 1973 M 1235
sqlite>

Der Befehl füllt immer den gleichen Beitragssatz. Dass er bei Anke richtig ist, scheint reiner Zufall. Ich habe das ganze zunächst mit großen Tabellen probiert (über 10tausend Zeilen und alle Alter vertreten), und da bekommen auch alle den gleichen Wert.

Das gewünschte Ergebnis wäre:

Vorname Geburtsjahr Geschlecht Beitragssatz


Anke 1944 F 1235
Markus 1973 M 944

Ich bin noch in den ersten Gehversuchen mit SQL. Ich habe mir etwas zu JOIN angelesen, ob die Lösung da zu suchen ist. Aber da scheinen immer komplette Zeilen im Rahmen einer Verknüpfung rübergezogen zu werden.

Ich weiß nicht, ob mit SQL überhaupt möglich, was ich sonst in Excel mit SVERWEIS mache. Der Nachteil von SVERWEIS/Excel ist, dass man die Formel immer 10tausende male herunterkopieren muss und der Rechner dabei ordentlich ins Schwitzen kommt.

Weiß jemand, ob und wie es in SQLITE3 geht?

Mit diesem Befehl bekomme ich schon richtige Werte zugeordnet:

SELECT leute.*,tarif.BtgsatzM FROM LEUTE,TARIF WHERE (2012 - leute.Geburtsjahr == tarif.LebAlter);

Vorname Geburtsjahr Geschlecht Beitragssatz BtgSatzM


Anke 1944 F 0 1235
Markus 1973 M 0 845

Aber den UPDATE der Spalte Beitragssatz kriege ich nicht hin:

UPDATE leute SET Beitragssatz = (SELECT tarif.BtgsatzM FROM LEUTE,TARIF WHERE (2012 - leute.Geburtsjahr == tarif.LebAlter));

sqlite> SELECT * FROM leute;
Vorname Geburtsjahr Geschlecht Beitragssatz

sqlite> SELECT * FROM leute;
Vorname Geburtsjahr Geschlecht Beitragssatz


Anke 1944 F 1235
Markus 1973 M 1235

Moin, wipfelblick,

Aber den UPDATE der Spalte Beitragssatz kriege ich nicht hin:

das solltest Du bleiben lassen. Wenn dem Vorstand einfällt, den Beitragssatz zu ändern, kannst Du alles nochmal rechnen.

In einem sauberen Entwurf wird der Beitragssatz nur einmal geschrieben, nämlich wenn er festgelegt wird. Ansonsten wird er nur gelesen!

Gruß Ralf

Wenn der Vorstand die Beitragssätze ändert, wird das auf die Werte der Tabelle leute keinen Einfluss haben, weil es eine Stichtagsbetrachtung ist.

Ab gesehen davon will ich in der Tabelle leute eigentlich auch nicht die Beitragssätze sondern die Beiträge stehen haben. Das hätte das Beispiel nur verkompliziert.

Das obige Beispiel ist frei erfunden. Ich möchte nur herausbekommen, wie ich das, was man in Excel mit einer SVERWEIS-Funktion erreicht, unter sqlite3 bewerkstelligt, weil ich das Mausgeklicke und -geziehe leid bin.

Gruß

Andreas

Hi Andreas,

 UPDATE leute 
 SET Beitragssatz = 
 (SELECT tarif.BtgsatzM FROM LEUTE,TARIF 
 WHERE (2012 - leute.Geburtsjahr == tarif.LebAlter)
 );

hier fehlt die Where-Klausel für die Tabelle im Update.

Der Select liefert die richtigen Werte, die werden aber immer auf die ganze Tabelle Leute angewendet. Sieger bleibt somit der - zufällig - Letzte.

Gruß Ralf

Hallo Ralf,

danke für den Hinweis. Ich begreife das noch nicht, was da warum passiert. Vielleicht das: Jede Zeile von leute wird jeweils nacheinander mit den Werten der Menge aus der inneren SELECT-Abfrage nacheinander belegt, weshalb immer der letzte „Sieger“ ist.

Dann müsste die fehlende WHERE-Klausel bestimmt werden, dass nur dann ein Update erfolgen soll, wenn das zum Selectwert zugehörige Alter mit dem Alter der aktuellen Zeile von leute übereinstimmt.

UPDATE leute SET Beitragssatz = (SELECT tarif.BtgsatzM FROM LEUTE,TARIF WHERE (2012 - leute.Geburtsjahr == tarif.LebAlter)
 ) where (2012 - leute.Geburtsjahr == tarif.LebAlter);

führt zu
Error: no such column: tarif.LebAlter

Anders als im SELECT-Befehl lässt sqlite3 keine zwei durch Komma angegebenen Tabellen zu. Ich kann also nicht schreiben „UPDATE leute,tarif …“, wie das anscheinend bei mySQL geht.

Darum habe ich es anders versucht:

UPDATE leute SET Beitragssatz = (SELECT tarif.BtgsatzM FROM LEUTE,TARIF WHERE (2012 - leute.Geburtsjahr == tarif.LebAlter)
 ) where (2012 - leute.Geburtsjahr == (select LebAlter from tarif));

Das bewirkt nichts. Ich habe den Beitragssatz genullt und dann den nächsten Befehl eingegeben:

UPDATE leute SET Beitragssatz = (SELECT tarif.BtgsatzM FROM LEUTE,TARIF WHERE (2012 - leute.Geburtsjahr == tarif.LebAlter)
 ) where (2012 - leute.Geburtsjahr == (select LebAlter from tarif,leute where tarif.LebAlter == 2012 - leute.Geburtsjahr));

select \* from leute;
Vorname Geburtsjahr Geschlecht Beitragssatz
---------- ----------- ---------- ------------
Anke 1944 F 1235
Markus 1973 M 0

Ist schon besser. Anke ist OK. Bei Markus hat der Befehl nichts gemacht.

Gruß

Andreas

So nicht lösbar
Viele gelesene Fundstellen zu vlookup bzw. sverweis und sql sowie diverse Beschreibungen bzw. Dokumentationen weiter komme ich zu dem Ergebnis, dass es in SQL keine Entsprechnung der SVERWEIS-Funktionalität gibt.

Man kann mit UPDATE SET WHERE aus SQL entweder alle Zeilen einer Spalte oder alle Zeilen für welche die WHERE-Klausel zutrifft, akualisieren. Anders als bei der SVERWEIS-Funktion wird die Bedingung (WHERE-Klausesl) aber nicht zeilenindividuell geprüft. d.h. die WHERE-Klausel prüft letztlich nur einen konstanten Ergebnis-Wert der Fremdtabelle gegen den jeweiligen Zeilenwert der zu aktualisierenden Tabelle.

So geht es also nicht.

Also vielleicht anders:

SELECT leute.\*,tarif.BtgsatzM FROM LEUTE,TARIF WHERE (2012 - leute.Geburtsjahr == tarif.LebAlter);

liefert bekanntlich

Vorname Geburtsjahr Geschlecht Beitragssatz BtgSatzM


Anke 1944 F 0 1235
Markus 1973 M 0 845

In der Spalte BtGSatzM der SELECT-Ausgabe steht ja genau das drin, was ich in die Spalte Beitragssatz per UPDATE geschrieben haben wollte. Vielleicht gibt es eine möglichkeit, die SELECT-Ausgabe als Input für eine neue Tabelle „leute1“ zu verwenden.

Mal sehen …

Du könntest einen View benutzen .
Also einfach eine Tabelle die automatisch ein Select ist was immer geupdatet wird.
Somit hast du dein Select der ja die Ausgabe bringt und erweiterst ihn so das du deine Gewünschte Tabelle erhälst.

Also ein View mit

Select A.id,A.Name,A.Jahr,B.Beitrag FROM leute AS A JOIN tarif as B ON (2012- A.Jahr == B.Jahr)

Dann nimmst du einfach den View :smile: Denn der hat dein Aktuellen Beitragssatz.

Übrigens fällt mir auf das auch du in Excel denkst.

Deswegen willst du auch ein Datenfeld Updaten in einer Tabelle anstantt , da die Daten ja schon vorhanden sind, ein Select zu machen.

Die Datenbank ist anders als in Excel nur für die Datenhaltung da.
Wenn du die Daten sehen willst nebeneinander dann baut man sie aus den vorhandenen Daten zusammen. Oder bei mehreren Schritten halt auch Insert SELECT. Aber dafür hat man eben den View entwickelt. Der gibt aus den Daten die entsprechende Ansicht .

Es reicht also , wie schon gesagt wurde, das Lesen , schreiben brauch ich nicht , wenn dann leg ich eine neue Tabelle an und die Alte ist dann eh nicht richtig, die hätte gar kein Feld Beitrag haben dürfen , denn der Ergibt sich ja aus der Tabelle tarif im bezug auf jahr bei den Leuten.

Also was bei Excel das Selbe Blatt ist, ist bei Datenbanken nur ein Augenblick , bzw nur das ergebnis einer Datensammlung durch eine Abfrage.

Hier noch mal ein Beispiel wie Datenhaltung und Ansicht unterschieden wird.

Hier also ein View Beispiel :

--
-- Tabellenstruktur für Tabelle `tarif`
--

CREATE TABLE `tarif` (
 `Jahr` int(11) NOT NULL,
 `Betrag` decimal(5,2) NOT NULL,
 PRIMARY KEY (`Jahr`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

--
-- Daten für Tabelle `tarif`
--

INSERT INTO `tarif` (`Jahr`, `Betrag`) VALUES
(1972, '200.00'),
(1973, '250.00'),
(1974, '260.00');




-- Tabellenstruktur für Tabelle `leute`
--

CREATE TABLE IF NOT EXISTS `leute` (
 `id` int(11) NOT NULL AUTO\_INCREMENT,
 `Name` varchar(255) NOT NULL,
 `Geboren` int(172) NOT NULL,
 PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO\_INCREMENT=4 ;

--
-- Daten für Tabelle `leute`
--

INSERT INTO `leute` (`id`, `Name`, `Geboren`) VALUES
(1, 'Thomas', 1974),
(2, 'Ingo', 1972),
(3, 'Paul', 1976);

Wir wollen nun den Betrag von allen Leuten haben.

SELECT L.id, L.Name, L.Geboren , T.Betrag FROM leute AS L JOIN tarif AS T ON L.Geboren = T. Jahr

Das setzen wir als View

CREATE VIEW leutebetrag AS SELECT L.id, L.Name, L.Geboren , T.Betrag FROM leute AS L JOIN tarif AS T ON L.Geboren = T. Jahr;

Und nun können wir mit

SELECT \* FROM leutebetrag 

Ausgeben .

id Name Geboren Betrag
1 Thomas 1974 260.00
2 Ingo 1972 200.00

Wegen dem JOIN Fehlt noch ein Eintrag

Also fügen wir noch ein Wert hinzu .

INSERT INTO viewexample.tarif (Jahr ,Betrag) VALUES (‚1976‘, ‚300.00‘);

Und gugen uns gleich nochmal

SELECT \* FROM leutebetrag

an

id Name Geboren Betrag
1 Thomas 1974 260.00
2 Ingo 1972 200.00
3 Paul 1976 300.00
1 Like

brauchen wir nur einen Auzug , so legen wir einen Auzug an.

CREATE TABLE `viewexample`.`leutebetragauszug` (
`id` INT NOT NULL AUTO\_INCREMENT PRIMARY KEY ,
`Name` VARCHAR( 255 ) NOT NULL ,
`Geboren` INT( 172 ) NOT NULL,
`Betrag` decimal(5,2) NOT NULL
) ENGINE = MYISAM ;




INSERT INTO leutebetragauszug SELECT L.id, L.Name, L.Geboren , T.Betrag FROM leute AS L JOIN tarif AS T ON L.Geboren = T. Jahr

Das sieht dann so aus

id Name Geboren Betrag
1 Thomas 1974 260.00
2 Ingo 1972 200.00
3 Paul 1976 300.00

hoffe es hilft jemanden.

Ich will das an dem View Beispiel nochmal festhalten.

Die Daten sind alle vorhanden.
Wenn ich aber die Daten zusammenführen will so das der Betrag bei den Leuten auftaucht, dann holle ich mir diese Ansicht. Ich ändere deswegen aber nichts in einer der Datentabellen . Zumal sich daraus inkosistenz ergibt. Wärend die eine Seite sich ändert ändern sich die andere Seite nicht mit. Damit ist es eine Auswertung zu einem Zeitpunkt X (Ansichtszeitpunkt) aber im sinne der Datenhaltung ein Fehler.

Ich hoffe das Dir das Begrifflicher wird durch das Beispiel.

Thomas Punkt.

P.S. Viele Wege führen nach Rom , einige kenn ich schon !!

Hi,

für diese Aufgabe musst du mittels eines Cursors eine Schleife über die TarifTabelle programmieren.

declare cur cursor for select Lebalter, BtgsatzM, BtgsatzF from tarif;
open cur;

myLoop: loop
set alter = -1;
fetch cur into alter, wertM, wertF;
if alter = -1 then leave myLoop; end if;
update leute set Beitragssatz = wertM where (2012 -
leute.Geburtsjahr == Alter);
end loop;

Das Abbruchkriterium für die Schleife sieht bei dir vielleicht anders aus und die Variablen musst du noch deklarieren.

Nach Geschlecht unterscheiden:

Beitragssatz = wertM * (Geschlecht == „M“)

  • wertF * (Geschlecht == „F“)

Müsste in etwa so funktionieren.

Gruß Fralang

Hallo,

es hat eine Weile gedauert, bis ich das mit den Views einigermaßen verstanden und Dein Beispiel dann auf meines mit SQLITE3 übertragen habe.

Nicht verstanden habe ich den Sinn, durch den JOIN fehlende Datensätze über INSERTs nachzutragen:

Wegen dem JOIN Fehlt noch ein Eintrag

Also fügen wir noch ein Wert hinzu .

INSERT INTO viewexample.tarif (Jahr ,Betrag) VALUES
(‚1976‘, ‚300.00‘);

Die Datensätze dürfen garnicht erst verloren gehen. Aber Deine Hinweise auf View und vor allem Dein Join-Beispiel haben es mir möglich gemacht, in die richtige Richtung weiter zu denken. Es muss ein LEFT OUTER JOIN sein.

Hier ist die vollständige Lösung, wonach ich gesucht habe und was ich mit Eurer Hilfe erstellen konnte:

DROP table leute;
CREATE TABLE leute
(
Vorname text,
Geburtsjahr integer,
Geschlecht text
);

DROP table tarif;
CREATE TABLE tarif
(
LebAlter integer ,
BtgSatzM integer ,
BtgSatzF integer 
);

INSERT INTO leute 
(Vorname, Geburtsjahr, Geschlecht)
VALUES ("Anke", 1944, "F");
INSERT INTO leute 
(Vorname, Geburtsjahr, Geschlecht)
VALUES ("Markus", 1973, "M");
INSERT INTO leute 
(Vorname, Geburtsjahr, Geschlecht)
VALUES ("Klaus", 1873, "M");


INSERT INTO tarif
(LebAlter, BtgSatzM, BtgSatzF)
VALUES
(1, 345, 444);

INSERT INTO tarif
(LebAlter, BtgSatzM, BtgSatzF)
VALUES
(39, 845, 944);

INSERT INTO tarif
(LebAlter, BtgSatzM, BtgSatzF)
VALUES
(68, 1235, 1484);

INSERT INTO tarif
(LebAlter, BtgSatzM, BtgSatzF)
VALUES
(101, 2845, 2544);


DROP VIEW leute1;
CREATE VIEW leute1 AS SELECT leute.\* , 
 CASE leute.Geschlecht 
 WHEN 'M' THEN tarif.BtgSatzM 
 WHEN 'F' THEN tarif.BtgSatzF
 ELSE 0
 END 
 AS BtgSatz 
FROM leute 
AS leute LEFT OUTER JOIN tarif 
ON (2012 - leute.Geburtsjahr == tarif.LebAlter);

SQLITE3 schränkt meine Bearbeitungsmöglichkeiten bei einem VIEW etwas ein. Es ist sinnvoll, Redundanzen zu vermeiden und das Konzept mit den VIEWs auszureizen. Im Falle des Falles bliebe aber immer noch die Möglichkeit, aus dem VIEW wieder eine Tabelle zu machen.

CREATE TABLE leute2 AS SELECT * FROM leute1;

sqlite> select * from leute;
Vorname Geburtsjahr Geschlecht


Anke 1944 F
Markus 1973 M
Klaus 1873 M

sqlite> select * from leute1;
Vorname Geburtsjahr Geschlecht BtgSatz


Anke 1944 F 1484
Markus 1973 M 845
Klaus 1873 M

sqlite> select * from leute2;
Vorname Geburtsjahr Geschlecht BtgSatz


Anke 1944 F 1484
Markus 1973 M 845
Klaus 1873 M

Klasse! Vielen Dank!

Gruß

Andreas

Danke für die Info.

Übrigens kannst du soviele JOINS machen wie du lustig bist.
Somit kannst du also auch eine Tabelle zusammenbasteln wo es mehrere Untergruppen gibt.

Select User.name , Adresse.PLZ , Gehaltsklassen.Gehalt FROM User
LEFT OUTER JOIN Adresse ON User.Adresse = Adresse.Adresse
LEFT OUTER JOIN Gehaltsklassen ON Gehaltsklassen.name = User.Gehaltsklasse

Ergebnis ist dann aus 3 Tabellen zusammen gesetzt , wovon Adresse und
Gehaltsklassen jeweils eigene Tabellen darstellen :smile:
Jeweils bezieht sich das auf in User enthaltende Daten :smile:

Vielleicht bringt es etwas zur Optimierung beizutragen.