2016-06-20 5 views
1

Über R Ich verbinde mich mit einer entfernten Datenbank. Das Problem, das ich habe, ist meine Hardware ist nicht so groß und der Datensatz enthält mehrere zehn Millionen Zeilen mit etwa 10 Spalten pro Tabelle. Wenn ich den folgenden Code ausführen, bei dem df Schritt, erhalte ich ein „nicht genügend Arbeitsspeicher“ Fehler von R:Teilen einer großen Tabelle in 2 Datenrahmen über eine JDBC-Verbindung in RStudio

library(DatabaseConnector) 
conn <- connect(connectionDetails) 
df <- querySql(conn,"SELECT * FROM Table1") 

Was dachte ich tut, um die Tabellen in zwei Teile jeden Filter/analysiert Aufspalten/kombinieren benötigt vorwärts gehen. I denke,, weil ich die Conn JDBC-Verbindung verwende Ich muss SQL-Syntax verwenden, damit es funktioniert. Mit SQL, beginne ich mit dem folgenden Code:

df <- querySql(conn,"SELECT TOP 5000000 FROM Table1") 

Und dann, wo ich nicht weiterkommen, wie erstelle ich einen zweiten Datenrahmen mit n beginnen - 5000000 Zeilen und in der letzten Zeile endet, aus Tabelle 1 abgerufen.

Ich bin offen für Vorschläge, aber ich denke, es gibt zwei mögliche Antworten auf diese Frage. Die erste besteht darin, innerhalb von querySql zu arbeiten, damit es funktioniert. Die zweite besteht darin, eine andere R-Funktion als querySql zu verwenden (keine Ahnung, wie dies aussehen würde). Ich bin aufgrund der Arbeitsumgebung auf R beschränkt.

+1

Ich denke, innerhalb der 10 Spalten gibt es mindestens eine, die Sie zum Filtern verwenden können. Sie können es sogar so wählen, dass Ihre Analysen einfacher durchzuführen sind (Geschlecht, Alter ..., geografische Region ... Jahr/Monat ...) –

+0

Welche Datenbank und welchen Treiber verwenden Sie? Wie viel RAM hat Ihr Computer? Wie viel Platz belegt Ihre Tabelle in der Datenbank (Megabyte)? Das Aufteilen der Daten in Stücke ist natürlich eine gute Lösung, aber wenn man das zugrundeliegende Problem kennt, kann man das Ergebnis besser optimieren (bis du mit der Antwort von bgoldst zufrieden bist.) Hast du zB versucht, 'RODBC' zu verwenden? Sie sind auf Windows) oder 'RJDBC' direkt (was von' DatabaseConnector' aufgerufen wird)? –

Antwort

1

Die SQL-Anweisung

SELECT TOP 5000000 * from Table1 

tut nicht das, was Sie denken, es tut.

Relationale Tabellen sind conceptually unordered.

Eine Relation ist definiert als eine Menge von n-Tupeln. Sowohl in der Mathematik als auch im relationalen Datenbankmodell ist eine Menge eine ungeordnete Sammlung eindeutiger, nicht duplizierter Elemente, obwohl einige DBMS ihren Daten eine Reihenfolge auferlegen.

Die Auswahl aus einer Tabelle ergibt eine Ergebnismenge. Ergebnissätze sind auch konzeptuell ungeordnet , außer und bis Sie explizit eine Reihenfolge für sie angeben, die in der Regel mit einer order by Klausel erfolgt.

Wenn Sie eine verwenden top (oder limit, auf dem DBMS abhängig) Klausel die Anzahl der Datensätze zu reduzieren, die von einer Abfrage zurückgegeben werden (nennen wir diese die „zurückgegebenen Datensätze“) unter die Anzahl der Datensätze, dass könnte von dieser Abfrage zurückgegeben werden (nennen wir diese die "ausgewählten Datensätze") und wenn Sie keine order by Klausel angegeben haben, dann ist es konzeptionell unvorhersehbar und zufällig, welche der ausgewählten Datensätze als die zurückgegebenen Datensätze gewählt werden.

Da Sie in Ihrer Abfrage keine Klausel order by angegeben haben, erhalten Sie effektiv 5.000.000 unvorhersehbare und zufällige Datensätze aus Ihrer Tabelle. Jedes Mal, wenn Sie die Abfrage ausführen, erhalten Sie möglicherweise einen anderen Satz von 5.000.000 Datensätzen (zumindest konzeptionell).

Daher macht es keinen Sinn zu fragen, wie man eine zweite Ergebnismenge erhält, "beginnend mit n - 5000000 und endend in der letzten Zeile". Es gibt keine n, und es gibt keine letzte Zeile. Die Auswahl der zurückgegebenen Datensätze war nicht deterministisch, und das DBMS erinnert sich nicht an solche Auswahlen von früheren Abfragen.Der einzige denkbare Weg, wie solche Informationen in eine nachfolgende Abfrage integriert werden könnten, wäre, sie explizit in die SQL einzuschließen, beispielsweise indem eine not in Bedingung für eine ID-Spalte verwendet wird und ID-Werte aus der ersten Abfrage als Literale eingebettet werden Negative Join, wieder unter Einbeziehung der ID-Werte als Literale. Aber das ist natürlich unvernünftig.

