Operación SQL en un archivo csv usando bash o shell

Operación SQL en un archivo csv usando bash o shell

Este es mi archivo de entrada

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

quiero mi resultado como

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

grupo usando col1 y para encontrar min(col2) y max(col3)
a través del script de shell.

Respuesta1

Usandocsvkit,

$ 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

Esto cargaría los datos CSV en una base de datos temporal (creo que SQLite por defecto) y luego le aplicaría la consulta SQL proporcionada. De forma predeterminada, la tabla tendrá el mismo nombre que el archivo de entrada (sin sufijo) y, dado que los datos carecen de encabezados de columna, los nombres de los campos predeterminados serán alfabéticos.

Las -Hopciones indican csvsqlque los datos no tienen encabezados de columna.

Para eliminar el encabezado generado en la salida, canalice el resultado a través de algo como sed '1d'.

Para obtener números enteros llenos de ceros:

$ 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"

Aquí, las líneas se citan ya que en realidad solo solicitamos un único campo de salida para cada registro de resultado (y contiene comas). Otra forma de hacerlo, que implica escribir un poco más, pero no genera comillas dobles adicionales:

$ 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

Nuevamente, el encabezado de salida se puede eliminar canalizando el resultado a través de sed '1d'.

Respuesta2

Usandocsvkit:

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

Tenga en cuenta que esto truncará el 0 inicial.

Producción:

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

Respuesta3

Con Molinero (http://johnkerl.org/miller/doc), usando

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'

tienes

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

Respuesta4

Puede dividir su SQL en operaciones de procedimiento básicas y replicarlas en un script de shell.

Por supuesto, esto no es una gran idea, ya que una de las ventajas de los lenguajes declarativos (como SQL) es que ocultan la verbosidad y la complejidad de la implementación de procedimientos a los desarrolladores, permitiéndoles concentrarse en los datos. (La optimización es una segunda gran ventaja de los lenguajes declarativos que se pierde si los replicas con un programa procedimental).
Además, este enfoque es problemático porqueprocesar texto en bucles de shell generalmente se considera una mala práctica.

Sin embargo, aquí hay un ejemplo de script de shell que aprovecha utilidades estándar que encontrará preinstaladas en muchos sistemas (excepto la construcción de matriz, no especificada en POSIX, pero ampliamente disponible y seguramente disponible para usted ya que está preguntando sobre 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

Debe ser invocado como

./script file

Este guión es equivalente a

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

información relacionada