2009-04-22 9 views
7

Usamos grep, cut, sort, uniq, y nos unimos a la línea de comandos todo el tiempo para hacer análisis de datos. Funcionan muy bien, aunque hay deficiencias. Por ejemplo, debe dar los números de columna a cada herramienta. A menudo tenemos archivos anchos (muchas columnas) y un encabezado de columna que da los nombres de las columnas. De hecho, nuestros archivos se parecen mucho a las tablas SQL. Estoy seguro de que hay un controlador (ODBC?) Que operará en archivos de texto delimitados, y un motor de consulta que usará ese controlador, por lo que podríamos usar consultas SQL en nuestros archivos de texto. Como el análisis suele ser ad hoc, debería ser una configuración mínima para consultar nuevos archivos (basta con usar los archivos que especifico en este directorio) en lugar de declarar tablas particulares en alguna configuración.Motor de consulta SQL para archivos de texto en Linux?

Hablando en términos prácticos, ¿qué es lo más fácil? Es decir, el motor y el controlador de SQL que es más fácil de configurar y usar para aplicar contra archivos de texto?

Respuesta

3

Ripeando la sugerencia de alguien más, aquí hay una secuencia de comandos de Python para sqlite3. Un poco detallado, pero funciona.

No me gusta tener que copiar completamente el archivo para soltar la línea del encabezado, pero no sé cómo convencer al .import de sqlite3 para que se salte. Podría crear instrucciones INSERT, pero eso parece igual de malo si no peor. invocación

muestra:

 
$ sql.py --file foo --sql "select count(*) from data" 

El código:

 
#!/usr/bin/env python 

"""Run a SQL statement on a text file""" 

import os 
import sys 
import getopt 
import tempfile 
import re 

class Usage(Exception): 
    def __init__(self, msg): 
     self.msg = msg 

def runCmd(cmd): 
    if os.system(cmd): 
     print "Error running " + cmd 
     sys.exit(1) 
     # TODO(dan): Return actual exit code 

def usage(): 
    print >>sys.stderr, "Usage: sql.py --file file --sql sql" 

def main(argv=None): 
    if argv is None: 
     argv = sys.argv 

    try: 
     try: 
      opts, args = getopt.getopt(argv[1:], "h", 
             ["help", "file=", "sql="]) 
     except getopt.error, msg: 
      raise Usage(msg) 
    except Usage, err: 
     print >>sys.stderr, err.msg 
     print >>sys.stderr, "for help use --help" 
     return 2 

    filename = None 
    sql = None 
    for o, a in opts: 
     if o in ("-h", "--help"): 
      usage() 
      return 0 
     elif o in ("--file"): 
      filename = a 
     elif o in ("--sql"): 
      sql = a 
     else: 
      print "Found unexpected option " + o 

    if not filename: 
     print >>sys.stderr, "Must give --file" 
     sys.exit(1) 
    if not sql: 
     print >>sys.stderr, "Must give --sql" 
     sys.exit(1) 

    # Get the first line of the file to make a CREATE statement 
    # 
    # Copy the rest of the lines into a new file (datafile) so that 
    # sqlite3 can import data without header. If sqlite3 could skip 
    # the first line with .import, this copy would be unnecessary. 
    foo = open(filename) 
    datafile = tempfile.NamedTemporaryFile() 
    first = True 
    for line in foo.readlines(): 
     if first: 
      headers = line.rstrip().split() 
      first = False 
     else: 
      print >>datafile, line, 
    datafile.flush() 
    #print datafile.name 
    #runCmd("cat %s" % datafile.name) 
    # Create columns with NUMERIC affinity so that if they are numbers, 
    # SQL queries will treat them as such. 
    create_statement = "CREATE TABLE data (" + ",".join(
     map(lambda x: "`%s` NUMERIC" % x, headers)) + ");" 

    cmdfile = tempfile.NamedTemporaryFile() 
    #print cmdfile.name 
    print >>cmdfile,create_statement 
    print >>cmdfile,".separator ' '" 
    print >>cmdfile,".import '" + datafile.name + "' data" 
    print >>cmdfile, sql + ";" 
    cmdfile.flush() 
    #runCmd("cat %s" % cmdfile.name) 
    runCmd("cat %s | sqlite3" % cmdfile.name) 

