In diesem Beispiel erfahren Sie, wie Sie einen skriptgesteuerten Upload mit Konflikterkennung einrichten. In dem Beispiel werden die konsolidierte und die entfernte Datenbank, gespeicherte Prozeduren, Publikationen und Subskriptionen erstellt, die vom skriptgesteuerten Upload gefordert werden. Sie können entweder das Beispiel einfach durchlesen oder den Text ausschneiden und einfügen, um das Beispiel auszuführen.
Erstellen Sie ein Verzeichnis für die Beispieldateien. Nennen Sie es beispielsweise scriptedupload. Öffnen Sie eine Eingabeaufforderung und gehen Sie zu dem Verzeichnis.
(In diesem Beispiel werden die Dateinamen angegeben und es wird davon ausgegangen, dass sich die Dateien in demselben Verzeichnis befinden. In einer Echtanwendung müssen Sie den vollen Pfad angeben.)
Geben Sie folgenden Befehl ein, um eine konsolidierte Datenbank zu erstellen:
dbinit consol.db |
Führen Sie dann den folgenden Befehl aus, um eine ODBC-Datenquelle für die konsolidierte Datenbank zu definieren:
dbdsn -w dsn_consol -y -c "uid=DBA;pwd=sql;dbf=consol.db;server=consol" |
Um eine Datenbank als konsolidierte MobiLink-Datenbank zu verwenden, müssen Sie ein Setup-Skript ausführen, das Systemtabellen, Ansichten und gespeicherte Prozeduren hinzufügt, die von MobiLink verwendet werden. Der folgende Befehl richtet "consol.db" als konsolidierte Datenbank ein:
dbisql -c "dsn=dsn_consol" %SQLANY12%\MobiLink\setup\syncsa.sql |
Öffnen Sie Interactive SQL und verbinden Sie "consol.db" mit dem DSN dsn_consol. Führen Sie die folgenden SQL-Anweisungen aus. Sie erstellen die Tabelle employee in der konsolidierten Datenbank, fügen Werte in die Tabelle ein und erstellen die erforderlichen Synchronisationsskripten.
CREATE TABLE employee ( id unsigned integer primary key, name varchar( 256), salary numeric( 9, 2 ) ); INSERT INTO employee VALUES( 100, 'smith', 225000 ); COMMIT; CALL ml_add_table_script( 'default', 'employee', 'upload_insert', 'INSERT INTO employee ( id, name, salary ) VALUES ( {ml r.id}, {ml r.name}, {ml r.salary} )' ); CALL ml_add_table_script( 'default', 'employee', 'upload_update', 'UPDATE employee SET name = {ml r.name}, salary = {ml r.salary} WHERE id = {ml r.id}' ); CALL ml_add_table_script( 'default', 'employee', 'upload_delete', 'DELETE FROM employee WHERE id = {ml r.id}' ); CALL ml_add_table_script( 'default', 'employee', 'download_cursor', 'SELECT * from employee' ); |
Führen Sie an der Eingabeaufforderung in Ihrem Beispielverzeichnis den folgenden Befehl aus, um die entfernte Datenbank zu erstellen.
dbinit remote.db |
Führen Sie dann den folgenden Befehl aus, um eine ODBC-Datenquelle zu definieren:
dbdsn -w dsn_remote -y -c "uid=dba;pwd=sql;dbf=remote.db;server=remote" |
Stellen Sie in Interactive SQL mit dem DSN dsn_remote eine Verbindung zu "remote.db" her. Führen Sie folgende Anweisungen aus, um in der entfernten Datenbank Objekte zu erstellen.
Erstellen Sie zunächst die Tabelle, die synchronisiert werden soll. Die Spalten insert_time und delete_time werden nicht synchronisiert, enthalten aber Informationen, die von der gespeicherten Upload-Prozedur verwendet werden, um festzulegen, welche Zeilen hochgeladen werden sollen.
CREATE TABLE employee ( id unsigned integer primary key, name varchar( 256), salary numeric( 9, 2 ), insert_time timestamp default '1900-01-01' ); |
Als nächstes müssen Sie gespeicherte Prozeduren und andere Komponenten definieren, um den Upload durchzuführen. Dies geschieht für Einfügungen, Aktualisierungen und Löschungen getrennt.
Erstellen Sie zunächst einen Trigger, um insert_time für jede Zeile festzulegen, wenn sie eingefügt wird. Dieser Zeitstempel wird verwendet, um festzustellen, ob seit der letzten Synchronisation eine Zeile eingefügt wurde. Dieser Trigger wird nicht ausgelöst, wenn dbmlsync übertragene Einfügungen aus der konsolidierten Datenbank anwendet, da Sie später in diesem Beispiel die erweiterte Option FireTriggers deaktivieren werden. Zeilen, die durch den Download eingefügt werden, erhalten den insert_time-Wert "1900-01-01", also den Standardwert, der bei der Erstellung der Tabelle employee definiert wurde. Dieser Wert sollte immer vor dem Beginn des Verarbeitungsfortschritt liegen, damit solche Zeilen nicht als neue Einfügungen behandelt und bei der nächsten Synchronisation nicht hochgeladen werden.
CREATE TRIGGER emp_ins AFTER INSERT ON employee REFERENCING NEW AS newrow FOR EACH ROW BEGIN UPDATE employee SET insert_time = CURRENT TIMESTAMP WHERE id = newrow.id END; |
Erstellen Sie als Nächstes eine Prozedur, die als Ergebnismenge alle eingefügten Zeilen zurückgibt, die Teil des Uploads sein sollen. Diese Prozedur gibt alle Zeilen zurück, die basierend auf insert_time seit dem letzten erfolgreichen Upload eingefügt und anschließend nicht gelöscht wurden. Der Zeitpunkt des letzten erfolgreichen Uploads wird durch den Wert des Start-Verarbeitungsfortschritts in der Tabelle #hook_dict definiert. Dieses Beispiel benutzt die Standardeinstellung für die erweiterte dbmlsync-Option LockTables, sodass dbmlsync die zu synchronisierenden Tabellen sperrt. Daher brauchen Sie keine Zeilen auszuschließen, die nach dem Verarbeitungsfortschritts-Abschluss eingefügt wurden. Die Tabellensperren verhindern jegliche Vorgänge nach dem Verarbeitungsfortschritts-Abschluss, während der Upload durchgeführt wird.
CREATE PROCEDURE employee_insert() RESULT( id unsigned integer, name varchar( 256 ), salary numeric( 9,2 ) ) BEGIN DECLARE start_time timestamp; SELECT value INTO start_time FROM #hook_dict WHERE name = 'start progress as timestamp'; // Upload as inserts all rows inserted after the start_time // that were not subsequently deleted SELECT id, name, salary FROM employee e WHERE insert_time > start_time AND NOT EXISTS( SELECT id FROM employee_delete ed WHERE ed.id = e.id ); END; |
Bei Aktualisierungen müssen Sie zunächst sicherstellen, dass während des Uploads das korrekte Pre-Image basierend auf dem Start-Verarbeitungsfortschritt verwendet wird.
Erstellen Sie zunächst eine Tabelle, in der die Pre-Images der aktualisierten Zeilen verwaltet werden. Die Pre-Images werden für das Generieren der skriptgesteuerten Uploads benötigt.
CREATE TABLE employee_preimages ( id unsigned integer NOT NULL, name varchar( 256), salary numeric( 9, 2 ), img_time timestamp default CURRENT TIMESTAMP, primary key( id, img_time ) ); |
Erstellen Sie dann einen Trigger, um ein Pre-Image für jede Zeile zu speichern, wenn sie aktualisiert wird. Wie bereits der Insert-Trigger wird dieser Trigger beim Download nicht ausgeführt.
Beachten Sie, dass dieser Trigger ein Pre-Image der Zeile speichert, sobald die Zeile aktualisiert wird (Ausnahme: Zwei Aktualisierungen liegen so nah beieinander, dass sie denselben Zeitstempel erhalten). Auf den ersten Blick erscheint dies überflüssig. Man könnte versucht sein, ein Pre-Image nur dann zu speichern, wenn dieses für die entsprechende Zeile in der Tabelle nicht existiert, und dann mithilfe des Hooks sp_hook_dbmlsync_upload_end die Pre-Images zu löschen, sobald sie heraufgeladen wurden.
Der Hook sp_hook_dbmlsync_upload_end ist hierfür jedoch nicht zuverlässig genug. Der Hook wird beispielsweise nicht aufgerufen, wenn dbmlsync aufgrund eines Hard- oder Softwarefehlers gestoppt wird, nachdem der Upload gesendet, aber noch nicht bestätigt wurde. Dies führt dazu, dass Zeilen nicht aus Pre-Image-Tabellen gelöscht werden, obwohl sie erfolgreich heraufgeladen wurden. Auch ein Verbindungsfehler kann dazu führen, dass dbmlsync keine Bestätigung für einen Upload vom Server erhält. In diesem Fall wird dem Hook der Upload-Status 'unknown' übergeben. Dann kann der Hook nicht erkennen, ob die Pre-Image-Tabelle bereinigt werden muss oder unverändert bleiben soll. Beim Speichern mehrerer Pre-Images kann das korrekte Pre-Image basierend auf dem Start-Verarbeitungsfortschritt beim Upload ausgewählt werden.
CREATE TRIGGER emp_upd AFTER UPDATE OF name,salary ON employee REFERENCING OLD AS oldrow FOR EACH ROW BEGIN INSERT INTO employee_preimages ON EXISTING SKIP VALUES( oldrow.id, oldrow.name, oldrow.salary, CURRENT TIMESTAMP ); END; |
Erstellen Sie als Nächstes eine Upload-Prozedur für das Bearbeiten von Aktualisierungen. Diese gespeicherte Prozedur gibt eine Ergebnismenge mit doppelt so vielen Spalten zurück wie andere Skripten: Sie enthält das Pre-Image (die Werte in der Zeile zu dem Zeitpunkt, als sie das letzte Mal vom MobiLink-Server empfangen oder erfolgreich heraufgeladen wurden) und das Post-Image (die Werte, die in die konsolidierte Datenbank eingegeben werden sollen).
Das Pre-Image ist die erste Wertemenge in employee_preimages, die nach start_progress aufgezeichnet wurde. Beachten Sie, dass in diesem Beispiel bestehende Zeilen, die gelöscht und erneut eingefügt werden, nicht korrekt verarbeitet werden. In einer komplexeren Lösung würden diese als Aktualisierung heraufgeladen werden.
CREATE PROCEDURE employee_update() RESULT( preimage_id unsigned integer, preimage_name varchar( 256), preimage_salary numeric( 9,2 ), postimage_id unsigned integer, postimage_name varchar( 256), postimage_salary numeric( 9,2 ) ) BEGIN DECLARE start_time timestamp; SELECT value INTO start_time FROM #hook_dict WHERE name = 'start progress as timestamp'; // Upload as an update all rows that have been updated since // start_time that were not newly inserted or deleted. SELECT ep.id, ep.name, ep.salary, e.id, e.name, e.salary FROM employee e JOIN employee_preimages ep ON ( e.id = ep.id ) // Do not select rows inserted since the start time. These should be // uploaded as inserts. WHERE insert_time <= start_time // Do not upload deleted rows. AND NOT EXISTS( SELECT id FROM employee_delete ed WHERE ed.id = e.id ) // Select the earliest pre-image after the start time. AND ep.img_time = ( SELECT MIN( img_time ) FROM employee_preimages WHERE id = ep.id AND img_time > start_time ); END; |
Erstellen Sie zunächst eine Tabelle, in der eine Liste der gelöschten Zeilen verwaltet wird.
CREATE TABLE employee_delete ( id unsigned integer primary key NOT NULL, name varchar( 256 ), salary numeric( 9, 2 ), delete_time timestamp ); |
Erstellen Sie dann einen Trigger, um die Tabelle employee_delete beim Löschen von Zeilen aus der Tabelle employee zu füllen. Dieser Trigger wird während eines Downloads nicht aufgerufen, da Sie später die erweiterte Option FireTriggers in dbmlsync auf FALSE setzen. Beachten Sie, dass dieser Trigger voraussetzt, dass eine gelöschte Zeile nicht wieder eingefügt wird. Daher können einmal gelöschte Zeilen nicht nochmals eingefügt werden.
CREATE TRIGGER emp_del AFTER DELETE ON employee REFERENCING OLD AS delrow FOR EACH ROW BEGIN INSERT INTO employee_delete VALUES( delrow.id, delrow.name, delrow.salary, CURRENT TIMESTAMP ); END; |
Die nächste SQL-Anweisung erstellt eine Upload-Prozedur für das Bearbeiten von Löschungen. Diese gespeicherte Prozedur gibt eine Ergebnismenge zurück, die die Zeilen enthält, die in der konsolidierten Datenbank gelöscht werden sollen. Die gespeicherte Prozedur verwendet die Tabelle employee_preimages. Wenn also eine Zeile aktualisiert und dann gelöscht wird, handelt es sich bei dem Image, das für die Löschung heraufgeladen wird, um das Image, das als Letztes erfolgreich herunter- oder heraufgeladen wurde.
CREATE PROCEDURE employee_delete() RESULT( id unsigned integer, name varchar( 256), salary numeric( 9,2 ) ) BEGIN DECLARE start_time timestamp; SELECT value INTO start_time FROM #hook_dict WHERE name = 'start progress as timestamp'; // Upload as a delete all rows that were deleted after the // start_time that were not inserted after the start_time. // If a row was updated before it was deleted, then the row // to be deleted is the pre-image of the update. SELECT IF ep.id IS NULL THEN ed.id ELSE ep.id ENDIF, IF ep.id IS NULL THEN ed.name ELSE ep.name ENDIF, IF ep.id IS NULL THEN ed.salary ELSE ep.salary ENDIF FROM employee_delete ed LEFT OUTER JOIN employee_preimages ep ON( ed.id = ep.id AND ep.img_time > start_time ) WHERE // Only upload deletes that occurred since the last sync. ed.delete_time > start_time // Don't upload a delete for rows that were inserted since // the last upload and then deleted. AND NOT EXISTS ( SELECT id FROM employee e WHERE e.id = ep.id AND e.insert_time > start_time ) // Select the earliest preimage after the start time. AND ( ep.id IS NULL OR ep.img_time = (SELECT MIN( img_time ) FROM employee_preimages WHERE id = ep.id AND img_time > start_time ) ); END; |
Erstellen Sie als nächstes den Hook upload_end, um die Einträge in den Tabellen employee_preimage und employee_delete nach einem erfolgreichen Upload zu löschen. Dieses Beispiel benutzt die Standardeinstellung für die erweiterte dbmlsync-Option LockTables, sodass die Tabellen während der Synchronisation gesperrt sind. Sie brauchen sich daher nicht darum zu kümmern, dass Zeilen in den Tabellen für Vorgänge zurückbleiben, die nach end_progress eingetreten sind. Durch die Sperre werden solche Vorgänge verhindert.
CREATE PROCEDURE sp_hook_dbmlsync_upload_end() BEGIN DECLARE val varchar(256); SELECT value INTO val FROM #hook_dict WHERE name = 'upload status'; IF val = 'committed' THEN DELETE FROM employee_delete; DELETE FROM employee_preimages; END IF; END; |
Die Publikation mit dem Namen "pub1" verwendet die skriptgesteuerte Upload-Syntax (WITH SCRIPTED UPLOAD). Sie erstellt einen Artikel für die Tabelle employee und registriert die drei gespeicherten Prozeduren, die Sie gerade zur Verwendung im skriptgesteuerten Upload erstellt haben. Sie erstellt außerdem einen MobiLink-Benutzer mit dem Namen "u1" und eine Subskription zwischen "v1" und "pub1". Die erweitere Option FireTriggers wird deaktiviert, um zu verhindern, dass Trigger in der entfernten Datenbank während des Downloads ausgelöst werden. Dadurch wird vermieden, dass heruntergeladene Änderungen bei der nächsten Synchronisation heraufgeladen werden.
CREATE PUBLICATION pub1 WITH SCRIPTED UPLOAD ( TABLE employee( id, name, salary ) USING ( PROCEDURE employee_insert FOR UPLOAD INSERT, PROCEDURE employee_update FOR UPLOAD UPDATE, PROCEDURE employee_delete FOR UPLOAD DELETE, ) ) CREATE SYNCHRONIZATION USER u1; CREATE SYNCHRONIZATION SUBSCRIPTION TO pub1 FOR u1 TYPE 'tcpip' ADDRESS 'host=localhost' OPTION FireTriggers='off'; SCRIPT VERSION 'default' |
Stellen Sie eine Verbindung mit der entfernten Datenbank her und fügen Sie die Daten, die synchronisiert werden sollen, mithilfe eines skriptgesteuerten Uploads ein. Führen Sie beispielsweise die folgenden SQL-Anweisungen in der entfernten Datenbank in Interactive SQL aus:
INSERT INTO employee(id, name, salary) VALUES( 7, 'black', 700 ); INSERT INTO employee(id, name, salary) VALUES( 8, 'anderson', 800 ); INSERT INTO employee(id, name, salary) VALUES( 9, 'dilon', 900 ); INSERT INTO employee(id, name, salary) VALUES( 10, 'dwit', 1000 ); INSERT INTO employee(id, name, salary) VALUES( 11, 'dwit', 1100 ); COMMIT; |
Starten Sie den MobiLink-Server an einer Eingabeaufforderung:
mlsrv12 -c "dsn=dsn_consol" -o mlserver.mls -v+ -dl -zu+ |
Starten Sie eine Synchronisation mit dbmlsync:
dbmlsync -c "dsn=dsn_remote" -k -uo -o remote.mlc -v+ |
Sie können jetzt prüfen, ob der Upload der Einfügungen erfolgt ist.
Wenn Sie Ihren Computer nach Abschluss dieses Beispiels bereinigen wollen, führen Sie den folgenden Befehl aus:
mlstop -h -w dbstop -y -c server=consol dbstop -y -c server=remote dberase -y consol.db dberase -y remote.db del remote.mlc mlserver.mls |
![]() |
Kommentieren Sie diese Seite in DocCommentXchange.
|
Copyright © 2010, iAnywhere Solutions, Inc. - SQL Anywhere 12.0.0 |