Linux 上文本文件的 SQL 查询引擎?

问题描述 投票:0回答:6

我们一直在命令行使用grep、cut、sort、uniq、join来进行数据分析。 尽管存在缺点,但它们工作得很好。 例如,您必须为每个工具指定列号。 我们经常有宽文件(许多列)和给出列名称的列标题。 事实上,我们的文件看起来很像 SQL 表。 我确信有一个驱动程序(ODBC?)可以对分隔文本文件进行操作,并且有一些查询引擎将使用该驱动程序,因此我们可以在文本文件上使用 SQL 查询。 由于进行分析通常是临时的,因此必须进行最少的设置来查询新文件(只需使用我在此目录中指定的文件),而不是在某些配置中声明特定的表。

实际上来说,什么是最简单的? 也就是说,最容易设置和用于应用于文本文件的 SQL 引擎和驱动程序?

sql command-line text
6个回答
7
投票

q - 直接在 CSV 或 TSV 文件上运行 SQL:

https://github.com/harelba/q


6
投票

David Malcolm 编写了一个名为“squeal”(以前称为“show”)的小工具,它允许您使用类似 SQL 的命令行语法来解析各种格式的文本文件,包括 CSV。

squeal 主页上的示例:

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

4
投票

借鉴别人的建议,这里是 sqlite3 的 Python 脚本。 有点冗长,但它有效。

我不喜欢必须完全复制文件才能删除标题行,但我不知道如何说服 sqlite3 的 .import 跳过它。 我可以创建 INSERT 语句,但这看起来同样糟糕,甚至更糟。

调用示例:

$ sql.py --file foo --sql "从数据中选择 count(*)"

代码:

#!/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())

3
投票

也许编写一个脚本来创建 SQLite 实例(可能在内存中),从文件/stdin 导入数据(接受数据格式),运行查询,然后退出?

根据数据量,性能可以接受。


2
投票

MySQL 有一个 CSV 存储引擎,如果您的文件是 CSV 文件,它可能会满足您的需要。

否则,您可以使用 mysqlimport 将文本文件导入 MySQL。您可以围绕 mysqlimport 创建一个包装器,它计算出列等并创建必要的表。

您也许还可以使用 DBD::AnyData,这是一个 Perl 模块,可让您像数据库一样访问文本文件。

也就是说,听起来你真的应该考虑使用数据库。在文本文件中保存面向表的数据真的更容易吗?


0
投票

我已经使用 Microsoft LogParser 多次查询 csv 文件......它达到了目的。很惊讶地看到 M$ 提供了如此有用的工具,而且还是免费的!

© www.soinside.com 2019 - 2024. All rights reserved.