Starten eines Python-Skripts über einen Einfügetrigger

10

Wir haben ein schönes Stück Python, das einige E-Mails sendet und mit einem Cloud-System interagiert. Funktioniert gut. Aber wir müssen es alle paar Minuten abfeuern, um die Datenbank abzufragen. Aus geschäftlichen Gründen muss das Python-Skript wirklich in Echtzeit ausgelöst werden, damit keine Abfrageverzögerung auftritt. (Dies dient Verkäufern, die mit Kunden telefonieren.)

Wir wollen wirklich keine 1-minütige Abfrageschleife. Oder 30 Sekunden. Wir möchten, dass die Aufzeichnung in der Datenbank angezeigt wird und dass die Dinge sofort geschehen.

Der schnelle Weg, um diese Fliege zu machen, besteht darin, sie auslösen zu lassen, wenn ein bestimmter Datensatztyp in eine Tabelle eingefügt wird.

Können wir ein Python-Skript über einen Trigger auslösen?

Gemäß Aarons Anmerkung unten wissen wir, dass dies eine sehr schlechte Sache ist , aber diese Tabelle wird nur sehr wenig genutzt (0-12 Einfügungen pro Tag). Das Abrufen der Tabelle entspricht nicht unseren geschäftlichen Anforderungen (die .py muss sofort ausgeführt werden - sie reicht weit mehr als das Senden einer E-Mail).

Wir glauben, dass eine Möglichkeit, unsere geschäftlichen Anforderungen zu erfüllen, darin besteht, die .net-Version von Python auf dem SQL Server einzurichten und dann T-SQL das Python-Skript so aufrufen zu lassen, wie es C # -Material aufruft ... aber wir haben keine Ahnung, wie mach das eigentlich! (ergo diese Frage).

Dokumente / Details?


Ich habe eine Folgefrage zum Stapelüberlauf gestellt: Wie erstelle ich eine Python-CLR-Prozedur in SQL Server?


Die Frage unter der Frage : Sie haben ein Stück Python. Sie möchten, dass es von einem SQL-Trigger ausgelöst wird, aber Sie wissen, dass dies eine sehr schlechte Sache ist. Was tun Sie also, um denselben Effekt zu erzielen, ohne Python-Code mitten in einer SQL-Operation zu haben?

Was ist der nicht auslösende, nicht abfragende Ansatz zur Lösung dieses Bedarfs?

(Der gleiche Effekt = "Einfügen / Aktualisieren / Löschen tritt in einer Tabelle auf und ein Python-Skript wird innerhalb von 2 Sekunden nach dem Datenbankereignis ausgelöst, ohne die Tabelle abzufragen")

Jonesome stellt Monica wieder her
quelle
Sie ändern die Frage fünf Jahre später? Voller Konflikte. Das Abrufen der Tabelle entspricht nicht Ihren geschäftlichen Anforderungen, da der py sofort ausgeführt werden muss. Im Update wird jedoch eine Verzögerung von 2 Sekunden akzeptiert. Verwirrend. Wenn eine Verzögerung von 2 Sekunden akzeptabel ist, wird die Tabelle meiner Meinung nach abgefragt.
Aaron Bertrand
1
@ AaronBertrand Ich stimme zu, dass diese Frage nicht jedermanns Sicht der Realität entspricht. Aber wenn wir uns einen Moment Zeit nehmen und davon ausgehen, dass der Fragesteller intelligent und aufrichtig in seinem Bedürfnis nach einer nicht-tatsächlichen Auslöser-aber-handelt-wie-ein-Auslöser-Suche ist, können wir (als SE-Community) entweder helfen, einen Weg zu finden fwd (oder lehne die Frage ab, die das Bedürfnis / Problem nicht wirklich verschwinden lässt). fwiw.
Jonesome Reinstate Monica
Das ist in Ordnung, aber Sie müssen auswählen, welches Problem gelöst werden soll, und dann die Frage beheben (oder vielleicht eine neue beginnen, wenn die Antwort, die Sie vor 5 Jahren erhalten haben, akzeptabel war, aber heute nicht mehr akzeptabel ist, ob dies daran liegt, dass sich Ihre Anforderungen seitdem geändert haben ). Derzeit sagen Sie, dass Sie keine Abfrage oder einen Auslöser wünschen , und Sie sagen auch, dass dies sofort erfolgen muss und eine Verzögerung von 2 Sekunden in Ordnung ist.
Aaron Bertrand
Dies ist ein Szenario, in dem NoSQL im Gegensatz zu DBMS nicht nützlich ist, da DBMS Trigger verwalten und als Anwendungsschicht (mehr als ein Datenspeicher)
beitragen kann
@samsmith Hast du diese Antwort durchgesehen?
Überaustausch

Antworten:

12

Lassen Sie Ihre Benutzertransaktion nicht auf den (hoffentlich!) Erfolgreichen Abschluss des Python-Skripts warten. Ihre gesamte Transaktion befindet sich dort und wartet darauf, dass dieser externe Prozess ausgeführt wird, versucht wird, E-Mails zu senden usw. Ich bezweifle, dass die E-Mail wirklich sofort gelöscht werden muss - insbesondere, da Sie keine Verzögerungen kontrollieren können, die beim Weiterleiten auftreten sowieso in den Posteingang des Empfängers. Warum nicht einfach den Prozess häufiger ausführen, wenn das Timing so wichtig ist?

