sq data wrangler
sq
is a command line tool that provides jq-style access to
structured data sources: SQL databases, or document formats like CSV or Excel.
sq
executes jq-like queries, or database-native SQL.
It can perform cross-source joins.
sq
outputs to a multitude of formats
including JSON,
Excel, CSV,
HTML, Markdown
and XML, and can insert query
results directly to a SQL database.
sq
can also inspect sources to view metadata about the source structure (tables,
columns, size) and has commands for common database operations to
copy, truncate,
and drop tables.
Find out more at sq.io.
Install
macOS
brew install neilotoole/sq/sq
Linux
/bin/sh -c "$(curl -fsSL https://sq.io/install.sh)"
Windows
scoop bucket add sq https://github.com/neilotoole/sq
scoop install sq
Go
go install github.com/neilotoole/sq
See other install options.
Overview
Use sq help
to see command help. Docs are over at sq.io.
Read the overview, and
tutorial. The cookbook has
recipes for common tasks, and the query guide covers sq
's query language.
The major concept is: sq
operates on data sources, which are treated as SQL databases (even if the
source is really a CSV or XLSX file etc.).
In a nutshell, you sq add
a source (giving it a handle
), and then execute commands against the
source.
Sources
Initially there are no sources.
$ sq ls
Let's add a source. First we'll add a SQLite
database, but this could also be Postgres,
SQL Server, Excel, etc.
Download the sample DB, and sq add
the source.
$ wget https://sq.io/testdata/sakila.db
$ sq add ./sakila.db
@sakila sqlite3 sakila.db
$ sq ls -v
HANDLE ACTIVE DRIVER LOCATION OPTIONS
@sakila active sqlite3 sqlite3:///Users/demo/sakila.db
$ sq ping @sakila
@sakila 1ms pong
$ sq src
@sakila sqlite3 sakila.db
The sq ping
command simply pings the source
to verify that it's available.
sq src
lists the active source, which in our
case is @sakila
.
You can change the active source using sq src @other_src
.
When there's an active source specified, you can usually omit the handle from sq
commands.
Thus you could instead do:
$ sq ping
@sakila 1ms pong
Query
Fundamentally, sq
is for querying data. The jq-style syntax is covered in
detail in the query guide.
$ sq '.actor | .actor_id < 100 | .[0:3]'
actor_id first_name last_name last_update
1 PENELOPE GUINESS 2020-02-15T06:59:28Z
2 NICK WAHLBERG 2020-02-15T06:59:28Z
3 ED CHASE 2020-02-15T06:59:28Z
The above query selected some rows from the actor
table. You could also
use native SQL, e.g.:
$ sq sql 'SELECT * FROM actor WHERE actor_id < 100 LIMIT 3'
actor_id first_name last_name last_update
1 PENELOPE GUINESS 2020-02-15T06:59:28Z
2 NICK WAHLBERG 2020-02-15T06:59:28Z
3 ED CHASE 2020-02-15T06:59:28Z
But we're flying a bit blind here: how did we know about the actor
table?
Inspect
sq inspect
is your friend (output abbreviated):
$ sq inspect
HANDLE DRIVER NAME FQ NAME SIZE TABLES LOCATION
@sakila sqlite3 sakila.db sakila.db/main 5.6MB 21 sqlite3:///Users/demo/sakila.db
TABLE ROWS COL NAMES
actor 200 actor_id, first_name, last_name, last_update
address 603 address_id, address, address2, district, city_id, postal_code, phone, last_update
category 16 category_id, name, last_update
Use sq inspect -v
to see more detail.
Or use -j
to get JSON output:
Combine sq inspect
with jq for some useful capabilities.
Here's how to list
all the table names in the active source:
$ sq inspect -j | jq -r '.tables[] | .name'
actor
address
category
city
country
customer
[...]
And here's how you could export each table to a CSV file:
$ sq inspect -j | jq -r '.tables[] | .name' | xargs -I % sq .% --csv --output %.csv
$ ls
actor.csv city.csv customer_list.csv film_category.csv inventory.csv rental.csv staff.csv
address.csv country.csv film.csv film_list.csv language.csv sales_by_film_category.csv staff_list.csv
category.csv customer.csv film_actor.csv film_text.csv payment.csv sales_by_store.csv store.csv
Note that you can also inspect an individual table:
$ sq inspect @sakila.actor
TABLE ROWS TYPE SIZE NUM COLS COL NAMES
actor 200 table - 4 actor_id, first_name, last_name, last_update
Diff
Use sq diff
to compare source metadata, or row data.
Insert query results
sq
query results can be output in various formats
(JSON, XML, CSV, etc), and can also be "outputted" as an
insert into database sources.
That is, you can use sq
to insert results from a Postgres query into a MySQL table,
or copy an Excel worksheet into a SQLite table, or a push a CSV file into
a SQL Server table etc.
Note: If you want to copy a table inside the same (database) source, use
sq tbl copy
instead, which uses the database's native table copy functionality.
For this example, we'll insert an Excel worksheet into our @sakila
SQLite database. First, we
download the XLSX file, and sq add
it as a source.
$ wget https://sq.io/testdata/xl_demo.xlsx
$ sq add ./xl_demo.xlsx --ingest.header=true
@xl_demo xlsx xl_demo.xlsx
$ sq @xl_demo.person
uid username email address_id
1 neilotoole [email protected] 1
2 ksoze [email protected] 2
3 kubla [email protected] NULL
[...]
Now, execute the same query, but this time sq
inserts the results into a new
table (person
)
in the SQLite @sakila
source:
$ sq @xl_demo.person --insert @sakila.person
Inserted 7 rows into @sakila.person
$ sq inspect @sakila.person
TABLE ROWS COL NAMES
person 7 uid, username, email, address_id
$ sq @sakila.person
uid username email address_id
1 neilotoole [email protected] 1
2 ksoze [email protected] 2
3 kubla [email protected] NULL
[...]
Cross-source join
sq
has rudimentary support for cross-source joins. That is, you can join an Excel worksheet with a
CSV file, or Postgres table, etc.
See the tutorial for further details, but
given an Excel source @xl_demo
and a CSV source @csv_demo
, you can do:
$ sq '@csv_demo.data, @xl_demo.address | join(.D == .address_id) | .C, .city'
C city
[email protected] Washington
[email protected] Ulan Bator
[email protected] Washington
[email protected] Ulan Bator
[email protected] Washington
Table commands
sq
provides several handy commands for working with tables:
tbl copy
, tbl truncate
and tbl drop
.
Note that these commands work directly
against SQL database sources, using their native SQL commands.
$ sq tbl copy .actor .actor_copy
Copied table: @sakila.actor --> @sakila.actor_copy (200 rows copied)
$ sq tbl truncate .actor_copy
Truncated 200 rows from @sakila.actor_copy
$ sq tbl drop .actor_copy
Dropped table @sakila.actor_copy
UNIX pipes
For file-based sources (such as CSV or XLSX), you can sq add
the source file,
but you can also pipe it:
$ cat ./example.xlsx | sq .Sheet1
Similarly, you can inspect:
$ cat ./example.xlsx | sq inspect
Drivers
sq
knows how to deal with a data source type via a driver
implementation. To view the installed/supported drivers:
$ sq driver ls
DRIVER DESCRIPTION
sqlite3 SQLite
postgres PostgreSQL
sqlserver Microsoft SQL Server / Azure SQL Edge
mysql MySQL
csv Comma-Separated Values
tsv Tab-Separated Values
json JSON
jsona JSON Array: LF-delimited JSON arrays
jsonl JSON Lines: LF-delimited JSON objects
xlsx Microsoft Excel XLSX
Output formats
sq
has many output formats:
--text
: Text--json
: JSON--jsona
: JSON Array--jsonl
: JSON Lines--csv
/--tsv
: CSV / TSV--xlsx
: XLSX (Microsoft Excel)--html
: HTML--xml
: XML--yaml
: YAML--markdown
: Markdown--raw
: Raw (bytes)
CHANGELOG
See CHANGELOG.md.
Acknowledgements
- Thanks to Diego Souza for creating the Arch Linux package.
- Much inspiration is owed to jq.
- See
go.mod
for a list of third-party packages. - Additionally,
sq
incorporates modified versions of:olekukonko/tablewriter
segmentio/encoding
for JSON encoding.
- The Sakila example databases were lifted from jOOQ, which in turn owe their heritage to earlier work on Sakila.
- Date rendering via
ncruces/go-strftime
.