tsvsheet logo tsvsheet

DuckDB

DuckDB queries tab-separated files in place — no schema, no import step. A rendered sheet is a table the moment the pipe connects, with column types sniffed for free.

Everything here runs against grades.tsvt from tsvsheet.examples:

curl -fsSO https://raw.githubusercontent.com/tsvsheet/tsvsheet.examples/main/grades.tsvt

One pipe, no import

read_csv consumes the rendered grid straight from stdin — and notice the types: Average was a formula cell a moment ago, now it’s a sniffed double:

$ tsv render grades.tsvt | duckdb -c \
    "SELECT Result, count(*) AS n, round(avg(Average),1) AS avg FROM read_csv('/dev/stdin', delim='\t', header=true) GROUP BY Result;"
┌─────────┬───────┬────────┐
│ Result  │   n   │  avg   │
│ varchar │ int64 │ double │
├─────────┼───────┼────────┤
│ Fail    │     1 │   61.7 │
│ Pass    │     3 │   83.0 │
└─────────┴───────┴────────┘

Query rendered files directly

Render sheets to .tsv files and DuckDB treats each file as a table — delimiter, header, and types all sniffed from the extension and content. Joins included:

$ tsv render grades.tsvt > grades.tsv
$ printf 'Student\tClub\nAlice\tChess\nBob\tRobotics\nCarol\tChess\nDave\tDebate\n' > clubs.tsvt
$ tsv render clubs.tsvt > clubs.tsv
$ duckdb -c "SELECT c.Club, round(avg(g.Average),1) AS club_avg FROM 'grades.tsv' g JOIN 'clubs.tsv' c USING (Student) GROUP BY c.Club ORDER BY club_avg DESC;"
┌──────────┬──────────┐
│   Club   │ club_avg │
│ varchar  │  double  │
├──────────┼──────────┤
│ Chess    │     87.8 │
│ Robotics │     73.3 │
│ Debate   │     61.7 │
└──────────┴──────────┘

And back to a sheet

COPY ... TO emits TSV, so a query result flows back into the format it came from — append a =formula row and it computes:

$ { duckdb -c "COPY (SELECT Student, Average FROM 'grades.tsv' ORDER BY Average DESC) TO '/dev/stdout' (FORMAT csv, DELIMITER '\t', HEADER);"
    printf 'Class average\t=round(avg(B2:B5), 1)\n'; } | tsv render -
Student	Average
Carol	91.3
Alice	84.3
Bob	73.3
Dave	61.7
Class average	77.6

Sheet → SQL → sheet, and every intermediate stage is diffable text.