Bitte schauen Sie sich diesen Tipp an .

Wenn Sie dies wirklich, wirklich, wirklich falsch machen möchten, können Sie es einfach aktivieren xp_cmdshellund abfeuern.

EXEC sp_configure 'show advanced options', 1;
GO
RECONFIGURE WITH OVERRIDE;
GO
EXEC sp_configure 'xp_cmdshell', 1;
GO
RECONFIGURE WITH OVERRIDE;
GO

Angenommen, der Benutzer hat Zugriff auf xp_cmdshellund / oder das SQL Server-Dienstkonto kann den Ordner sehen, in dem das Python-Skript gespeichert ist, sollten Sie dies über Ihren Trigger tun können:

EXEC master..xp_cmdshell N'C:\Python27\python.exe C:\source\NotifyAgents.py';

Als beiseite, sollten Sie in Ihrer Frage sagen , dass Sie sich bewusst , dass dies eine sehr schlechte Sache TM , aber Sie sind nicht mit dem betreffenden, aus welchem Grunde. Ich glaube immer noch nicht, dass Sie so viel Echtzeit bekommen, wie Sie erwarten, selbst wenn Sie dies vom Abzug aus abfeuern. Haben Sie Datenbank-Mail anstelle von Python in Betracht gezogen?

Aaron Bertrand
quelle
Aaron, alle deine Punkte sind gültig, aber das ist mein Problem. Ich möchte Python von einem Trigger aus abfeuern und habe kein Problem mit den Problemen. (Ich kann den Trigger auf der realen Tabelle einen Wert in eine "Job" -Tabelle schieben lassen, und einen Trigger auf der "Job" -Tabelle Python ausführen ...)
Jonesome Reinstate Monica
4
Auch Ihr Trigger -> andere Tabelle -> andere Trigger-Idee leidet immer noch unter dem gleichen Problem, nur dass es jetzt schlimmer ist. Die ursprüngliche Transaktion muss noch warten, bis alle diese kaskadierenden Aktivitäten abgeschlossen sind.
Aaron Bertrand
Guter Anruf RE kaskadiert das Problem. Ich werde nicht dorthin gehen!
Jonesome Reinstate Monica
OP verbessert, um die Frage neu zu formulieren
Jonesome Reinstate Monica
2

"Einfügen / Aktualisieren / Löschen erfolgt in einer Tabelle und ein Python-Skript wird innerhalb von 2 Sekunden nach dem DB-Ereignis ausgelöst.

Wenn Sie zunächst einen Trigger verwenden, um eine Nachricht in eine für diesen Zweck bestimmte Tabelle zu schreiben, können Sie den Pooling-Prozess kontinuierlich mit einer Wartezeit von 1 Sekunde oder sogar weniger ausführen. Der Schlüssel besteht darin, die Abfrageabfrage billig genug zu machen (<1 ms) und keine andere Transaktion (also die dedizierte "Warteschlangentabelle") zu stören.

ZB lassen Sie Ihren Abrufprozess einen Stapel wie folgt ausführen:

declare @TriesRemaining int = 25
while not exists (select * from queue_table)
begin
  if @TriesRemaining <= 0
    break;
  set @TriesRemaining -= 1
  waitfor delay '0:0:1'
end
delete top (1)  
from queue_table
output deleted.*

Warten Sie bis zu 25 Sekunden, bis eine Zeile in der Tabelle angezeigt wird, und rufen Sie jede Sekunde ab. Bei einer Zeitüberschreitung wird einfach eine leere Ergebnismenge zurückgegeben.

ohne die Tabelle abzufragen

Am einfachsten ist es dann, Service Broker zusammen mit einer internen Aktivierungsprozedur zu verwenden, die Python über xp_cmdshell aufruft, oder einem externen Prozess, der ein blockierendes RECEIVE in der Ziel-Service-Broker-Warteschlange durchläuft. So funktioniert Database Mail unter der Haube.

David Browne - Microsoft
quelle
Es gibt einen schönen detaillierten Artikel über das Service Broker-Setup .
Mustaccio
2

Um die Auswirkungen der synchronen Ausführung des Python-Skripts von Ihrem Trigger aus zu minimieren, können Sie Ihren Python-Code in Folgendes einschließen BaseHTTPServer:

import BaseHTTPServer

class MyHTTPHandler(BaseHTTPServer.BaseHTTPRequestHandler):
    def do_POST(self):
        print "Serving %s" % self.path
        # Your code here
        self.send_response(200, "OK")

def run(server_class=BaseHTTPServer.HTTPServer,
        handler_class=MyHTTPHandler):
    server_address = ('', 8000)
    httpd = server_class(server_address, handler_class)
    httpd.serve_forever()

if __name__ == "__main__":
    run()

Sie können dann eine HTTP-Anforderung von Ihrem Trigger an den obigen Daemon senden, wie beispielsweise in dieser SO-Q & A gezeigt . Der Anforderungshandler kann sogar einen separaten Thread zum asynchronen Ausführen Ihrer Python-Logik erzeugen.

mustaccio
quelle
Gute Antwort!!! Die meisten der von mir bearbeiteten Mg-Anwendungen Mitte der 90er Jahre haben die Datenbank mit einem Abfrageintervall abgefragt.
Überaustausch