Execute SQL against structured text like CSV or TSV
github.com
github.com
SQLite has virtual tables where app provided code can provide the underlying data and you can then use SQL queries against it. This is by far the easiest approach for foreign data formats. http://www.sqlite.org/vtab.html
I'm the author of a Python wrapper for SQLite. It provides a shell compatible with the main SQLite one invocable from the command line (ie you don't need to go anywhere near Python) - http://rogerbinns.github.io/apsw/shell.html
That shell has an autoimport command which automatically figures things out for csv like data (separators, data types etc)
sqlite> .help autoimport
.autoimport FILENAME ?TABLE? Imports filename creating a table and automatically working out separators and data types (alternative to .import command)
The import command requires that you precisely pre-setup the table and schema, and set the data separators (eg commas or tabs). In many cases this information can be automatically deduced from the file contents which is what this command does. There must be at least two columns and two rows.
If the table is not specified then the basename of the file will be used.
Additionally the type of the contents of each column is also deduced - for example if it is a number or date. Empty values are turned into nulls. Dates are normalized into YYYY-MM-DD format and DateTime are normalized into ISO8601 format to allow easy sorting and searching. 4 digit years must be used to detect dates. US (swapped day and month) versus rest of the world is also detected providing there is at least one value that resolves the ambiguity.
Care is taken to ensure that columns looking like numbers are only treated as numbers if they do not have unnecessary leading zeroes or plus signs. This is to avoid treating phone numbers and similar number like strings as integers.
This command can take quite some time on large files as they are effectively imported twice. The first time is to determine the format and the types for each column while the second pass actually imports the data.
I did a similar thing at work, but with virtual tables. Supports zip/gz files and multiple files with glob expansion. Might open source if there's interest. It's based on a sample CSV vtab source. Has type automatic detection. No indexing yet, though.
lnav tries to push things a little further and extract data from unstructured log messages. For example, given the following message:
"Registering new address record for 10.1.10.62 on eth0."
It will create a virtual table with two columns that capture the IP address (10.1.10.62) and the device name (eth0). Queries against this table will return the values from all messages with the same format (Registering new address record for COL_0 on COL_1). See http://lnav.readthedocs.org/en/latest/data.html for more info.http://msdn.microsoft.com/en-us/library/ms974559.aspx
(article dated 2004)
I'm so glad C# is a thing now.
http://technet.microsoft.com/en-us/scriptcenter/dd919274.asp...
http://www.postgresql.org/docs/current/interactive/file-fdw....
Example: #Find all unique from col1 logparser -i:csv -o:csv -stats:off -dtlines:2000 -headers:off "select distinct col1 from input.csv" >out.csv
sqlite> create table mydata (id integer, value1 integer, description text);
sqlite> .separator ","
sqlite> .import mydatafile.csv mydata
I assume textql is basically just running a similar SQL query, but perhaps autodetecting column types for you? (Haven't looked at the code.) Is that the value proposition, or does it do something more? sqlite> select "3"+"3";
6
though note: sqlite> select "24" < 300;
0
sqlite> select "9" < "10";
0
(see http://www.sqlite.org/datatype3.html for more about SQLite typing.)+ is the SQL numeric addition operator. || is string concatenation.
sqlite> select 3 || 3 ;
33
sqlite> select 'a'+'b' ;
0textql imports your data into SQLite. In other words its a import utility.
Key differences: - sqlite import demands an on disk db, textql will use an in memory db if it can, increases performance - sqlite import will not accept stdin, breaking unix pipes. textql will happily do so.
I will make a note of this in the project readme, seems to crop up a lot.
EDIT: here's gfycat version: http://gfycat.com/FlusteredFarApatosaur#
$ j -J sample_data.xlsb tbl | jq 'length'
3
$ j -J sample_data.xlsb tbl | jq '[.[]|.id]|max'
3
$ j -J sample_data.xlsb tbl | jq '[.[]|.id]|min'
1
$ j -J sample_data.xlsb tbl | jq '[.[]|.value]|add'
18
jq syntax is very terse compared to the equivalent SQLNearly every sysadmin/firefigher I know has command line perl/awk/sed magic text files full of this.
Having said that I would appreciate if something like this was available for XMLs. Having to deal with SQL designs force fully shoe horned into NoSQL databases so often these days. I frequently run into situations where I have to query data from XML dumps. Most of the time, I have to invent a Perl one liner that essentially does the job of a select query.
30-second XPath pitch:
//foo/bar
This gets the 'bar' children from every 'foo', regardless of where it occurs in the hierarchy. //baz[@name='Alice']
Gets everything like: <baz name="Alice">There's lots of more complicated things you can do, but these are the most frequent types of things I've had to do.
$x('//a')xml sel --template --value-of "//foo/bar" \ --template --value-of "//baz[@name='Alice']" << EOF <foo> <bar>hello</bar> <baz name="Alice"> world</baz> </foo> EOF
http://xmlstar.sourceforge.net/docs.php
It also can edit with XPath which is really handy.
XML/HTML support, via XPATH selectors, is coming. I would love to be able to pipe the output of curl through this!
https://www.sqlite.org/cvstrac/wiki?p=ImportingFiles
(They also seem ignorant of the existence of RFC4180 when they claim there is no CSV standard.)
At the end of the day the problem is that non technical users don't fill in things properly. They have a space before a number so it gets treated as text. Or in the same column Excel spits out an integer as a float. Basically the things that a real database keeps in order for you, so unfortunately I don't see it solving my problems. Shame.
At the moment I parse and update the parser every time a new error appears. Most of the time it seems to be excel presenting things to the user that appear correct when they are not.
I have even been tempted to write my own "data import grid". It would look like excel, have very little of the functionality, but could allow a developer to set rules on what can / can't be entered in each column.
That said, if you are dealing with excel, you can use Spreadsheet-ParseExcel and Spreadsheet-WriteExcel to have perl handle excel .xls files directly (though I'm not sure about xls files including embedded vba macros).
The authors get a loading throughput (including index creation!) of over 1.5 GB/S with multithreading. They don't seem to do any dirty tricks when loading the data as they immediately can run hundreds of thousands of queries per second on the data.
There also seem to be more interesting papers on the research project page http://www.hyper-db.de/index.html
[1] "Instant Loading for Main Memory Databases" http://www.vldb.org/pvldb/vol6/p1702-muehlbauer.pdf
textql -query "select count(*) from persons.csv"
...because csv files are basically tables. This would also allow you to do joins on multiple files:
textql -query "select p.name, c.name from persons.csv p inner join companies.csv c on p.companyid = c.id"
I like this idea! I will consider it, but a problem is that '.' is not allowed as a table name, without escaping brackets. May have to drop file-ext for it to work.
Would it theoretically be faster than the unix join/grep commands for small CSV files, the kind you get usually via email?
I love SQL and prefer it for data work of this type, but I'm not entirely sure of the benefit of this, since it looks like you're just reading it into SQL, which means having to format and normalize anyway?
[1]http://www.gregreda.com/2013/07/15/unix-commands-for-data-sc...
I made a simple ruby version of this a while back: https://github.com/dergachev/csv2sqlite
Subsequently also I discovered CSVKIT, a python implementation which is probably more robust:
[1]: https://pypi.python.org/pypi/csvkit [2]: https://gist.github.com/jdp/8447221
It has two modes: 1) as a commnad line
./comp -f commits.json,authors.txt '[ i.commits | i <- commits, a <- author, i.commits.author.name == a ]'
2) as a service to allow querying of the files through simple http interface
./comp -f commits.json,authors.tx -l :9090
curl -d '{"expr": "[ i.commits | i <- commits, a <- author, i.commits.author.name == a ]"}' http://localhost:9090/full
or through an interactive console on http://localhost:9090/console
disclosure: I'm a co-author, and happy to get a feedback or answer any questions :)
:)
Otherwise - already checking out, that's what I wanted for a while.
Also, without support for multiple tables & joins this is not very useful.
Not even slightly true. Can we stop crapping on v1 releases? I'd rather a developer release early and often than hold their code back until it has every possible feature.
- Multiple file/table support is coming. - I like the -tab setting. Will add. I also want to support -dlm=xFF format for arbitrary delimiters, which makes working with Hive text file formats easier.
Basically I recommend you to look at xmlstarlet sel (and find) as an example of converting complex languages into command line.
Did you think of loading every sqlite file on boot (-load?)
How about textql -header -source data.csv -uniq id -tstmp created,updated -index created 'SELECT ...'? - i.e. changes to table structure from command line switches?
Conventions also won't hurt. I.e. my-data.csv turns to my_data table that is saved to my-data.sqlite3 without me mentioning so. -save should do that.
The idea is to create a re-entrant workspace in the current directory which becomes richer with every invocation.
Also, does CREATE TABLE ... SELECT ... FROM tbl work? Plus -save.
Yes. You can even modify and append to existing sqlite datebases. I'd love for this to become, eventually, a general purpose tool for working on data from the CLI.
I will look into xmlstarlet sel and find for sure. I like the -load tag idea too. There's lots of possibilities here.