Erkennen von Änderungen in einer SQL Server-Tabelle

13

In meiner Anwendung mit einer Datenbank, die unter SQL Server 2012 ausgeführt wird, habe ich einen Job (geplante Aufgabe), der regelmäßig eine teure Abfrage ausführt und die Ergebnisse in eine Tabelle schreibt, die später von der Anwendung abgefragt werden kann.

Im Idealfall möchte ich diese teure Abfrage nur ausführen, wenn sich seit der letzten Ausführung der Abfrage etwas geändert hat. Da die Quelltabellen sehr groß sind, kann ich nicht einfach eine Prüfsumme über alle Kandidatenspalten oder ähnliches auswählen.

Ich habe folgende Ideen:

  • Schreiben Sie einen zuletzt geänderten Zeitstempel, ein "Muss-Abfragen" -Flag oder so etwas explizit in eine Verfolgungstabelle, wenn ich etwas in einer Quelltabelle ändere.
  • Verwenden Sie einen Auslöser, um dasselbe zu tun.

Ich möchte jedoch wirklich wissen, ob es eine einfache Möglichkeit gibt, Änderungen an einer Tabelle zu erkennen, ohne dass ich die Schreibvorgänge explizit nachverfolge. Kann ich zum Beispiel den "aktuellen" Stand ROWVERSIONeines Tisches oder ähnliches abrufen?

Fabian Schmied
quelle

Antworten:

14

Nein, es gibt keine. Jede Art von Verfolgung der letzten Aktualisierung um würde zu einem schwerwiegenden Leistungsproblem führen, da alle Aktualisierungen aller Transaktionen versuchen würden, den einen Datensatz zu aktualisieren, der die Verfolgung der letzten Aktualisierung um verfolgt. Dies würde effektiv bedeuten, dass zu jedem Zeitpunkt nur eine Transaktion die Tabelle aktualisieren kann, und alle anderen Transaktionen müssen warten, bis die erste festgeschrieben wird . Vollständige Serialisierung. Die Anzahl der Administratoren / Entwickler, die bereit sind, solche Performance-Einbußen hinzunehmen, um zu wissen, wann das letzte Update durchgeführt wurde, ist wahrscheinlich gering.

Sie sind also gestrandet, um mit benutzerdefiniertem Code umzugehen. Dies bedeutet Trigger, da die Alternative (Erkennen anhand von Protokolldatensätzen) ein Vorrecht ist, das nur für die Transaktionsreplikation reserviert ist (oder es ist CDC- Alter-Ego). Beachten Sie, dass Sie genau das oben erwähnte Serialisierungsproblem haben, wenn Sie versuchen, es über eine Spalte "Zuletzt aktualisiert um" zu verfolgen. Wenn die gleichzeitige Aktualisierung wichtig ist, müssen Sie einen Warteschlangenmechanismus verwenden (der Auslöser verwendet ein INSERT, und dann aggregiert ein Prozess die eingefügten Werte, um das zuletzt aktualisierte Datum zu formulieren). Versuchen Sie nicht, mit einer 'cleveren' Lösung zu schummeln, wie zum Beispiel die aktuelle Identität zu überprüfen oder sys.dm_db_index_usage_stats nachzuschlagen . Und auch eine 'updated_at'-Spalte pro Datensatz, wie Rails-Zeitstempel,

Gibt es eine "leichte" Alternative? Eigentlich gibt es eines, aber es ist schwierig zu sagen, ob es für Sie funktioniert und es ist schwierig, es richtig zu machen: Benachrichtigungen abfragen . Abfragebenachrichtigung genau das tut, wird es eine Benachrichtigung einrichten, wenn jede Datenänderungen hat und Sie müssen Ihre Abfrage aktualisieren. Obwohl die meisten Entwickler nur mit der .Net-Inkarnation als SqlDependency vertraut sind, kann die Abfragebenachrichtigung als langlebiger, beständiger Mechanismus zum Erkennen von Datenänderungen verwendet werden. Im Vergleich zu echtem Änderungs-Tracking wird es sehr leicht sein, und seine Semantik entspricht eher Ihren Anforderungen (etwas, alles , was geändert wurde, also müssen Sie die Abfrage erneut ausführen).

Aber am Ende würde ich an Ihrer Stelle meine Annahmen wirklich überdenken und zum Zeichenbrett zurückkehren. Möglicherweise können Sie den Protokollversand oder die Replikation verwenden, um eine Berichtsdatenbank auf einem anderen Server einzurichten. Was ich zwischen den Zeilen gelesen habe, ist, dass Sie eine richtige ETL-Pipeline und ein Analytics Data Warehouse benötigen ...

