2012-09-19 10 views
5

Ich habe ein bisschen eine "upsert" Art von Frage ... aber ich will es rauswerfen, weil es ein bisschen anders ist als alle, die ich gelesen habe Paketüberfluss.Wie kann ich feststellen, ob sich der Datensatz in Postgres geändert hat?

Grundproblem.

Ich arbeite an der Migration von MySQL zu PostgreSQL 9.1.5 (auf Heroku gehostet). Als Teil davon muss ich jeden Tag mehrere CSV-Dateien importieren. Einige der Daten sind Verkaufsinformationen und sind fast garantiert neu und müssen eingefügt werden. Aber andere Teile der Daten sind fast garantiert identisch. Zum Beispiel enthalten die CSV-Dateien (Anmerkung Plural) POS (Point of Sale) Informationen in ihnen. Dies ändert sich selten (und ist höchstwahrscheinlich nur über Ergänzungen). Dann gibt es Produktinformationen. Es gibt ungefähr 10.000 Produkte (die überwiegende Mehrheit wird unverändert bleiben, aber es ist möglich, sowohl Ergänzungen als auch Aktualisierungen zu haben).

Der letzte Punkt (aber wichtig) ist, dass ich eine Anforderung haben muss, um einen Audit-Trail/Informationen für einen bestimmten Artikel bereitzustellen. Zum Beispiel, wenn ich einen neuen POS-Datensatz hinzufüge, muss ich in der Lage sein, diesen zurück zu der Datei zu verfolgen, in der er gefunden wurde. Wenn ich einen UPC-Code oder eine Beschreibung eines Produktes ändere, muss ich es zurückverfolgen können zum Import (und zur Datei), woher die Änderung kam.

Lösung, die ich erwäge.

Da mir die Daten per CSV zur Verfügung gestellt werden, arbeite ich daran, dass COPY der beste/schnellste Weg ist. Die Struktur der Daten in den Dateien ist nicht genau das, was ich in der Datenbank habe (d. H. Endgültiges Ziel). Also kopiere ich sie in Tabellen im Staging-Schema, die mit der CSV übereinstimmen (Anmerkung: ein Schema pro Datenquelle). Die Tabellen in den Staging-Schemas haben eine Trigger-Zeile vor dem Einfügen. Diese Trigger können entscheiden, was mit den Daten geschehen soll (Einfügen, Aktualisieren oder Ignorieren).

Für die Tabellen, die am wahrscheinlichsten neue Daten enthalten, wird zuerst versucht, sie einzufügen. Wenn der Datensatz bereits vorhanden ist, wird NULL zurückgegeben (und die Einfügung in die Staging-Tabelle wird angehalten). Bei Tabellen, die sich nur selten ändern, wird die Tabelle abgefragt und überprüft, ob der Datensatz gefunden wurde. Wenn dies der Fall ist, muss ich herausfinden, ob eines der Felder geändert wurde. (weil erinnern, ich muss zeigen, dass der Datensatz wurde durch den Import x aus der Datei y geändert) Ich kann natürlich nur Kessel den Code ausplattieren und jede Spalte testen. Aber, war auf der Suche nach etwas, das ein wenig "eloquenter" und haltbarer als das ist.

In gewisser Weise kombiniere ich ein importierendes System mit einem Audit-Trail-System. Bei der Untersuchung von Prüfpfaden habe ich den folgenden Artikel überprüft: wiki.postgresql.org. Es scheint, als ob der H-Store eine nette Möglichkeit wäre, Änderungen zu erhalten (und einige Spalten in der Tabelle leicht ignorieren zu können - zB "last_modified")

Ich bin zu 90% sicher, dass es alles geben wird Arbeit ... Ich habe ein paar Testtische erstellt und damit herumgespielt.

Meine Frage?

Ist eine bessere, wartungsfreundlichere Art, diese Aufgabe zu erledigen, die vielleicht 3 Datensätze aus 10K zu finden, die eine Änderung der Datenbank erfordern. Ich könnte sicherlich ein Python-Skript (oder etwas anderes) schreiben, das die Datei liest und versucht, herauszufinden, was mit jedem Datensatz zu tun ist, aber das fühlt sich schrecklich ineffizient an und wird zu vielen Rundreisen führen.

Ein paar letzten Dinge:

  1. Ich habe keine Kontrolle über die Eingabedateien. Ich würde es lieben, wenn sie mir nur die Deltas schicken würden, aber sie tun es nicht und es liegt völlig außerhalb meiner Kontrolle oder meines Einflusses.
  2. er System wird wachsen und neue Datenquellen werden wahrscheinlich hinzugefügt werden, die die Menge der zu verarbeitenden Daten stark erhöhen wird (so versuche ich, die Dinge effizient zu halten)
  3. Ich weiß, das ist nicht schön, einfach SO Frage (wie "wie eine Liste in Python zu sortieren"), aber ich glaube, eine der großen Dinge über SO ist, dass Sie harte Fragen stellen können und die Leute ihre Gedanken darüber teilen, wie sie denken, der beste Weg, um es zu lösen ist.
+0