if __name__ == "__main__": 
    sys.exit(main()) 
+0

Cool, gracias dfrankow! – mcassano

2

MySQL tiene un CVS storage engine, que podría hacer lo que necesite, si sus archivos son archivos CSV.

De lo contrario, puede usar mysqlimport para importar archivos de texto a MySQL. Puede crear un contenedor alrededor de mysqlimport, que resuelva columnas, etc. y cree la tabla necesaria.

También es posible que pueda usar DBD::AnyData, un módulo Perl que le permite acceder a archivos de texto como una base de datos.

Dicho esto, suena muy parecido a lo que realmente debería ver al usar una base de datos. ¿Es realmente más fácil mantener los datos orientados a tablas en los archivos de texto?

+0

Sí, realmente es más fácil mantener los datos orientadas a tablas en archivos de texto.Es la naturaleza ad-hoc, combinada con todas las herramientas de estadísticas de línea de comandos que tenemos: tome esta información, únase a eso, recorte algunas columnas, páselo a través de ANOVA. Configurar tablas todo el tiempo, importar y exportar para acceder a nuestras herramientas estadísticas sería doloroso. – dfrankow

+0

De acuerdo, el problema aquí es que uso datos de muchas fuentes diferentes y el medio común suele ser un archivo separado en espacios en blanco. Mi trabajo sería más fácil si pudiera, para mayor claridad, dar 'cortar' un nombre de campo en lugar de un número de columna. Cinco minutos después, he terminado con ese archivo que hace que importar a mysql se vuelva engorroso. – mcassano

3

Quizás escriba una secuencia de comandos que cree una instancia de SQLite (posiblemente en la memoria), importe sus datos de un archivo/stdin (aceptando el formato de sus datos), ejecute una consulta y luego salga.

Según la cantidad de datos, el rendimiento podría ser aceptable.

+0

Feo, pero lo primero que se me vino a la mente. Ciertamente lo suficientemente bueno para prototipos y trabajos pequeños. – dmckee

5

David Malcolm escribió una pequeña herramienta llamada "squeal" (anteriormente "show"), lo que le permite utilizar de comandos similar a SQL sintaxis de línea para analizar archivos de texto de varios formatos, incluido CSV.

Un ejemplo de la página principal del chirrido:

$ squeal "count(*)", source from /var/log/messages* group by source order by "count(*)" desc 
count(*)|source    | 
--------+--------------------+ 
1633 |kernel    | 
1324 |NetworkManager  | 
98  |ntpd    | 
70  |avahi-daemon  | 
63  |dhclient   | 
48  |setroubleshoot  | 
39  |dnsmasq    | 
29  |nm-system-settings | 
27  |bluetoothd   | 
14  |/usr/sbin/gpm  | 
13  |acpid    | 
10  |init    | 
9  |pcscd    | 
9  |pulseaudio   | 
6  |gnome-keyring-ask | 
6  |gnome-keyring-daemon| 
6  |gnome-session  | 
6  |rsyslogd   | 
5  |rpc.statd   | 
4  |vpnc    | 
3  |gdm-session-worker | 
2  |auditd    | 
2  |console-kit-daemon | 
2  |libvirtd   | 
2  |rpcbind    | 
1  |nm-dispatcher.action| 
1  |restorecond   | 
0

he utilizado Microsoft LogParser para consultar archivos CSV varias veces ... y que sirve al propósito. Fue sorprendente ver una herramienta tan útil de M $ que también ¡Gratis!

+0

Probablemente un comentario. Evite usar el bloque de respuestas para comentar. – Luv

+0

La pregunta era para Linux, una herramienta MS probablemente no funcione sin Wine (y tal vez con una GUI). – David

Cuestiones relacionadas