Remus Rusanu
quelle
Warum sollte sich Microsoft also die Mühe machen, sys.dm_db_index_usage_stats zu erstellen, wenn sich die bereitgestellten Informationen nicht darauf verlassen können?
Craig Efrein
Es ist kein DMV, der für die Änderungsnachverfolgung entwickelt wurde . Ist sehr zuverlässig für den vorgesehenen Zweck, die Leistungsoptimierung.
Remus Rusanu
8

Es sieht so aus, als ob ich zwei Jahre zu spät zum Spiel komme, aber es gibt in der Tat eine ziemlich leichte Art, das zu tun, wonach du fragst.

Es gibt zwei SQL Server-Mechanismen, die Ihnen helfen können. Ihre ultimative Lösung könnte eine Mischung aus beidem sein.

Tracking ändern . SQL Server kann bestimmte Tabellen überwachen und nur aufzeichnen, welche Zeilen geändert wurden (anhand ihres Primärschlüsselwerts) und welche Art von Änderung (Einfügen, Aktualisieren oder Löschen). Sobald Sie die Änderungserkennung für eine Reihe von Tabellen eingerichtet haben, können Sie anhand einer übersichtlichen Abfrage feststellen, ob seit der letzten Überprüfung Änderungen an der Tabelle vorgenommen wurden. Der Aufwand entspricht in etwa der Verwaltung eines zusätzlichen einfachen Index.

Zeilenversion / Zeitstempel . Dies ist ein 8-Byte-Varbinary-Spaltentyp (auf BigInt umsetzbar), der datenbankweit inkrementiert wird, wenn eine Zeile mit einer solchen eingefügt oder aktualisiert wird (dies hilft nicht beim Löschen). Wenn Sie diese Spalten indiziert haben, können Sie leicht feststellen, ob sich die Zeilendaten geändert haben, indem Sie den MAX (Zeitstempel) mit seinem Wert seit der letzten Auswertung vergleichen. Da der Wert monoton ansteigt, erhalten Sie einen zuverlässigen Hinweis darauf, dass sich die Daten geändert haben, wenn der neue Wert größer ist als beim letzten Überprüfen.

Curt
quelle
7

Wenn die Quelle nur Einfügen ist, geben Sie ihr eine IDENTITYSpalte. Bei der Datenübertragung protokollieren Sie den höchsten übermittelten Wert. Bei der nächsten Übertragung müssen Sie nur nach Werten fragen, die höher sind als die bei der vorherigen Übertragung protokollierten. Dies tun wir, um Protokollsätze in ein Data Warehouse zu übertragen.

Fügen Sie für aktualisierbare Zeilen ein "Dirty" -Flag hinzu. Es hat drei Werte - sauber, schmutzig und gelöscht. Bei täglichen Abfragen müssen Zeilen mit dem Flag "gelöscht" weggelassen werden. Dies ist teuer in Wartung, Test und Laufzeit. Nach der großen Abfrage, die Sie erwähnen, müssen alle zum Löschen markierten Zeilen entfernt und das Flag für alle anderen zurückgesetzt werden. Dies wird nicht gut skalieren.

Eine leichtere Alternative zu Change Data Capture ist Change Tracking . Es wird nicht angezeigt, welche Werte geändert wurden, nur, dass sich die Zeile seit der letzten Abfrage geändert hat. Integrierte Funktionen erleichtern das Abrufen geänderter Werte und die Verwaltung der Nachverfolgung. Wir hatten Erfolg mit CT, um ungefähr 100.000 Änderungen pro Tag in einer 100.000.000-Zeilen-Tabelle zu verarbeiten.

Abfragebenachrichtigungen wirken immer noch auf einem höheren Hebel - auf der Ebene einer Ergebnismenge. Konzeptionell ist es so, als würde man eine Ansicht definieren. Wenn SQL Server feststellt, dass eine durch diese Ansicht zurückgegebene Zeile geändert wurde, wird eine Nachricht an die Anwendung gesendet. Es wird nicht angezeigt, wie viele Zeilen oder welche Spalten geändert wurden. Es gibt nur eine einfache Nachricht mit der Aufschrift "Etwas ist passiert". Es liegt an der Bewerbung, sich zu erkundigen und zu reagieren. Praktisch ist es viel komplexer als das, wie Sie sich vorstellen können. Es gibt Einschränkungen, wie die Abfrage definiert werden kann, und die Benachrichtigung wird möglicherweise für andere Bedingungen als geänderte Daten ausgelöst. Wenn die Benachrichtigung ausgelöst wird, wird sie entfernt. Wenn später weitere Aktivitäten von Interesse stattfinden, wird keine weitere Nachricht gesendet.

