SQL-Operation auf CSV-Datei mit Bash oder Shell

SQL-Operation auf CSV-Datei mit Bash oder Shell

Dies ist meine Eingabedatei

0164318,001449,001452,001922  
0164318,001456,001457,001922  
0842179,002115,002118,001485  
0846354,001512,001513,001590  
0841422,001221,001224,001860  
0841422,001227,001228,001860

Ich möchte mein Ergebnis als

0164318,001449,001457,001922  
0842179,002115,002118,001485  
0846354,001512,001513,001590  
0841422,001221,001228,001860 

Gruppieren Sie mithilfe von Spalte1 und finden Sie Min. (Spalte2) und Max. (Spalte3)
über ein Shell-Skript.

Antwort1

Verwenden voncsvkit,

$ csvsql -H --query 'SELECT a,min(b),max(c),d FROM file GROUP BY a' file.csv
a,min(b),max(c),d
164318,1449,1457,1922
841422,1221,1228,1860
842179,2115,2118,1485
846354,1512,1513,1590

Dadurch werden die CSV-Daten in eine temporäre Datenbank geladen (standardmäßig SQLite, glaube ich) und dann die angegebene SQL-Abfrage darauf angewendet. Die Tabelle hat standardmäßig denselben Namen wie die Eingabedatei (ohne Suffix) und da die Daten keine Spaltenüberschriften haben, sind die Standardfeldnamen alphabetisch.

Die -HOptionen geben an csvsql, dass die Daten keine Spaltenüberschriften haben.

Um den generierten Header in der Ausgabe zu löschen, leiten Sie das Ergebnis durch etwas wie weiter sed '1d'.

So erhalten Sie mit Nullen aufgefüllte Ganzzahlen:

$ csvsql -H --query 'SELECT printf("%07d,%06d,%06d,%06d",a,min(b),max(c),d) FROM file GROUP BY a' file.csv
"printf(""%07d,%06d,%06d,%06d"",a,min(b),max(c),d)"
"0164318,001449,001457,001922"
"0841422,001221,001228,001860"
"0842179,002115,002118,001485"
"0846354,001512,001513,001590"

Hier werden die Zeilen in Anführungszeichen gesetzt, da wir eigentlich nur ein einziges Ausgabefeld für jeden Ergebnisdatensatz anfordern (und dieses enthält Kommas). Eine andere Möglichkeit, dies zu tun, erfordert zwar etwas mehr Tipparbeit, erzeugt aber keine zusätzlichen Anführungszeichen:

$ csvsql -H --query 'SELECT printf("%07d",a),printf("%06d",min(b)),printf("%06d",max(c)),printf("%06d",d) FROM file GROUP BY a' file.csv
"printf(""%07d"",a)","printf(""%06d"",min(b))","printf(""%06d"",max(c))","printf(""%06d"",d)"
0164318,001449,001457,001922
0841422,001221,001228,001860
0842179,002115,002118,001485
0846354,001512,001513,001590

Auch hier kann der Ausgabeheader entfernt werden, indem das Ergebnis weitergereicht wird sed '1d'.

Antwort2

Verwenden voncsvkit:

csvsql -H --query "select a,min(b),max(c),d from file group by a,d" file.csv

Beachten Sie, dass dadurch die führende 0 abgeschnitten wird.

Ausgabe:

a,min(b),max(c),d
164318,1449,1457,1922
841422,1221,1228,1860
842179,2115,2118,1485
846354,1512,1513,1590

Antwort3

Mit Miller (http://johnkerl.org/miller/doc), mit

mlr --ocsv --quote-all --inidx --ifs , cat inputFile | \
mlr --ocsv --quote-none  --icsvlite stats1 -g '"1"' -a min,max,min -f '"2","3","4"' \
then cut -f '"1","2"_min,"3"_max,"4"_min' \
then label id,col2,col3,col4 | sed 's/"//g'

du hast

id,col2,col3,col4
0164318,001449,001457,001922
0842179,002115,002118,001485
0846354,001512,001513,001590
0841422,001221,001228,001860

Antwort4

Sie können Ihr SQL in grundlegende prozedurale Operationen aufteilen und diese in einem Shell-Skript replizieren.

Dies ist natürlich keine gute Idee, da einer der Vorteile deklarativer Sprachen (wie SQL) darin besteht, dass sie die Ausführlichkeit und Komplexität der prozeduralen Implementierung vor den Entwicklern verbergen, sodass diese sich auf die Daten konzentrieren können. (Optimierung ist ein zweiter großer Vorteil deklarativer Sprachen, der verloren geht, wenn Sie sie mit einem prozeduralen Programm replizieren.)
Außerdem ist dieser Ansatz problematisch, weilDie Verarbeitung von Text in Shell-Schleifen wird normalerweise als schlechte Praxis angesehen.

Hier ist jedoch ein Beispiel für ein Shell-Skript, das Standarddienstprogramme nutzt, die auf vielen Systemen vorinstalliert sind (mit Ausnahme der Array-Konstruktion – nicht in POSIX spezifiziert, aber weit verbreitet und sicherlich auch für Sie verfügbar, da Sie danach fragen bash):

#!/bin/bash

# The input file will be passed as the first argument
file="$1"

# For each input line:
# We take only the values of the first field, sort them, remove duplicates
for i in $(cut -d ',' -f 1 "$file" | sort -n -u); do

    # Resetting the array is not really needed; we do it for safety
    out=()

    # The first field of the output row is the key of the loop
    out[0]="$i"

    # We only consider the rows whose first field is equal
    # to the current key (grep) and...

    # ... we sort the values of the second field
    # in ascending order and take only the first one
    out[1]="$(grep "^${out[0]}" "$file" | cut -d ',' -f 2 | sort -n | head -n 1)"

    # ... we sort the values of the third field in
    # ascending order and take only the last one
    out[2]="$(grep "^${out[0]}" "$file" | cut -d ',' -f 3 | sort -n | tail -n 1)"

    # ... we sort the values of the fourth field in
    # ascending order and take only the first one
    out[3]="$(grep "^${out[0]}" "$file" | cut -d ',' -f 4 | sort -n | head -n 1)"

    # Finally we print out the output, separating fields with ','
    printf '%s,%s,%s,%s\n' "${out[@]}"

done

Es soll aufgerufen werden als

./script file

Dieses Skript entspricht

SELECT col1, MIN(col2), MAX(col3), MIN(col4)
FROM text
GROUP BY col1
ORDER BY col1

verwandte Informationen