chgs2011
Goto Top

SQL MERGE-Befehl . INSERT aller Datenzeilen verhindern, beschränkbar auf nur geänderte?

Hallo,

ich habe mir die Funktion MERGE die Tage angeschaut, die mir bislang neu war.

Der Syntax stammt von @4400667902
MERGE datev_eodb_mandanten_backup AS t
USING datev_eodb_mandanten AS s
ON		t.MDTGUID = s.MDTGUID
AND		t.MDTID = s.MDTID
WHEN	MATCHED
AND (	isnull(t.MDTVON,''1900-01-01 00:00:00.000'') != isnull(s.MDTVON,''1900-01-01 00:00:00.000'')  
OR		isnull(t.MDTBIS,''1900-01-01 00:00:00.000'') != isnull(s.MDTBIS,''1900-01-01 00:00:00.000'')  
OR		isnull(t.MDTSTATUS,0) != isnull(s.MDTSTATUS,0)
OR		isnull(t.MDTNR,0) != isnull(s.MDTNR,0)
OR		isnull(t.MDTNAME,'''') != isnull(s.MDTNAME,'''')  
OR		isnull(t.MDTTYP,'''') != isnull(s.MDTTYP,'''')  
OR		isnull(t.MDTPLZ,'''') != isnull(s.MDTPLZ,'''')  
OR		isnull(t.MDTORT,'''') != isnull(s.MDTORT,'''')  
OR		isnull(t.MDTSTR,'''') != isnull(s.MDTSTR,'''')  
OR		isnull(t.MDTGID,0) != isnull(s.MDTGID,0)
OR		isnull(t.MDTGNAME,'''') != isnull(s.MDTGNAME,'''')  
OR		isnull(t.DOHR,'''') != isnull(s.DOHR,'''') )  
THEN
	UPDATE
	SET		t.MDTVON = s.MDTVON,
			t.MDTBIS = s.MDTBIS,
			t.MDTSTATUS = s.MDTSTATUS,
			t.MDTNR = s.MDTNR,
			t.MDTNAME = s.MDTNAME,
			t.MDTTYP = s.MDTTYP,
			t.MDTPLZ = s.MDTPLZ,
			t.MDTORT = s.MDTORT,
			t.MDTSTR = s.MDTSTR,
			t.MDTGID = s.MDTGID,
			t.MDTGNAME = s.MDTGNAME,
			t.DOHR = s.DOHR
WHEN	NOT MATCHED BY TARGET
THEN
	INSERT(MDTGUID,MDTID,MDTVON,MDTBIS,MDTSTATUS,MDTNR,MDTNAME,MDTTYP,MDTPLZ,MDTORT,MDTSTR,MDTGID,MDTGNAME,DOHR)
	VALUES(s.MDTGUID,s.MDTID,s.MDTVON,s.MDTBIS,s.MDTSTATUS,s.MDTNR,s.MDTNAME,s.MDTTYP,s.MDTPLZ,s.MDTORT,s.MDTSTR,s.MDTGID,s.MDTGNAME,s.DOHR)
WHEN	NOT MATCHED BY SOURCE
THEN
	DELETE;


Ich habe nun festgestellt, dass wenn ich in der Tabelle EINEN Datensatz ändere, er mir ALLE Datensätze in die Zieltabelle kopiert, was bei 85.000 Zeilen dann Zeit frisst.
Werden anschließend einzelne Zeilen gelöscht/geändert/neu hinzugefügt, geht dies natürlich flux.

ABER, kann ich denn diesem MERGE-Befehl auch mitgeben, dass er NICHT alle Datenzeilen erst mal kopiert, sondern nur die sich ändern in der Tabelle?

Ich möchte den MERGE anstelle INSERT/UPDATE in einem Trigger verwenden.

Content-ID: 396263

Url: https://administrator.de/contentid/396263

Ausgedruckt am: 22.11.2024 um 22:11 Uhr

MadMax
Lösung MadMax 20.12.2018 aktualisiert um 16:55:18 Uhr
Goto Top
Hallo chgs2011,

Du vergleichst zwei komplette Tabellen, da werden dann eben auch die Daten der kompletten Tabelle eingetragen. Grundsätzlich kannst Du die Daten bei USING auch einschränken bzw. dir irgendwelche Abfragen zusammenschustern, z.B.:
MERGE datev_eodb_mandanten_backup AS t
USING (select d.* from datev_eodb_mandanten d join inserted i on i.ID = d.ID) AS s
...
Oder wenn es sowieso der Trigger auf datev_eodb_mandanten ist, dann gleich nur inserted verwenden.

Dann mußt Du aber achtgeben mit dem "when not matched by source", da werden dann nämlich alle Datensätze, die Du beim USING nicht mitgenommen hast, gelöscht, weil sie ja in dieser eingeschränkten Abfrage nicht existieren. Deswegen dürfte ein "when not matched by source" sowieso ziemlich sinnlos sein, wenn man SOURCE einschränkt.

Alternativ könntest Du auch beim "when not matched by target" noch die Zusatzbedingung dranhängen "and exists (select 1 from inserted where ID = s.ID)", dann werden nur die eingefügten Datensätze auch in datev_eodb_mandanten_backup eingefügt. Aber bei "matched" und "when not matched by source" hättest Du immer noch die komplette Überprüfung, was da wahrscheinlich auch nicht so ideal ist.

Ich würde beim USING einschränken und das "when not matched by source" rausnehmen.

Gruß, Mad Max
chgs2011
chgs2011 20.12.2018 um 19:02:20 Uhr
Goto Top
Danke dir für den Tipp, werde ich mal testen.

Habe heute auch etwas zu einer eher unbekannten Funktion gelesen, schon mal OUTPUT gehört?
Man kann wohl abfragen, welcher Befehl gefeuert wurde (UPDATE/INSERT), siehe Link.

Ich möchte ja nicht immer ALLE Daten prüfen, mir würden nur geänderte oder neue Daten ausreichen.
So füllt sich also die Tabelle nach und nach ...

Ich überlege daher, ob ich vllt. in Kombination damit die Performance optimieren kann.

https://saschalorenz.blogspot.com/2011/06/merge-in-t-sql-der-unbekannte- ...
ukulele-7
ukulele-7 21.12.2018 um 09:05:05 Uhr
Goto Top
Da du das ganze aus einem Trigger heraus ausführst würde ich einfach nur Inserted als Quelltabelle heranziehen und alles aus Inserted mit der Zieltabelle mergen. Das würde nur die Datensätze betreffen, die geändert wurden. Du dürftest nur mit MERGE nichts löschen in der Zieltabelle, denn das würde ja jedesmal alles löschen was nicht in Inserted steht.
chgs2011
chgs2011 21.12.2018 aktualisiert um 09:26:01 Uhr
Goto Top
Danke dir, ich komme heute hoffentlich noch dazu den Rest zu testen, muss auch paar Testcases durchspielen ob alles passt.

Das brachte aber bereits schon einen Teilerfolg, dass nicht alles mit kommt.
USING (select d.* from datev_eodb_mandanten d join inserted i on i.ID = d.ID) AS s
ukulele-7
ukulele-7 21.12.2018 um 09:37:23 Uhr
Goto Top
Das Ergebnis ist das selbe wenn du die Quelltabelle mit Instered joinst als wenn du nur Inserted verwendest, jedenfalls wenn der Trigger nach dem INSERT UPDATE feuert.