Fragensteller
Update aus zwei Datensätzen

Frage
-
Hallo allerseits,
heute wende ich mich wieder mal an das Forum und wäre für Hilfestellung bei meinem Problem sehr dankbar. Ich habe Datensätze, die täglich auflaufen und die folgenden Felder darstellen.
MANr Datum Beginn Ende VerspaetetDatum VerspaetetZeit
1 10.12.2012 10:00 18:00 0 0
1 12.12.2012 10:30 20:00 10.12.2012 19:00
Es geht um Zeitkorrekturen, bei denen ein Mitarbeiter z.B. am 12.12.2012 mitteilt, dass er am 10.12.2012 nicht um 18:00 Uhr sondern um 19:00 Uhr Feierabend gemacht hat. Es soll also die „VerspaetetZeit“ aus dem Datensatz vom 12.12. als „Ende“ in den Datensatz vom 10.12. übernommen und damit 18:00 Uhr ersetzt werden. Der Datensatz vom 12.12. bleibt unverändert.Wie stelle ich das am besten an? Ich bekomme keine vernünftige Update/Replace-Funktion hin.
Vielen Dank schon mal für Hilfe.
Gruß
Volkmar
Alle Antworten
-
Hallo Alexander,
stimmt, habe ich vergessen zu sagen: Es sind varchar-Felder. Mit deinem Update-Vorschlag hatte ich es schon versucht, aber dabei bleibt die Änderung in der Zeile. Es soll aber so sein, dass der VerspaetetZeit-Wert aus dem zweiten Datensatz (19:00) den Ende-Wert (18:00)des ersten (vom Datum her betrachtet) ersetzt.
Gruß
Volkmar
-
Hallo Volkmar,
Du benötigst keine Updates sondern nur eine View, die Aktualisierungen entsprechend widerspiegelt. Dennoch solltest Du ev. mal das Datenmodell überdenken; große Vorteile sehe ich da nicht :)
USE tempdb GO DECLARE @t TABLE ( MANr int NOT NULL, Datum char(10) NOT NULL, Beginn char(5) NOT NULL, Ende char(5) NOT NULL, VerspaetetDatum char(10) NULL, VerspaetetZeit char(5) NULL ); INSERT INTO @t (MANr, Datum, Beginn, Ende, VerspaetetDatum, VerspaetetZeit) VALUES (1, '10.12.2012', '10:00', '18:00', '0', '0'), (1, '12.12.2012', '10:30', '20:00', '10.12.2012', '19:00'), (2, '11.12.2012', '09:00', '15:00', '0', '0'), (3, '12.12.2012', '10:00', '14:30', '0', '0'), (3, '13.12.2012', '09:00', '18:00', '12.12.2012', '20:00') SELECT * FROM @t; SELECT o.MANr, o.Datum, o.Beginn, o.Ende FROM @t o LEFT JOIN @t v ON ( o.MANr = v.MANr AND o.Datum = v.VerspaetetDatum ) WHERE v.MANr IS NULL UNION SELECT o.MANr, o.Datum, o.Beginn, v.VerspaetetZeit FROM @t v INNER JOIN @t o ON ( v.MANr = o.MANr AND v.VerspaetetDatum = o.Datum )
Uwe Ricken
MCITP Database Administrator 2005
MCITP Database Administrator 2008
MCITP Microsoft SQL Server 2008, Database Development
db Berater GmbH
http://www-db-berater.de
SQL Server Blog (german only)- Bearbeitet Uwe RickenMVP Mittwoch, 9. Januar 2013 12:44
-
Hallo Volkmar,
als ohne Deinen genauen Anwendungsfall zu kennen, aber dann würde ich den Vorschlag von Uwe (Ricken) folgen und das Datenmodell überdenken.
Ich unreinen gesprochen würde ich dann die Änderung die direkt in die Tabelle schreiben und die Änderung in einer Historytabelle dokumentieren...
Gruss Bernhard
-
Hallo Uwe, hallo Bernhard,
danke für eure Hilfe. Die Datensätze bestehen natürlich nicht nur aus den paar Werten, sondern sind sehr viel länger und stammen aus Quellen, die ich derzeit nicht beeinflussen kann. Anhand dieser Datensätze werden umfangreiche Berechnungen durchgeführt und die hier aufgezeigten Änderungen "von Hand" vorgenommen - das will ich abstellen ohne das Datenmodell zu ändern.
Gibt es keine Möglichkeit einen Wert von einer Zeile in eine andere zu übertragen?
Gruß
Volkmar
-
Hallo Volkmar,
hast Du denn Einfluss auf die Tabelle, wenn Du sagst, "kommen aus Quellen die Du nicht beeinfluss kannst" würde mir nur die Möglichkeit einfallen
auf die Tabelle einen INSERT-Trigger zusetzen, der die eingefügte Zeile auswertet und dann die andere "Parent"-Zeile updatet..
Gruss Bernhard
-
Hallo Volkmar,
mal so auf die schnelle (ohne funktionsgarantie!)
CREATE TRIGGER VerspaetDatumergaenzenen ON table_2 FOR INSERT AS BEGIN -- SET NOCOUNT ON added to prevent extra result sets from -- interfering with SELECT statements. SET NOCOUNT ON; -- Insert statements for trigger here DECLARE @wasupdated nvarchar(10), @MANr nvarchar(10), @Datum nvarchar(10), @VZeit nvarchar(10) SET @wasupdated = (SELECT VerspaetetDatum FROM INSERTED); SET @MANr = (SELECT MANr FROM INSERTED); SET @VDatum = (SELECT VerspaetetDatum FROM INSERTED); SET @VZeit = (SELECT VerspaetetZeit FROM INSERTED); IF (@wasupdated != 0) UPDATE dbo.table_2 SET VerspaetetDatum = @Vzeit WHERE MANr = @MANr AND Datum = @VDatum END GO
In der virutellen Tabelle "INSERTED" schreibt der SQL-Server selbst den aktuell eingefügten Datensatz.
In Deinem Beispiel war der "leere / Standardwert" 0 für das Datum oder die Zeit, deswegen das ungleich 0.
Datentypen musst Du natürlich auch Deinem gegebenheiten anpassen.
Es ist auch eventuell nicht der eleganteste Code, aber sollte funktionieren.
Bei Fragen oder Probleme versuche ich gerne zu helfen..
Gruss Bernhard
-
So kompliziert, wie Bernhard es gemacht hat, muss es nicht gleich sein :)
Das nachfolgende Beispiel macht das eleganter und einfacher und berücksicht auch mehrere Datensätze (das ist das Manko an Bernhards - dennoch gutem - Beispiel)USE tempdb GO IF OBJECT_ID('dbo.test', 'U') IS NOT NULL DROP TABLE dbo.Test GO CREATE TABLE dbo.Test ( MANr int NOT NULL, Datum char(10) NOT NULL, Beginn char(5) NOT NULL, Ende char(5) NOT NULL, VerspaetetDatum char(10) NULL, VerspaetetZeit char(5) NULL, CONSTRAINT pk_Test PRIMARY KEY CLUSTERED ( MANr, Datum ) ) GO CREATE TRIGGER dbo.trg_Test ON dbo.Test FOR INSERT, UPDATE AS SET NOCOUNT ON UPDATE t SET t.Ende = i.VerspaetetZeit FROM inserted i INNER JOIN dbo.Test t ON ( i.MANr = t.MANr AND i.VerspaetetDatum = t.Datum ) WHERE i.VerspaetetDatum != '0' SET NOCOUNT OFF GO INSERT INTO dbo.Test (MANr, Datum, Beginn, Ende, VerspaetetDatum, VerspaetetZeit) VALUES (1, '10.12.2012', '10:00', '18:00', '0', '0'), (1, '12.12.2012', '10:30', '20:00', '10.12.2012', '19:00'), (2, '11.12.2012', '09:00', '15:00', '0', '0'), (3, '12.12.2012', '10:00', '14:30', '0', '0'), (3, '13.12.2012', '09:00', '18:00', '12.12.2012', '20:00') SELECT * FROM dbo.Test;
Dennoch würde ich mit Triggern vorsichtig sein, wenn es sich um ein eingekauftes Produkt handelt. In diesem Fall würdest Du nämlch jeglichen Supportanspruch verlieren. Gleichwohl dringend anzuraten sind Tests in einer DEV-Umgebung!
Uwe Ricken
MCITP Database Administrator 2005
MCITP Database Administrator 2008
MCITP Microsoft SQL Server 2008, Database Development
db Berater GmbH
http://www-db-berater.de
SQL Server Blog (german only)- Bearbeitet Uwe RickenMVP Mittwoch, 9. Januar 2013 14:32
-
Hallo Uwe,
hatte ich ja auch schon bemerkt, dass eventuell eleganter geht. Du hast es ja auch geschrieben, dass es trotzdem ein gutes Beispiel ist. Ich denke es vielleicht für Anfänger oder "SQL-Unbedarfte" leichter zu verstehen... Mal abgesehen von der Frage, ob sich diese an solch eine Aufgabenstellung wagen sollten. Aber man wächst mit den Aufgaben ;-)
Deswegen mal für mich: Du schreibst meine Lösung ist nicht für mehrere Datensätze geeignet. Ich bin bis dato davon ausgegangen, dass der SQL-Server den Trigger für jeden INSERT aufruft, auf wenn mehrere Datensätze eingefügt werden.
Danke und Gruss
Bernhard
-
Deswegen mal für mich: Du schreibst meine Lösung ist nicht für mehrere Datensätze geeignet. Ich bin bis dato davon ausgegangen, dass der SQL-Server den Trigger für jeden INSERT aufruft, auf wenn mehrere Datensätze eingefügt werden.
Hallo Bernhard,
du hast recht, dass der Trigger pro INSERT durchgeführt wird - aber das gilt immer für die Transaktion selbst.
Ein einfaches Beispiel, wie es auf jeden Fall mit Deinem Trigger funktionieren wird:
INSERT INTO dbo.Test (MANr, Datum, Beginn, Ende, VerspaetetDatum, VerspaetetZeit) VALUES (1, '10.12.2012', '10:00', '18:00', '0', '0')
Hierbei wird innerhalb der Transaktion nur EIN Datensatz eingefügt. Nun nehmen wir ein anderes Beispiel, in dem mehrere Datensätze in einer Transaktion eingetragen werden:
INSERT INTO dbo.Test (MANr, Datum, Beginn, Ende, VerspaetetDatum, VerspaetetZeit) SELECT 1, '10.12.2012', '10:00', '18:00', '0', '0' UNION SELECT 2, '11.12.2012', '09:00', '15:00', '0', '0' UNION SELECT 3, '12.12.2012', '10:00', '14:30', '0', '0')
Nun kann Dein Trigger nicht mehr funktionieren, da er immer darauf abzielt, nur einen Datensatz in inserted vorzufinden. Ein ganz simples Beispiel, dass Du ausprobieren kannst, verdeutlicht das Problem. Als Beispiel nehme ich die Daten aus der diesem Thread zugrunde liegenden Relation:
DECLARE @MANr int SELECT @MANr = MANr FROM dbo.test SELECT @MANr
Das Ergebnis wird sicherlich überraschend kommen, es wird die 3 sein. Das liegt daran, dass der Variablen immer der Wert des LETZTEN Datensatzes aus dem Resultset zugewiesen wird!
Würde nun also der Trigger so eingesetzt werden, wie von Dir vorgeschlagen, würde nur der letzte Datensatz aus inserted berücksichtigt werden.
Uwe Ricken
MCITP Database Administrator 2005
MCITP Database Administrator 2008
MCITP Microsoft SQL Server 2008, Database Development
db Berater GmbH
http://www-db-berater.de
SQL Server Blog (german only) -
Hallo Bernhard, hallo Uwe,
ich danke euch sehr für die Unterstützung. Nun ist es an mir die Lösungen umzusetzen und - wichtig - zu verstehen. Bisher habe ich noch keine Trigger verwendet und bin deshalb sehr gespannt. Ich melde mich wieder, wenn alles läuft - so hoffe ich.
Also nochmals vielen Dank und einen schönen Abend.
Gruß
Volkmar
-
Hallo Bernhard, hallo Uwe,
dank eurer Unterstützung habe ich mein Problem lösen können - alles läuft prima und ich experimentiere gerade noch weiter mit Triggern. Die Funktion ist ja zunächst verlockend, aber man verliert auch leicht die Übersicht, sobald mehrere zum Einsatz kommen. Man "sieht" sie ja nicht.
Danke für die Hilfe.
Gruß
Volkmar