[PYTHON] Schreiben Sie ein Skript, um einen MySQL-Dump in TSV zu konvertieren

Versuche, das von MySQL ausgegebene SQL für andere Zwecke in tabulatorgetrennten Text (sogenannte TSV) zu konvertieren.

Überblick

MySQL-Tabellen können mit mysqldump als SQL ausgegeben werden. Die Hauptteile sind die Anweisungen CREATE und INSERT.

(Weggelassen) CREATE TABLE t_price ( (Weggelassen) INSERT INTO t_price VALUES (1,'feste Form',25,82),(Weggelassen)(10,'feste Form外',4000,1180); (Weggelassen)

Sie können direkt an TSV sichern, wenn es sich um einen von Ihnen verwalteten Speicherauszug von MySQL handelt.

Wenn die einzigen an anderer Stelle veröffentlichten Daten ein SQL-Dump sind, können Sie sie entweder abrufen und sichern oder konvertieren. Dieses Mal werde ich ein Skript schreiben und die Konvertierung versuchen.

Spezifikation

Extrahiert nur die VALUES der INSERT-Anweisung aus SQL und konvertiert sie in TSV. Insbesondere streben wir das folgende Verhalten an.

Vor der Konvertierung


INSERT INTO `table` VALUES (1,2,3),(4,5,6);
INSERT INTO `table` VALUES (1,'a,b','c\'d');

Nach der Konvertierung


1	2	3
4	5	6
1	a,b	c'd

Python

Schreiben Sie ein Konvertierungsskript in Python.

read_string

Analysiert die vom einfachen Anführungszeichen eingeschlossene Zeichenfolge. Dies ist das geschäftigste.

Überprüft, ob das erste Zeichen "" ist. Die Escape-Sequenzverarbeitung wird vereinfacht, indem "" für "", "", "\" entfernt wird, aber "" für andere Steuerzeichen übrig bleibt.

test ist ein Testflag.

sql2tsv.py


test = True

def read_string(src, pos):
    length = len(src)
    if pos >= length or src[pos] != "'": return None
    pos += 1
    ret = ""
    while pos < length and src[pos] != "'":
        if src[pos] == "\\":
            pos += 1
            end = pos >= length
            if end or not src[pos] in "\"'\\":
                ret += "\\"
            if end: break
        ret += src[pos]
        pos += 1
    if pos < length and src[pos] == "'": pos += 1
    return (ret, pos)

if test:
    src = r"'a,b','c\'d'"
    pos = 0
    while pos < len(src):
        s, p = read_string(src, pos)
        print("read_string", (src, pos), "->", (s, p))
        pos = p + 1

Ausführungsergebnis


read_string ("'a,b','c\\'d'", 0) -> ('a,b', 5)
read_string ("'a,b','c\\'d'", 6) -> ("c'd", 12)

read_value

Lesen Sie ein Datenelement. Wenn es sich um eine Zeichenfolge handelt, lesen Sie "read_string", andernfalls "," oder "(".

sql2tsv.py (Fortsetzung)


def read_value(src, pos):
    length = len(src)
    if pos >= length: return None
    sp = read_string(src, pos)
    if sp: return sp
    p = pos
    while p < length and src[p] != "," and src[p] != ")":
        p += 1
    return (src[pos:p], p)

if test:
    for src in ["1,2,3", r"1,'a,b','c\'d'"]:
        pos = 0
        while (value := read_value(src, pos)):
            s, p = value
            print("read_value", (src, pos), "->", (s, p))
            pos = p + 1

Ausführungsergebnis


read_value ('1,2,3', 0) -> ('1', 1)
read_value ('1,2,3', 2) -> ('2', 3)
read_value ('1,2,3', 4) -> ('3', 5)
read_value ("1,'a,b','c\\'d'", 0) -> ('1', 1)
read_value ("1,'a,b','c\\'d'", 2) -> ('a,b', 7)
read_value ("1,'a,b','c\\'d'", 8) -> ("c'd", 14)

read_values

Liest alle durch Kommas getrennten Daten in Klammern.

sql2tsv.py (Fortsetzung)


def read_values(src, pos):
    length = len(src)
    if pos >= length or src[pos] != "(": return None
    pos += 1
    ret = []
    if pos < length and src[pos] != ")":
        while (value := read_value(src, pos)):
            s, pos = value
            ret.append(s)
            if pos >= length or src[pos] != ",": break
            pos += 1
    if pos < length and src[pos] == ")": pos += 1
    return (ret, pos)

if test:
    for src in [r"(1,2,3)", r"(1,'a,b','c\'d')"]:
        print("read_values", (src, 0), "->", read_values(src, 0))

Ausführungsergebnis


read_values ('(1,2,3)', 0) -> (['1', '2', '3'], 7)
read_values ("(1,'a,b','c\\'d')", 0) -> (['1', 'a,b', "c'd"], 16)

read_all_values

Liest alle in Klammern eingeschlossenen Daten. Machen Sie es zu einem Generator, der die Handhabung in einer Schleife übernimmt.

sql2tsv.py (Fortsetzung)


def read_all_values(src, pos):
    length = len(src)
    while (sp := read_values(src, pos)):
        s, pos = sp
        yield s
        if pos >= length or src[pos] != ",": break
        pos += 1

if test:
    src = r"(1,2,3),(1,'a,b','c\'d')"
    print("read_all_values", (src, 0), "->", list(read_all_values(src, 0)))

Ausführungsergebnis


read_all_values ("(1,2,3),(1,'a,b','c\\'d')", 0) -> [['1', '2', '3'], ['1', 'a,b', "c'd"]]

read_sql

Suchen Sie die Zeile, die mit "INSERT INTO" beginnt, aus jeder Zeile in SQL und verarbeiten Sie sie mit "read_all_values".

sql2tsv.py (Fortsetzung)


def read_sql(stream):
    while (line := stream.readline()):
        if line.startswith("INSERT INTO "):
            p = line.find("VALUES (")
            if p >= 0: yield from read_all_values(line, p + 7)

if test:
    import io
    src = r"""
INSERT INTO `table` VALUES (1,2,3),(4,5,6);
INSERT INTO `table` VALUES (1,'a,b','c\'d');
""".strip()
    print("read_sql", (src,))
    print("->", list(read_sql(io.StringIO(src))))

Ausführungsergebnis


read_sql ("INSERT INTO `table` VALUES (1,2,3),(4,5,6);\nINSERT INTO `table` VALUES (1,'a,b','c\\'d');",)
-> [['1', '2', '3'], ['4', '5', '6'], ['1', 'a,b', "c'd"]]

Befehl

Ich habe alle notwendigen Funktionen implementiert. Schalten Sie den Test aus.

sql2tsv.py (ändern)


test = False

Liest die angegebene Datei und schreibt sie in die angegebene Datei.

Geben Sie für "Öffnen" "error =" replace "an, vorausgesetzt, die UTF-8-Byte-Zeichenfolge wird in der Mitte eines Zeichens abgeschnitten. Dies ersetzt den anomalen Charakter durch. Wenn Sie keine "Fehler" angeben, tritt ein Fehler auf.

[Referenz] (Windows) Ursachen und Problemumgehungen für UnicodeEncodeError in Python 3 --Qiita

sql2tsv.py (Fortsetzung)


if __name__ == "__main__":
    import sys
    try:
        sql, tsv = sys.argv[-2], sys.argv[-1]
        if not (sql.endswith(".sql") and tsv.endswith(".tsv")):
            raise Exception
    except:
        print("usage: %s sql tsv" % sys.argv[0])
        exit(1)
    with open(sql, "r", encoding="utf-8", errors="replace") as fr:
        with open(tsv, "w", encoding="utf-8") as fw:
            for values in read_sql(fr):
                fw.write("\t".join(values))
                fw.write("\n")

Verwenden Sie es wie folgt.

python sql2tsv.py input.sql output.tsv

Ich habe das ganze Skript unten gestellt.

Messung

Es misst, wie schnell es mit einer riesigen Datei verarbeitet werden kann.

Wiktionary japanische Version

Wie bei Wikipedia sind Dump-Daten für die Öffentlichkeit zugänglich.

Die folgenden Dateien werden ab der Ausgabe vom 1. Mai 2020 verwendet, die zum Zeitpunkt des Schreibens des Artikels verfügbar ist.

Das Extrahieren der Datei beträgt ca. 90 MB.

gunzip -k jawiktionary-20200501-categorylinks.sql.gz

Beim Konvertieren des erweiterten SQL mit diesem Skript wird eine TSV von ca. 79 MB ausgegeben.

$ time python sql2tsv.py jawiktionary-20200501-categorylinks.sql jawiktionary-20200501-categorylinks.tsv

real    0m19.921s
user    0m19.375s
sys     0m0.516s

Die Anzahl der Zeilen beträgt ca. 950.000.

$ wc -l jawiktionary-20200501-categorylinks.tsv
951077 jawiktionary-20200501-categorylinks.tsv

Wiktionary englische Version

Dann probieren Sie die englische Version.

Verwenden Sie die folgenden Dateien. Im aufgeklappten Zustand schwillt es auf ca. 3 GB an.

Konvertieren Sie dies. Die Ausgabedatei ist ungefähr 2,6 GB groß.

$ time python sql2tsv.py enwiktionary-20200501-categorylinks.sql enwiktionary-20200501-categorylinks.tsv

real    15m58.965s
user    15m39.063s
sys     0m12.578s

Die Anzahl der Leitungen beträgt 28 Millionen.

$ wc -l enwiktionary-20200501-categorylinks.tsv
28021874 enwiktionary-20200501-categorylinks.tsv

F#

Ich habe es als Test auf F # portiert.

Die Verarbeitung derselben Datei war viel schneller.

Datei Zeit
jawiktionary-20200501-categorylinks.sql 0m03.168s
enwiktionary-20200501-categorylinks.sql 1m52.396s

Komprimierte Datei lesen

Das .NET Framework verfügt über einen GZipStream, mit dem Sie eine komprimierte gz-Datei direkt lesen können.

sql2tsv.fsx (ändern)


open System.IO.Compression
let args = Environment.GetCommandLineArgs()
let sqlgz, tsv =
    if args.Length < 2 then ("", "") else
    let len = args.Length
    (args.[len - 2], args.[len - 1])
if not (sqlgz.EndsWith ".sql.gz") || Path.GetExtension tsv <> ".tsv" then
    printfn "usage: sql2tsv sql.gz tsv"
    exit 1
do
    use fs = new FileStream(sqlgz, FileMode.Open)
    use gs = new GZipStream(fs, CompressionMode.Decompress)
    use sr = new StreamReader(gs)
    use sw = new StreamWriter(tsv)
    sw.NewLine <- "\n"
    for values in readSql sr do
        sw.WriteLine(String.concat "\t" values)

Die Messergebnisse sind nachstehend zusammengefasst.

Zusammenfassung

Ordnen Sie die Messergebnisse.

Sprache Datei Zeit
Python jawiktionary-20200501-categorylinks.sql 0m19.921s
F# jawiktionary-20200501-categorylinks.sql 0m03.168s
F# jawiktionary-20200501-categorylinks.sql.gz 0m03.509s
Python enwiktionary-20200501-categorylinks.sql 15m58.965s
F# enwiktionary-20200501-categorylinks.sql 1m52.396s
F# enwiktionary-20200501-categorylinks.sql.gz 2m15.380s

Die direkte Verarbeitung komprimierter Dateien ist etwas langsamer, spart jedoch Speicherplatz, was von Vorteil ist.

Recommended Posts

Schreiben Sie ein Skript, um einen MySQL-Dump in TSV zu konvertieren
Erstellen eines Shell-Skripts zum Schreiben eines Tagebuchs
Schreiben wir ein entsprechendes Ping-Bestätigungsskript
Ich werde nie vergessen, wie man ein Shell-Skript schreibt, nicht vergessen! !!
Skript an mysqldump an alle MySQL-DBs
Schreiben Sie die Standardausgabe in eine Datei
Konvertieren Sie eine Zeichenfolge in ein Bild
Tool zum Konvertieren der Juniper-Konfiguration
Schreiben Sie ein Batch-Skript mit Python3.5 ~
Schreiben Sie ein Skript, um die Entfernung mit dem Elasticsearch 5-System schmerzfrei zu berechnen
Qiita (1) Wie schreibe ich einen Codenamen?
Führen Sie ein Skript von Jupyter aus, um es zu verarbeiten
[Python] So konvertieren Sie eine zweidimensionale Liste in eine eindimensionale Liste
So konvertieren Sie csv in tsv in CLI
[Ubuntu] So führen Sie ein Shell-Skript aus
Ein einfaches IDAPython-Skript zum Benennen einer Funktion
[An die Herren auf Twitter] Ich habe ein Skript geschrieben, um .jpg-large sofort in .jpg zu konvertieren.
Speichern Sie SQLite3-Daten und migrieren Sie zu MySQL
Ich habe ein Skript in Python erstellt, um MDD-Dateien in das Scrapbox-Format zu konvertieren
Ich habe ein Skript erstellt, um Piktogramme anzuzeigen
Skript zum Erstellen einer Mac-Wörterbuchdatei
So führen Sie Maya Python-Skripte aus
Konvertieren der cURL-API in ein Python-Skript (mithilfe des IBM Cloud-Objektspeichers)
Python-Skript zum Konvertieren von Breiten- und Längengrad in Mesh-Code
Konvertieren Sie eine mehrdimensionale Liste (Array) in eine Dimension
Warum muss Python einen Doppelpunkt schreiben?
So schreiben Sie einen ShellScript Bash für Anweisung
Ich habe ein Skript geschrieben, um ein WordPress-Plug-In hochzuladen
Wie schreibe ich ein benanntes Tupeldokument im Jahr 2020?
[Go] So schreiben oder rufen Sie eine Funktion auf
Ich möchte mit Python in eine Datei schreiben
So schreiben Sie eine ShellScript-Bash-Case-Anweisung
Stellen Sie eine Verbindung zu MySQL her
So konvertieren / wiederherstellen Sie einen String mit [] in Python
So schreiben Sie eine GUI mit dem Befehl maya
Konvertieren Sie das Slice-Objekt in eine Liste mit Indexnummern
Konvertieren Sie eine Textdatei mit hexadezimalen Werten in eine Binärdatei
Ich möchte in Python schreiben! (2) Schreiben wir einen Test
So konvertieren Sie ein Klassenobjekt mit SQLAlchemy in ein Wörterbuch
Ich habe ein Skript erstellt, um ein Snippet in README.md einzufügen
So schreiben Sie einen Listen- / Wörterbuchtyp von Python3
Ein Memorandum zum Ausführen eines Python-Skripts in einer Bat-Datei
Ich habe einen Code erstellt, um illustration2vec in ein Keras-Modell zu konvertieren
So konvertieren Sie ein Mel-Spektrogramm zurück in eine WAV-Datei
[Python] Ein Memo zum vertikalen Schreiben von CSV mit Pandas
[Python] Erstellt eine Methode zum Konvertieren von Radix in 1 Sekunde
So erstellen Sie ein einfaches TCP-Server / Client-Skript
Schreiben Sie Code in UnitTest, eine Python-Webanwendung
Python-Skript, das eine JSON-Datei aus einer CSV-Datei erstellt
Geben Sie einen Binärspeicherauszug in Binärdatei und zurück in eine Binärdatei aus
Generieren Sie ein Bash-Skript, um die Einstellungen des Datadog-Monitors hinzuzufügen
Ich habe ein Skript in Python erstellt, um eine Textdatei für JSON zu konvertieren (für das vscode-Benutzer-Snippet).