MSSQL: Trigger After SELECT?

rrobbyy

Mitglied
Hallo zusammen,

ich habe folgendes Problem. Unser Warenwirtschaftsprogramm nutzt eine MSSQL 2008 Datenbank. In diesem Programm ist es möglich Angebote, Rechnungen (Modul: "Vorgangsverwaltung") und Bestellungen zu diesen Angeboten beim Lieferanten auszulösen.

Im Hintergrund wird beim Abschicken der Bestellung ein PDF zur Archivierung beim Lieferanten hinterlegt (als physikalische Datei. Die Datei liegt in einem Verzeichnis des Lieferanten).

Auch die Angebote und Rechnungen besitzen kundenbezogene Verzeichnisse. Es gibt also auf physikalischer Ebene keine Verknüpfung vom Bestellwesen zur Vorgangsverwaltung).

In der Tabelle BESTELLWESEN gibt es die Möglichkeit Vorgänge (also Angebote, Rechnungen, etc.) einer Bestellung zuzuordnen.

Das Ziel ist, dass ich das physikalische Dokument der Bestellung in den Vorgangsordner kopieren möchte. Dafür habe ich folgenden Trigger auf der Tabelle BESTELLWESEN erstellt, der auch prinzipiell funktioniert:

Code:
Use DACH_Test
go

IF OBJECT_ID ('DokuKopie', 'TR') IS NOT NULL
   DROP TRIGGER DokuKopie
go

CREATE TRIGGER DokuKopie
on dbo.BESTELLWESEN
AFTER INSERT, UPDATE AS

	declare @Bestellnummer varchar(200), 
			@BestellVorgangNr varchar(200),
			@LieferantenNr varchar(10),
			@LieferantenAdrNR varchar(10),
			@LieferantenKurzname varchar(60),
			@VorgangKunde varchar(60),
			@VorgangKundeADRNR varchar(60),
			@VorgangKurzname varchar(60),
			@Tmp varchar(400),
			@Path1 varchar(200),
			@Path2 varchar(200),
			@DokuPath varchar(200)

	set @DokuPath = 'c:\Dokumente\' 


	--bearbeitete Bestellnummer / Vorgangsnummer / Lieferantennr. finden
	select @Bestellnummer = BESTELLNR from INSERTED
	print 'Bestellnr: ' + @Bestellnummer 
	
	select @BestellVorgangNr = VORGANG from INSERTED --where BESTELLWESEN.BESTELLNR = @Bestellummer
	print 'Vorgangsnr: ' + @BestellVorgangNr 
	
	select @VorgangKunde = VORGANG.ROWADRESSEN from VORGANG where VORGANG.VORGANGNR = @BestellVorgangNr
	select @VorgangKundeADRNR = ADRESSEN.ADRNR from ADRESSEN where ADRESSEN.ROWADRESSEN = @VorgangKunde
	print 'VorgangKunde: ' + @VorgangKundeADRNR 
	
	select @VorgangKurzname = ADRESSEN.KURZNAME from ADRESSEN where ADRESSEN.ROWADRESSEN = @VorgangKunde
	print 'Vorgang Name: ' + @VorgangKurzname 
	
	select @Path2 = @VorgangKurzname + '_' + @VorgangKundeADRNR + '\B_' + @BestellVorgangNr + '\Bestellungen'
	print 'Pfad: ' + @Path2 
	
	select @LieferantenNr = ROWADRESSENL from INSERTED where BESTELLNR = @Bestellnummer
	select @LieferantenAdrNR = ADRESSEN.ADRNR from ADRESSEN where ADRESSEN.ROWADRESSEN = @LieferantenNr
	print 'Lieferantennr: ' + @LieferantenAdrNR 
	
	select @LieferantenKurzname = ADRESSEN.KURZNAME from ADRESSEN where ADRESSEN.ROWADRESSEN = @LieferantenNr
	print 'LieferantenName: ' + @LieferantenKurzname 

	select @Path1 = @LieferantenKurzname + '_' + @LieferantenAdrNR + '\B_' + @Bestellnummer
	print 'Path1: ' + @Path1 

	set @Tmp = ' xcopy "' + @Dokupath + @Path1 + '\*.*" ' + ' "' + @Dokupath + @Path2 + '" /Y /C'
	print 'Befehl: ' + @Tmp
	--WAITFOR DELAY '000:00:02'
	EXEC xp_cmdshell @Tmp, no_output