Hier gibt es zwei mögliche Lösungen.

1:order by mit limit und offset

Werfen Sie einen Blick die PostgreSQL documentation on limit and offset an. Zuerst nur den Punkt über Mangel an Ordnung zu verstärken, beachten Sie bitte die folgenden Abschnitte:

Wenn LIMIT verwenden, ist es wichtig, eine ORDER BY Klausel zu verwenden, die die Ergebniszeilen in eine einzigartige Reihenfolge beschränkt. Andernfalls erhalten Sie eine unvorhersehbare Teilmenge der Zeilen der Abfrage. Sie fragen sich vielleicht nach der zehnten bis zwanzigsten Reihe, aber nach der zehnten bis zwanzigsten in welcher Reihenfolge? Die Bestellung ist unbekannt, außer Sie haben ORDER BY angegeben.

Der Abfrageoptimierer nimmt LIMIT berücksichtigt werden, wenn Abfragepläne zu erzeugen, so dass Sie sehr wahrscheinlich verschiedene Pläne (wodurch man verschiedene Reihenaufträge) zu erhalten, je nachdem, was Sie geben für LIMIT und OFFSET. Wenn Sie also verschiedene Werte LIMIT/OFFSET verwenden, um verschiedene Teilmengen eines Abfrageergebnisses auszuwählen, erhalten Sie inkonsistente Ergebnisse, es sei denn, Sie erzwingen eine vorhersagbare Ergebnisreihenfolge mit ORDER BY. Dies ist kein Fehler. Es ist eine inhärente Konsequenz der Tatsache, dass SQL nicht verspricht, die Ergebnisse einer Abfrage in einer bestimmten Reihenfolge zu liefern, es sei denn, ORDER BY wird verwendet, um die Bestellung einzuschränken.

Nun erfordert diese Lösung, dass Sie eine order by Klausel, dass vollständig Aufträge die Ergebnismenge angeben. Eine order by Klausel, die nur teilweise Bestellungen das Ergebnis-Set wird nicht genug sein, da es immer noch Platz für einige Unvorhersagbarkeit und Zufälligkeit lässt. Wenn Sie die order by-Klausel haben, können Sie die Abfrage mit demselben limit-Wert wiederholen und die Werte offset erhöhen.

Etwas wie folgt aus:

select * from table1 order by id1, id2, ... limit 5000000 offset 0; 
select * from table1 order by id1, id2, ... limit 5000000 offset 5000000; 
select * from table1 order by id1, id2, ... limit 5000000 offset 10000000; 
... 

2: ein Nummerierungsspalte synthetisieren und Filter auf sie

Es ist möglich, eine Spalte in die select-Klausel hinzuzufügen, die eine vollständige Bestellung für das zur Verfügung stellen wird Ergebnismenge. Indem Sie diese SQL in eine Unterabfrage einfügen, können Sie anschließend nach der neuen Spalte filtern und dadurch Ihre eigene Seitennummerierung der Daten erreichen. Tatsächlich ist diese Lösung potenziell etwas leistungsfähiger, da Sie theoretisch diskontinuierliche Teilmengen von Datensätzen auswählen könnten, obwohl ich noch nie jemanden gesehen habe, der das tatsächlich gemacht hat.

Um die Sortierreihenfolge zu berechnen, können Sie die Partitionsfunktion row_number() verwenden.

Wichtig: Sie müssen noch ID-Spalten angeben, nach denen die Partition sortiert werden soll.Dies ist unter jeder denkbaren Lösung unvermeidlich; es muss immer eine deterministische, vorhersehbare Aufzeichnungsreihenfolge geben, um staatenloses Paging durch Daten zu führen.

Etwas wie folgt aus:

select * from (select *, row_number() over (id1, id2, ...) rn from table1) t1 where rn>0 and rn<=5000000; 
select * from (select *, row_number() over (id1, id2, ...) rn from table1) t1 where rn>5000000 and rn<=10000000; 
select * from (select *, row_number() over (id1, id2, ...) rn from table1) t1 where rn>10000000 and rn<=15000000; 
... 

Offensichtlich ist diese Lösung komplizierter und ausführlicher als die vorherige. Und die vorherige Lösung könnte Leistungsoptimierungen ermöglichen, die unter dem eher manuellen Ansatz der Partitionierung und Filterung nicht möglich sind. Daher würde ich die vorherige Lösung empfehlen.

Meine obige Diskussion konzentriert sich auf PostgreSQL, aber andere DBMS sollten entsprechende Funktionen bieten. Ein Beispiel für SQL Server finden Sie unter Equivalent of LIMIT and OFFSET for SQL Server?, in dem ein Beispiel für die synthetische Nummerierungslösung dargestellt wird. Außerdem können Sie (mindestens ab SQL Server 2012) OFFSET {offset} ROWS und FETCH NEXT {limit} ROWS ONLY verwenden, um Limit/Offset-Funktionalität zu erzielen.

Verwandte Themen