Im Zusammenhang mit der Frage des OP wird QN den Vorteil eines geringen Aufwands für die Einrichtung und geringer Laufzeitkosten haben. Es kann ein erheblicher Aufwand sein, ein strenges Regime für das Abonnieren und Reagieren von Nachrichten einzurichten und aufrechtzuerhalten. Da die Datentabelle groß ist, wird sie wahrscheinlich häufig geändert, was bedeutet, dass die Benachrichtigung in den meisten Verarbeitungszyklen wahrscheinlich ausgelöst wird. Da nicht angegeben ist, welche Änderungen vorgenommen wurden, ist eine inkrementelle Verarbeitung der Deltas nicht möglich, wie dies bei CT oder CDC der Fall wäre. Der Overhead durch falsches Auslösen ist lästig, aber auch im schlimmsten Fall muss die teure Abfrage nicht häufiger ausgeführt werden als derzeit.

Michael Green
quelle
3

SqlTableDependency

SqlTableDependency ist eine Implementierungskomponente auf hoher Ebene für den Zugriff auf Benachrichtigungen, die Tabellendatensatzwerte in der SQL Server-Datenbank enthalten.

SqlTableDependency ist eine generische C # -Komponente, mit der Benachrichtigungen empfangen werden, wenn sich der Inhalt einer angegebenen Datenbanktabelle ändert.

Was ist der Unterschied zu .NET SqlDepenency?

Grundsätzlich besteht der Hauptunterschied darin, dass SqlTableDependency Ereignisse sendet, die Werte für den in der Tabelle eingefügten, geänderten oder gelöschten Datensatz sowie für die ausgeführte DML-Operation (Einfügen / Löschen / Aktualisieren) enthalten: SqlDepenency sagt nicht, welche Daten in der Tabelle geändert wurden Datenbanktabelle, sie sagen nur, dass sich etwas geändert hat.

Schauen Sie sich das GITHUB-Projekt an .

Christian Del Bianco
quelle
1

Wenn sich die erwarteten Aktualisierungen auf einen Index auswirken (und nur dann), können Sie mithilfe der sys.dm_db_index_usage_statsSystemtabelle die letzte Aktualisierung eines Indexes für die betreffende Tabelle ermitteln. Du würdest das last_user_updateFeld benutzen .

So rufen Sie beispielsweise die zuletzt aktualisierten Tabellen ab:

select
    object_name(object_id) as OBJ_NAME, *
from
    sys.dm_db_index_usage_stats
where
    database_id = db_id(db_name())
order by
    dm_db_index_usage_stats.last_user_update desc

Oder um zu überprüfen, ob eine bestimmte Tabelle seit einem bestimmten Datum geändert wurde:

select
    case when count(distinct object_id) > 0 then 1 else 0 end as IS_CHANGED
from
    sys.dm_db_index_usage_stats
where
    database_id = db_id(db_name())
    and object_id = object_id('MY_TABLE_NAME')
    and last_user_update > '2016-02-18'
Geoff
quelle
Was ist deine Meinung zu Remus 'Kommentar oben? "Versuchen Sie nicht, mit einer 'cleveren' Lösung zu schummeln, wie sich an die aktuelle Identität zu gewöhnen oder sys.dm_db_index_usage_stats nachzuschlagen." (Siehe auch seinen Kommentar unter seiner Antwort.)
Fabian Schmied
1
@FabianSchmied Interessant - Ich hatte nicht gesehen, dass ich, als ich meine Antwort hinzufügte, nichts aussagekräftiges außer einer anderen Antwort von Remus finden konnte, um anzuzeigen, dass es für diesen Anwendungsfall unzuverlässig ist. Auf der MS-Seite für dm_db_index_operational_statswerden Probleme angezeigt (die beim Löschen des Metadaten-Cache gelöscht wurden), nicht jedoch für dm_db_index_usage_stats. Das einzige Problem, das ich fand, war die Indexwiederherstellung, der Serverneustart und die Datenbankfreigabe, wodurch die Verwendungsstatistiken gelöscht wurden. Dies schien hier nicht zutreffend zu sein. Würde mich über fundierte Infos dazu freuen.
Geoff