go

Es werden die Dokumente von A nach B kopiert. Das Problem ist jedoch, dass der Trigger schneller ist, als das Erstellen der Datei. Sprich, das Programm ändert den STATUS der Bestellung und generiert erst dann das PDF, was widerum SELECTS auf der Datenbank verursacht.

Am Ende habe ich mit WAITFOR DELAY versucht, eine Pause einzubauen. Tja, das Ding macht aber tatächlich eine Pause. Nicht mal SELECTS werden in dieser Zeit ausgeführt.
Bekommt man es irgendwie hin, den EXEC Befehl eine Zeit X später auszuführen während die anderen Befehle weiterlaufen?

Ich hatte die Idee, dass ich nach einem bestimmten SELECT den EXEC-Befehl aufrufe, aber TRIGGER reagieren ja nur auf INSERT, UPDATE und DELETE.
 
SELECT trigger gibt es in MSSQL leider nicht.

Das Problem was du hier hast, ist dass der trigger als teil einer transaction abläuft und daher einen lock auf die daten hält.

Wenn du den datensatz der gerade geschrieben wird nicht brauchst im späteren verlauf, könntest du das transaction isolation level wechseln. Dadurch wird es dir ermöglicht dirty reads auf der datenbank auszuführen.

(SET TRANSACTION ISOLATION LEVEL ...)

Das kann jedoch an anderer stelle zu unerwarteten nebeneffekten führen, vorsicht also damit.

Die sinnvollste möglichkeit die mir für dieses Problem einfällt, wäre das erstellen eines jobs.

Hier hast du 2 möglichkeiten:
1.) du erstellst für jeden vorgang einen eigenen job, schedulest ihn, und löscht ihn wieder
2.) du trägst die aufgaben in eine tabelle ein, und läst einen job alle paar minuten laufen, der die aufgaben in der tabelle abarbeitet.
Beides funktioniert ähnlich.

hier die msdn kategorie der prozeduren dafür:
http://msdn.microsoft.com/en-us/library/ms187763.aspx
In besondere interessieren dürten dich:
sp_add_job
sp_add_jobstep
sp_add_jobschedule
 
Danke für die rasche Antwort.
Die Idee ist gar nicht mal so schlecht. Ich werde es probieren müssen, wie es läuft.

Vielleicht könnte ich auch den Trigger so erweitern, dass in einer separaten Tabelle ein Eintrag gemacht wird, und sobald dort ein INSERT geschieht ein weiterer Trigger ausgeführt wird, der den Kopiervorgang einige Zeit lang weiterholt. Jetzt ist die Frage, ob SQL parallel die Trigger verarbeiten kann, da ja meiner Meinung nach der zweite Trigger nicht zur Transaction gehört.

Ich habe jetzt noch eine weitere Idee. Ich könnte Datenbank-e-Mail einsetzen.
So viel ich weiß, kann man auch Anhänge damit versenden. Der Vorteil ist, dass der Projektleiter, soweit er erfasst wurde, sofort eine Mail erhält, vorausgesetzt, in den Stammdaten sind die Felder gefüllt.
 
doch der 2. trigger wäre teil der selben transaktion.

das insert in die 2. tabelle blockt solange bis der trigger der 2. tabelle abgearbeitet wurde.
was bedeutet, dass auch das erste insert solange blockt.
 
Zurück