Zwei (letzte) Fragen: 1) haben Sie löscht, oder ist der Eingang "inkrementell"? 2) Können die Anbieter der Daten * stabile Schlüssel * (keine Schlüsselaktualisierungen) garantieren? – wildplasser

+0

Es ist verschieden mit der Datenquelle und dem Datentyp. Es ist definitiv eine Situation, in der ich beim Umgang mit den Daten wahrscheinlich "defensiv" sein und auf alles vorbereitet sein sollte.Das heißt, ich denke, ich hätte löschen können (aber selten) und ich glaube, dass die Schlüssel * stabil sein sollten (mit anderen Worten, die ID für den POS-Datensatz sollte zwischen Uploads gleich bleiben). –

Antwort

7

Ich habe viele ähnliche Operationen. Was ich tue, ist COPY zu temporäre Importiertabellen:

CREATE TEMP TABLE target_tmp AS 
SELECT * FROM target_tbl LIMIT 0; -- only copy structure, no data 

COPY target_tmp FROM '/path/to/target.csv'; 

Für Leistung, laufen ANALYZE - Temp. Tabellen werden nicht durch Autovacuum analysiert!

ANALYZE target_tmp; 

Auch für die Leistung, vielleicht sogar einen Index oder zwei auf der temporären Tabelle erstellen oder einen Primärschlüssel hinzufügen, wenn die Daten für das erlaubt.

ALTER TABLE ADD CONSTRAINT target_tmp_pkey PRIMARY KEY(target_id); 

Sie brauchen das Leistungskram für kleine Importe nicht.

Verwenden Sie dann den vollen Umfang der SQL-Befehle, um die neuen Daten zu verdauen.
Zum Beispiel, wenn der Primärschlüssel der Zieltabelle ist target_id ..

Vielleicht DELETE was ist nicht mehr da?

DELETE FROM target_tbl t 
WHERE NOT EXISTS (
    SELECT 1 FROM target_tmp t1 
    WHERE t1.target_id = t.target_id 
); 

Dann UPDATE was ist schon da:

UPDATE target_tbl t 
SET col1 = t1.col1 
FROM target_tmp t1 
WHERE t.target_id = t1.target_id 

leer Änderungen zu vermeiden, fügen Sie einfach:

... 
AND col1 IS DISTINCT FROM t1.col1; -- repeat for relevant columns 

Oder, wenn die gesamte Zeile ist relevant:

... 
AND t IS DISTINCT FROM t1;   -- check the whole row 

Dann INSERT, was neu ist:

INSERT INTO target_tbl(target_id, col1) 
SELECT t1.target_id, t1.col1 
FROM target_tmp t1 
LEFT JOIN target_tbl t USING (target_id) 
WHERE t.target_id IS NULL; 

Abgleich, wenn die Sitzung geht weiter (temporäre Tabellen werden am Ende der Sitzung automatisch gelöscht):

DROP TABLE target_tmp; 

Oder ON COMMIT DROP oder ähnliches verwenden, um mit CREATE TEMP TABLE.
Code nicht getestet, sollte aber in jeder modernen Version von PostgreSQL mit Ausnahme von Tippfehlern funktionieren.

+0

Danke für die Antwort. Gutes Beispiel/Detail; Ich bin sicher, dass dies einigen Menschen helfen wird. Und es ist sehr nah an dem, was ich tun muss. Die einzige Anforderung, die mich dazu bringt, ist, dass die Tracking-Verfolgung der "Batch-ID" des Imports tatsächlich den Datensatz änderte. Mit anderen Worten, ich möchte nicht immer den Datensatz überschreiben/auffrischen, wenn er existiert - nur aktualisieren, wenn es Änderungen gibt. Ich nehme an, ich könnte. Und dann haben Sie einfach einen Aktualisierungstrigger auf der Tabelle und finden dort heraus, ob sich der Datensatz wirklich geändert hat ... Wenn ja, dann fügen Sie einen Audit-Trail-Datensatz hinzu. Scheint vernünftig? Einen besseren Weg? –

+0

@DavidS: Dies ist möglicherweise einfacher als Sie erwarten. Ich habe ein bisschen zum UPDATE-Abschnitt hinzugefügt. Es ist fast immer eine gute Idee, leere Updates trotzdem auszuschließen. Wenn Sie einen Prüfpfad benötigen, sollten Sie veraltete Versionen (plus Zeitstempel) in eine Protokolltabelle kopieren, bevor Sie "DELETE"/"UPDATE" ausführen. –

+0

gute Vorschläge und danke für die Aktualisierung der Antwort. Ziehen Sie es vor, zu Archivierungszwecken in die Archivtabelle (eine vollständige Kopie der Daten und des Zeitstempels) zu kopieren, anstatt nur einen hstore (x. *) Zu verwenden und in einem Textfeld in einer Tabelle audit_trail/history type zu speichern? Der Hauptvorteil der Archivtabelle besteht darin, dass sie leicht abgefragt werden kann. Der Hauptvorteil des Ansatzes von hstore scheint Flexibilität zu sein, wenn sich Ihr Schema ändert. Ich weiß, es ist ein bisschen vom Thema und könnte wahrscheinlich eine Frage der eigenen sein, aber neugierig auf Ihre Gedanken. Vielen Dank! –