Fragensteller
Kann mir jemand beim Trigger helfen?

Frage
-
Hallo Leute, und danke für's reinschauen...
Ich benötige Hilfe bei einem DB-Trigger, und wäre euch dankbar für eure Tipps.
ist es möglich mit einem Trigger eine Sortierung in X Richtung vornehmen wenn was dazu bzw. entfehrnt wird?
Beispiel:
Insert: x -> 1, 2, 3, 4 wird eine dazu genommen mit x=3 -> 1, 2, 3, 3, 4 wird vom trigger in 1, 2, 3, 4, 5 verwandelt.
oder
Delete x -> 1, 2, 3, 4 wird eine gelöscht mit x = 3 -> 1, 2, 4 wird vom trigger in 1, 2, 3 verwandelt.
danke im voraus...
Alle Antworten
-
Hallo,
Ja, solche leicht merkwürdigen Anforderungen kann man lösen, dazu muss man aber die komplette gewünschte Logik wissen. Warum soll bei einem Insert die Daten abgeändert werden, warum soll bei einem Delete ein anderer Datensatz als vorgegeben gelöscht werden.
Was wenn bei einem Delete gleich mehrere Datensätze gelöscht oder beim Insert mehrere eingefügt werden sollen? Vergiss nicht, ein Trigger zündet nicht je Datensatz, sondern je Transaktion, in den virtuellen Tabellen inserted/deleted können also 1-n Datensätze stehen.
Olaf Helper
[ Blog] [ Xing] [ MVP] -
Vielen Dank für den Gedanken...
Letztendlich sollen es platten darstellen. Es ist nicht möglich, dass zwei der Platten auf x=3 liegen... genau so ist es nicht möglich, dass eine Zahl in Reihe fehlt. Angenommen ich füge mehrere zu. Beispiel: ist Tabelle -> 1, 2, 3, 4, 5 und ich füge 2, 3, 4 ein. Das Ergebnis wäre so: 1, 2, 2, 3, 3, 4, 4, 5. Er soll daraus 1, 2, 3, 4, 5, 6, 7, 8 machen. Also immer nach vorne stellen.
Beim delete:
von 2, 3, 4 wäre das Ergebnis rückwärts... ich hoffe ich habe jetzt kein Knoten im Kopf...
-
Ich denke, das sich eine solche Logik über kurz oder lang rächen wird.
Was ist denn der Primary Key der Tabelle? Diese Zahlen?Woraus soll sich den die Sortierung ergeben?
Könntest Du nicht viel einfacher mit ROW_NUMBER() OVER(ORDER BY ...) zur Laufzeit eine Zahl (für die Reihenfolge) erzeugen?
Einen schönen Tag noch,
Christoph
--
Microsoft SQL Server MVP - http://www.insidesql.org/blogs/cmu -
- Als Antwort vorgeschlagen Mathias Liefke Dienstag, 6. Januar 2015 10:05
-
Naja, ich wollte jetzt nicht eine Diskussion auslösen ob es gut oder schlecht ist. Es geht mir nur um DB Sortierung nach X. Natürlich gibt es ein anderen Primärschlüssel. Wobei das eigentlich die DB selbst verwalten sollte. Selbst wenn ich jetzt als Beispiel probieren würde eine 3 reinzupacken, und auf dem Platz liegt nun mal keine Platte, dann soll er mir daraus natürlich eine 1 machen. Die Platte selbst hat einen Barcode, sowie ID spalte gibt es auch. Kleine Übersicht über die Spalten: ID(PK), Barcode, X_Pos, L, B, D
Deswegen suche ich im Prinzip nicht nach gelegten Position, sondern eigentlich nur nach Barcode.
Ich wollte mich eigentlich im Programm nicht mit der Position beschäftigen...
Danke fürs mitmachen Leute...
-
-
??? Verstehe ich nicht... die Sortierung erfolgt nur im X Richtung. Alles andere ist eigentlich irrelevant. Barcodes können ausgedacht sein, sowie die ID. Die ID ist nur der PK von dem ganzen. es könnte ja sein, dass ich sortiert die platten einfüge, und hinterher mitten drin eine lösche. zwar ist die ID dann aufsteigend, ist aber nicht sinnvoll. Denn wenn ich dann wieder eine einfüge in die Mitte, wird die ID größer... ich hoffe man kann meinen Gedankengang folgen...
-
Vielen Dank für den Gedanken...
Letztendlich sollen es platten darstellen. Es ist nicht möglich, dass zwei der Platten auf x=3 liegen... genau so ist es nicht möglich, dass eine Zahl in Reihe fehlt. Angenommen ich füge mehrere zu. Beispiel: ist Tabelle -> 1, 2, 3, 4, 5 und ich füge 2, 3, 4 ein. Das Ergebnis wäre so: 1, 2, 2, 3, 3, 4, 4, 5. Er soll daraus 1, 2, 3, 4, 5, 6, 7, 8 machen. Also immer nach vorne stellen.
Beim delete:
von 2, 3, 4 wäre das Ergebnis rückwärts... ich hoffe ich habe jetzt kein Knoten im Kopf...
Immer nach vorne... Ich dachte meine Beispiele wären hinreichend... sollte es die Zahl nicht geben, dann nächst-fortlaufende. Beispiel: 1,2,3,4,5 ich füge eine 10 ein, dann wird aus 10 eine 6...
Bei der Zahl 0 und kleiner wird automatisch eine 1 gemacht, und wieder nach ganz vorne gestellt. Somit kann ich jede nur erdenkliche Situation abdecken...
-
Ich denke, wir sollten das jetzt mal mit einem konkreten Beispiel angehen. Es gibt also eine Tabelle #Platten, die für verschiedene Reihen eine fortlaufende Nummer liefern soll (nicht enthalten!). Hier zeige ich mal, wie es ohne Trigger gehen könnte:
Create Table #Platten(ID Integer Identity, Reihe integer, Barcode varchar(10), Sortierung integer, Eingefuegt Datetime DEFAULT GETDATE()); Insert into #Platten(Reihe, Barcode, Sortierung) Values(1, 'A_BC', 1); Insert into #Platten(Reihe, Barcode, Sortierung) Values(1, 'B_BC', 2); Insert into #Platten(Reihe, Barcode, Sortierung) Values(1, 'C_BC', 3); Insert into #Platten(Reihe, Barcode, Sortierung) Values(1, 'D_BC', 4); Insert into #Platten(Reihe, Barcode, Sortierung) Values(1, 'E_BC', 5); Insert into #Platten(Reihe, Barcode, Sortierung) Values(2, 'X_BC', 10); Insert into #Platten(Reihe, Barcode, Sortierung) Values(2, 'B_BC', 10); Select Reihe, Barcode, ROW_NUMBER() OVER(PARTITION BY Reihe ORDER BY Sortierung, Eingefuegt Desc) as LfdNr, Sortierung, Eingefuegt from #Platten; Insert into #Platten(Reihe, Barcode, Sortierung) Values(1, 'X_BC', 2); Insert into #Platten(Reihe, Barcode, Sortierung) Values(1, 'Y_BC', 3); Insert into #Platten(Reihe, Barcode, Sortierung) Values(1, 'Z_BC', 4); Select Reihe, Barcode, ROW_NUMBER() OVER(PARTITION BY Reihe ORDER BY Sortierung, Eingefuegt Desc) as LfdNr, Sortierung, Eingefuegt from #Platten; Drop Table #Platten;
Die zuletzt eingefügten Sätze X..Z werden immer vor den bestehenden Nummern einsortiert, diese wandern dann nach hinten.
Einen schönen Tag noch,
Christoph
--
Microsoft SQL Server MVP - http://www.insidesql.org/blogs/cmu -
Vielen Dank, aber das ist es so irgend wie nicht was ich wollte...
Die sollen ja nicht nach hinten wandern... Ich kann es per sql string machen. aber sollte schon automatisch gehen, ganz wichtig auch in der mitte... Beispiel:
update [Lagen] set x = (SELECT TOP 1 x FROM [Lagen] where PlatzNr = 1 and x = 2 order by x asc, id desc) + 1 where ID = (SELECT TOP 1 id FROM [Lagen] where PlatzNr = 1 and x = 2 order by PlatzPosition asc, id asc)
ID ist fortlaufend. damit erfahre ich, welches teil gehört jetzt nach vorne. Beispiel:
ID // PlatzNr // X
1 // 1 // 1
2 // 1 // 2
3 // 1 // 3
Jetzt kommt ein Eintrag: ID wird automatisch vergeben und somit eine 4! Also mein neuer DS wäre so:
ID(Auto), PlatzNr=1, X = 2, BC = Irgend was...
Die DB macht daraus jetzt wie folgt:
ID // PlatzNr // X
1 // 1 // 1
2 // 1 // 2
3 // 1 // 3
4 // 1 // 2
Jetzt macht der Trigger daraus:
ID // PlatzNr // X
1 // 1 // 1
4 // 1 // 2
2 // 1 // 3
3 // 1 // 4
Kann doch nicht so schwer sein... oder?
Jedenfalls dachte ich mir so, dass er nach der ID guckt, was als letztes insertet worden ist, soll nach vorn usw...
- Bearbeitet EugenIS_001 Mittwoch, 7. Januar 2015 10:59 Spalten haben sich verschoben...
-
Hallo Eugen,
Christophs Ansatz wäre schon der Richtige, Du musst ihn nur auf Deine Belange anpassen...
Eine mögliche Variante (nur oberflächlich getestet):
USE tempdb; GO IF OBJECT_ID('Lagen', 'U') IS NOT NULL DROP TABLE Lagen; GO CREATE TABLE Lagen( Id int IDENTITY(1, 1) NOT NULL, PlatzNr int NOT NULL, Sortierung int NOT NULL DEFAULT (0), Sortierung_Alt int NOT NULL DEFAULT (0), -- nur für Test Eingefuegt datetime2 NOT NULL DEFAULT SYSDATETIME()); GO CREATE TRIGGER TR_Lagen ON Lagen FOR INSERT, UPDATE, DELETE AS SET NOCOUNT ON; UPDATE Lagen SET Sortierung_Alt = Lagen.Sortierung, -- nur für Test Sortierung = LagenSortierung.Sortierung FROM Lagen INNER JOIN (SELECT Id, ROW_NUMBER() OVER(PARTITION BY PlatzNr ORDER BY PlatzNr, Sortierung, Id) FROM Lagen WHERE PlatzNr IN ( SELECT PlatzNr FROM inserted UNION SELECT PlatzNr FROM deleted) ) AS LagenSortierung(Id, Sortierung) ON Lagen.Id = LagenSortierung.Id WHERE Lagen.Sortierung != LagenSortierung.Sortierung; GO INSERT INTO Lagen(PlatzNr, Sortierung) VALUES (1, 1), (1, 2), (1, 4), (1, 3), (2, 1), (2, 2); SELECT * FROM Lagen ORDER BY PlatzNr, Sortierung; -- Weitere Einfügen INSERT INTO Lagen(PlatzNr, Sortierung) VALUES (1, 2), (1, 5), (2, 2), (2, 4); SELECT * FROM Lagen ORDER BY PlatzNr, Sortierung; -- Löschen DELETE Lagen WHERE PlatzNr = 1 AND Sortierung IN (3, 4); SELECT * FROM Lagen ORDER BY PlatzNr, Sortierung; -- Verschieben UPDATE Lagen SET PlatzNr = 3 WHERE PlatzNr = 2 AND Sortierung IN (1, 4); SELECT * FROM Lagen ORDER BY PlatzNr, Sortierung; GO
Gruß Elmar
P. S.: Günstiger ist es, Du lieferst gleich das notwendige SQL mit, das erspart Nachfragen und Irrtümer.
-
Falls ich es richtig verstanden habe, müsste im Trigger eine Zeile geändert werden (ID DESC):
ROW_NUMBER() OVER(PARTITION BY PlatzNr ORDER BY PlatzNr, Sortierung, Id DESC)
Diese Variante sollten den Fall des Fragenstellers ohne Trigger abdecken:
Create Table #Platten(ID Integer Identity, PlatzNr integer, X integer); Insert into #Platten(PlatzNr, X) Values(1, 1); Insert into #Platten(PlatzNr, X) Values(1, 2); Insert into #Platten(PlatzNr, X) Values(1, 3); Select ID, PlatzNr, ROW_NUMBER() OVER(PARTITION BY PlatzNr ORDER BY X, ID Desc) as LfdNr, X from #Platten order by PlatzNr, LfdNr; Insert into #Platten(PlatzNr, X) Values(1, 2); Select ID, PlatzNr, ROW_NUMBER() OVER(PARTITION BY PlatzNr ORDER BY X, ID Desc) as LfdNr, X from #Platten order by PlatzNr, LfdNr; Drop Table #Platten; go
Und diese Variante basiert auf den Daten in Elmars Posting:
Create Table #Platten(ID Integer Identity, PlatzNr integer, X integer); -- Weitere Einfügen (siehe Elmars Posting) INSERT INTO #Platten(PlatzNr, X) VALUES (1, 1), (1, 2), (1, 4), (1, 3), (2, 1), (2, 2); Select ID, PlatzNr, ROW_NUMBER() OVER(PARTITION BY PlatzNr ORDER BY X, ID Desc) as LfdNr, X from #Platten order by PlatzNr, LfdNr; INSERT INTO #Platten(PlatzNr, X) VALUES (1, 2), (1, 5), (2, 2), (2, 4); Select ID, PlatzNr, ROW_NUMBER() OVER(PARTITION BY PlatzNr ORDER BY X, ID Desc) as LfdNr, X from #Platten order by PlatzNr, LfdNr; -- Löschen With NumberedRows as (Select ID, PlatzNr, ROW_NUMBER() OVER(PARTITION BY PlatzNr ORDER BY X, ID Desc) as LfdNr, X from #Platten ) DELETE NumberedRows WHERE PlatzNr = 1 AND LfdNr IN (3, 4); Select ID, PlatzNr, ROW_NUMBER() OVER(PARTITION BY PlatzNr ORDER BY X, ID Desc) as LfdNr, X from #Platten order by PlatzNr, LfdNr; -- Verschieben With NumberedRows as (Select ID, PlatzNr, ROW_NUMBER() OVER(PARTITION BY PlatzNr ORDER BY X, ID Desc) as LfdNr, X from #Platten ) UPDATE NumberedRows SET PlatzNr = 3 WHERE PlatzNr = 2 AND LfdNr IN (1, 4); Select ID, PlatzNr, ROW_NUMBER() OVER(PARTITION BY PlatzNr ORDER BY X, ID Desc) as LfdNr, X from #Platten order by PlatzNr, LfdNr; Drop Table #Platten;
Nachteil bei der Variante ohne Trigger: Gelegentlich sind CTEs notwendig (hier NumberedRows).
Nachteil beim Trigger: Es werden mehr Sperren und IO erzeugt und daher ist die Laufzeit schlechter, was bei steigender Datensatzanzahl zunimmt.Einen schönen Tag noch,
Christoph
--
Microsoft SQL Server MVP - http://www.insidesql.org/blogs/cmu -
Danke euch... Ich werde es vermutlich doch in C# Lösen. Ist weniger aufwähndig und bietet doch schon mehr Möglichkeiten... Außerdem beim hin und her Probieren habe ich festgestellt, dass die DB zu selbständig ist. Hinterher weis keiner mehr was da passiert...
Dennoch vielen Dank euch für's mitmachen. Hab doch was draus gelernt...
-
Hallo Eugen,
ich sehe nicht, was an der einen Anweisung - mit oder ohne Trigger - so schwierig ist.
Zumal ich das mit C# in LINQ fast genauso machen würde, mit dem enormen Nachteil, dass dabei jede Zeile einzeln verändert würde - langsam und deutlich fehleranfälliger.
Nicht zuletzt... wenn man es in einem externen Programm löst, weiß am Ende bestimmt keiner mehr, was da passiert...
Gruß Elmar
-
Hallo Eugen,
wenn Du es in C# löst, gilt es nur für eine Anwendung. Der Trigger würde zumindest sicherstellen, dass bei allen Aktionen das gleiche rauskommt.Allerdings kann ich die Befürchtung vor zu viel Selbstständigkeit verstehen und würde die Lösung ohne Trigger über eine View abfedern, die dann die richtige Reihenfolge liefert. Dann müssen nur alle Anwendungen diese View verwenden.
Einen schönen Tag noch,
Christoph
--
Microsoft SQL Server MVP - http://www.insidesql.org/blogs/cmu -