3 poin oleh GN⁺ 2023-09-16 | 1 komentar | Bagikan ke WhatsApp
  • eurofxref-hist.zip milik ECB hanyalah kumpulan CSV kurs tukar yang sederhana, tetapi dengan curl, gunzip, dan sqlite3 saja kita bisa langsung menemukan tanggal saat dolar paling kuat terhadap euro, yaitu 2000-10-26
  • Format asli berupa wide format dengan kolom mata uang mengikuti Date, sehingga kurang nyaman untuk analisis dan perlu dirapikan menjadi long format berbentuk Date,Currency,Rate
  • Karena ada trailing comma di akhir setiap baris, parser CSV membaca kolom kosong, sehingga di Pandas perlu menghapus kolom terakhir dengan .iloc[:,:-1] agar hasil melt bersih
  • CSV yang sudah dirapikan dapat diunggah ke csvbase lewat HTTP PUT lalu disambungkan dengan alat seperti gnuplot, DuckDB, dan sqlite3 untuk membuat grafik, menghitung moving average, dan memuat CSV lewat HTTP
  • Data publik yang bisa diambil tanpa negosiasi akses, autentikasi, kuota, atau dokumentasi API yang rumit dapat berfungsi seperti open API, dan file zip sederhana pun bisa menjadi dasar pertukaran data untuk aplikasi finansial

Mengueri kurs tukar hanya dengan satu file zip

  • ECB mempublikasikan data historis kurs euro terhadap mata uang lain dalam bentuk file zip resmi
  • Pipeline di bawah ini mengunduh data, mengekstrak isinya, membaca CSV ke SQLite in-memory DB, lalu mengurutkan berdasarkan nilai USD untuk mengambil tanggal pertama
curl -s https://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist.zip \
| gunzip \
| sqlite3 ':memory:' '.import /dev/stdin stdin' \
  "select Date from stdin order by USD asc limit 1;"
  • Output-nya adalah 2000-10-26
  • curl -s mengurangi noise di standard error, dan gunzip mengekstrak file zip
  • Di Mac OS atau BSD, gunzip keluarga BSD tidak mendukung file zip, jadi perlu memakai bsdtar -xOf - sebagai gantinya
  • sqlite3 ':memory:' memakai DB in-memory, dan .import /dev/stdin stdin memuat standard input ke tabel stdin

Merapikan bentuk CSV dan Pandas melt

  • Header CSV asli berbentuk wide format seperti Date,USD,JPY,BGN,CYP,CZK,DKK,..., dengan kolom tanggal diikuti kolom per mata uang
  • Untuk filter dan agregasi, long format berbentuk Date,Currency,Rate lebih mudah digunakan
  • Proses mengubah wide format menjadi long format biasa disebut melt
  • Kebanyakan database SQL tidak punya operasi yang setara dengan melt, sehingga Pandas berguna untuk perapian data
curl -s https://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist.zip | \
gunzip | \
python3 -c 'import sys, pandas as pd
pd.read_csv(sys.stdin).melt("Date").to_csv(sys.stdout, index=False)'
  • File ECB memiliki trailing comma di akhir setiap baris, sehingga parser CSV menambahkan kolom kosong di bagian akhir
  • Kolom kosong ini membuat baris tak berguna di akhir hasil melt, jadi perlu dihapus
curl -s https://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist.zip | \
gunzip | \
python3 -c 'import sys, pandas as pd
pd.read_csv(sys.stdin).iloc[:, :-1].melt("Date")\
.to_csv(sys.stdout, index=False)'
  • .iloc[:, :-1] memilih semua baris dan semua kolom kecuali kolom terakhir
  • Data valas ECB memang perlu sedikit perapian format, tetapi bisa langsung dipakai tanpa negosiasi akses, pembayaran, bicara dengan sales, mengirim email·nama perusahaan·jabatan, kuota, autentikasi, atau membaca dokumentasi API
  • Karena hanya perlu menangani masalah format dan bentuk dasar, ini termasuk rilis data publik yang relatif bagus

Mengunggah data yang sudah dirapikan ke csvbase

  • CSV yang sudah dirapikan bisa diunggah ke csvbase table agar tidak perlu mengulangi proses perapian
  • Cukup tambahkan satu curl lagi di akhir pipeline yang ada untuk mengunggah CSV lewat HTTP PUT
curl -s https://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist.zip | \
gunzip | \
python3 -c 'import sys, pandas as pd
pd.read_csv(sys.stdin).iloc[:, :-1].melt("Date")\
.to_csv(sys.stdout, index=False)' | \
curl -n --upload-file - \
'https://csvbase.com/calpaterson/eurofxref-hist?public=yes'
  • --upload-file - mengunggah data yang diterima dari standard input ke URL yang ditentukan
  • Jika tabel belum ada di csvbase, tabel baru akan dibuat; jika sudah ada, data dimasukkan ke tabel tersebut
  • -n memakai informasi autentikasi dari ~/.netrc

Menggambar grafik kurs dengan gnuplot

  • Tabel csvbase yang sudah dirapikan bisa diambil sebagai CSV dengan curl, lalu disambungkan ke grep, cut, dan gnuplot
curl -s https://csvbase.com/calpaterson/eurofxref-hist | \
grep USD | \
cut -d, -f 2,4 | \
gnuplot -e "set datafile separator ','; set term dumb; \
plot '-' using 1:2 with lines title 'usd'"
  • Perintah ini menggambar lebih dari 6.000 titik data sebagai ASCII art yang masih cukup bisa dibaca di terminal karakter 80x25
  • Konfigurasi gnuplot diatur agar menerima input CSV dan menggambar tanggal serta kurs sebagai grafik garis
    • set datafile separator ',': menyatakan bahwa input adalah CSV
    • set term dumb: menggambar sebagai ASCII art
    • plot -: menerima data dari standard input
    • using 1:2 with lines: menggambar garis dari kolom 1 dan 2, yaitu tanggal dan kurs
    • title 'usd': menetapkan nama garis menjadi usd
  • Output juga bisa dibuat sebagai gambar SVG, dan agar terlihat seperti data deret waktu, sumbu x perlu ditandai sebagai waktu serta format waktu dan rotasi tick sumbu x perlu diatur
  • Untuk pemakaian berulang, ini bisa dibungkus dalam fungsi Bash plot_timeseries_to_svg

Menghitung moving average dengan DuckDB

  • Untuk melihat garis tren kurs USD, kita bisa menghitung moving average dengan DuckDB
curl -s https://csvbase.com/calpaterson/eurofxref-hist | \
duckdb -csv -c "select Date, avg(value) over \
(order by date rows between 100 preceding and current row) \
as rolling from read_csv_auto('/dev/stdin')
where variable = 'USD';" | \
plot_timeseries_to_svg rolling
  • Jika tidak ada duckdb, tidak sulit mengubah kueri yang sama untuk sqlite3
  • DuckDB mirip SQLite, tetapi berorientasi kolom alih-alih berorientasi baris
  • DuckDB dapat membaca CSV langsung dari HTTP dan membuatnya menjadi file tabel
CREATE TABLE eurofxref_hist AS SELECT * FROM
read_csv_auto("https://csvbase.com/calpaterson/eurofxref-hist";);
  • DuckDB cukup bagus dalam inferensi tipe, dan secara default mendeteksi ukuran terminal lalu menyingkat hasil besar saat ditampilkan
  • Untuk kueri besar, DuckDB bisa menampilkan progress bar dan juga dapat mengeluarkan tabel Markdown

Cara data publik bekerja seperti open API

  • Hanya dengan CSV di dalam file zip dan alat yang mudah dipasang lewat brew install atau apt install, kita sudah bisa melakukan banyak hal
  • eurofxref-hist.zip adalah bentuk yang sangat sederhana dari protokol pertukaran data antarorganisasi
  • File zip ini terlihat kecil, tetapi banyak aplikasi finansial memakainya setiap hari
  • Alasan ECB membiarkan trailing comma apa adanya kemungkinan karena menghapusnya sekarang akan merusak banyak kode yang sudah ada
  • Jika data publik disediakan dengan sangat mudah, itu juga dapat berperan sebagai open API
  • Jika banyak API pada dasarnya lebih mirip pertukaran data daripada pemanggilan fungsi jarak jauh, maka secara fungsional tidak jauh berbeda dari data publik yang mudah diambil

URL sederhana dan kata kerja HTTP di csvbase

  • csvbase memberi satu URL untuk setiap tabel
https://csvbase.com/<username>/<table_name>;
  • Contohnya sebagai berikut
https://csvbase.com/calpaterson/eurofxref-hist
  • Setiap URL memiliki empat kata kerja HTTP utama
    • GET: mengambil CSV; di browser juga bisa mendapatkan halaman web
    • PUT: membuat tabel baru dengan CSV baru atau menimpa tabel yang ada
    • POST: menambahkan baris CSV dalam jumlah besar ke tabel yang sudah ada
    • DELETE: menghapus tabel tersebut
  • Autentikasi memakai HTTP Basic Auth

Catatan tentang perapian data dan pipeline

  • Di antara database SQL, fitur yang setara dengan melt tersedia misalnya pada UNPIVOT milik Snowflake dan PIVOT/UNPIVOT milik MS SQL Server
  • Salah satu alasan penting R dan Pandas banyak dipakai adalah kuatnya kemampuan perapian data
  • Pipeline Bash bekerja secara multiproses, sehingga tiap program berjalan paralel dalam proses yang terpisah
  • Pada Oktober 2000, kurs dolar terhadap euro adalah 0.8252, yang berarti 1 dolar bisa membeli 1,21 euro
  • Euro diluncurkan pada Januari 1999 tanpa uang kertas dan koin; pada awalnya hanya ada di dalam sistem perbankan, dan uang kertas serta koin menyusul kemudian

1 komentar

 
GN⁺ 2023-09-16
Komentar Hacker News
  • Saya ingat file ini saat bekerja di ECB sekitar 15 tahun lalu
    File ini sejauh ini adalah file yang paling banyak diunduh dari situs web ECB, dan banyak orang serta lembaga keuangan mengunduhnya setiap hari untuk memperbarui sistem mereka
    Selama beberapa menit tepat setelah waktu rilis harian yang sudah ditentukan, trafik melonjak tajam, dan keputusan untuk membuatnya menjadi file CSV sederhana setelah diekstrak memang disengaja
    Berkat itu, file bisa disajikan dengan stabil dan cepat, memakai sedikit sumber daya, dan tim kecil yang saat itu mengelola situs web publik ECB memang layak sangat bangga atas keputusan teknis untuk menyajikan data ini sebagai satu file statis

    • Cara seperti ini menopang banyak pertukaran data, tetapi orang yang belum pernah menangani sistem lama sering tidak mengetahuinya
      Tidak mencolok dan tidak memakai framework
      Sekitar 15 tahun lalu, saya menangani pertukaran data di sebuah perusahaan besar lama yang produknya mungkin pernah dibeli siapa saja, antara sistem pencatatan produk dan sub-sistem/sistem paralel yang tersisa dari merger dan akuisisi; kebanyakan berupa impor/ekspor massal file fixed-width atau file ber-delimiter melalui server SFTP
      Saat itu produknya sendiri sudah berumur 15 tahun, dan ada sekitar 20–30 sumber data atau ekspor semacam itu yang berjalan bolak-balik, tetapi semuanya berjalan sangat baik
      Kemungkinan besar masih dipakai sekarang tanpa banyak perubahan, dan saat itu frontend-nya sedang ditulis ulang dari versi lama berbasis Smalltalk
    • Saya juga tahu file ini, dan saya salah satu dari mereka
      Dari semua sumber data yang kami gunakan, ini yang paling mudah ditangani
    • Membayangkan gerbang yang harus dilewati di perusahaan saya untuk mengunggah data yang berubah sebagai CSV/ZIP statis saja sudah membuat heran
      Arsitek akan bilang ZIP bukan format yang sesuai spesifikasi untuk tujuan ini, compliance akan bilang perlu pemeriksaan kebocoran data pribadi, dan pihak risk akan bilang harus mencegah aktor jahat mengunduh file tersebut
      Tim web mungkin akan bilang bahwa menambahkan sesuatu ke situs memerlukan prosedur perubahan yang disetujui
    • Saya penasaran mengapa mereka memutuskan menyediakannya sebagai file ZIP, bukan sekadar mengunggah CSV dan menyerahkannya ke kompresi HTTP
    • Sedikit menakutkan membayangkan akibatnya jika file ini rusak dengan cara apa pun, misalnya judul kolom salah
  • Unduhan file sederhana dan file CSV itu bagus
    Saya berharap lebih banyak pihak memublikasikan data dalam format sederhana seperti ini, dan setiap kali harus mengisi “keranjang belanja” untuk mengunduh data pemerintah AS, rasanya sedikit mati di dalam
    Ada banyak tool wrapper yang memudahkan pipeline khusus ini, dan jika perlu tampilan web serta fitur yang sedikit lebih canggih, sesuatu seperti Datasette juga bagus

    • Keuntungan menyajikan CSV yang dibungkus ZIP lewat web adalah, tanpa menyimpannya di mana pun—kecuali database—data bisa langsung di-stream dan dimasukkan dengan sangat cepat
      Anda bisa membaca file ZIP sebagai stream, memproses CSV baris demi baris untuk melakukan transformasi, lalu memuatnya ke database dengan COPY FROM stdin pada Postgres
    • Menakjubkan bahwa SQLite bisa menerima CSV sebagai input dan langsung menjalankan kueri atasnya
      Kedengarannya begitu logis dan berguna, tetapi saya belum pernah menemukannya sampai sekarang
      Karena ada banyak laporan dalam bentuk CSV, saya ingin segera mencobanya untuk menjalankan kueri dengan cepat
    • CSV itu bagus jika bisa benar-benar dijamin ada “satu CSV”, dan tidak ada yang membukanya atau mengeditnya dengan program spreadsheet, atau membetulkannya secara manual di editor teks
      Misalnya, cara menangani tanda kutip bisa bercabang seperti "Look, this contains \"quotes\"!",012345 dan "Look, this contains ""quotes""!",012345, dan contoh yang lebih rusak seperti "Look, this contains "quotes"!",012345 atau Look, this contains "quotes"!,012345 juga bisa muncul
      Jejak spreadsheet juga bisa memotong nol di depan, seperti "Look, this contains ""quotes""!",12345
      Secara teori, JSON juga bisa diedit manual hingga menjadi file yang setengah rusak, tetapi dalam praktiknya saya hampir tidak pernah melihat orang melakukan itu pada file JSON, dan nilai seperti nomor seri pun dalam JSON cenderung tetap berupa string, bukan integer yang nol depannya dipotong oleh aplikasi yang “membantu”
    • Saya belum pernah benar-benar memikirkan betapa absurdnya metode keranjang belanja untuk data pemerintah
      Sebenarnya mengapa begitu, apakah ada alasan yang sah?
    • Format dokumennya sendiri tampaknya bukan masalah utama
      Bahkan jika CSV diganti menjadi dokumen JSON yang dibungkus ZIP, keuntungannya tetap sama
      Masalah sebenarnya adalah terlalu banyak hambatan untuk sekadar mengunduh satu file yang disajikan secara statis
  • Saya pernah membuat API untuk lembaga pemerintah, dan datanya hanya berubah setahun sekali atau direvisi sangat jarang
    Seluruh dataset bisa dibungkus menjadi satu file ZIP kurang dari 1MB, tetapi urusannya membesar ketika solution architect menetapkan requirement
    Karena data bisa saja berubah tepat pada saat request dilakukan, cache tidak boleh dipakai, sehingga API menjadi lambat, dan bahkan muncul sistem webhook yang terlalu rumit untuk memberi tahu pelanggan tentang perubahan data
    Satu file ZIP mungkin terlalu sederhana, tetapi sebenarnya tidak jauh berbeda dari yang benar-benar dibutuhkan

    • Jika data kurang dari 1MB berubah sekali atau dua kali setahun, menurut saya API yang tepat adalah server web statis yang mendukung ETag/If-Modified-Since dengan benar
      Jika ingin dibuat lebih keren, tambahkan webhook yang dipicu ketika file berubah, sehingga klien tahu kapan harus mengunduh ulang tanpa melakukan polling sekali sehari
      Atau cukup buat skrip yang mengirim email yang sudah ditentukan ke mailing list saat ada perubahan
    • Letakkan file ZIP di server web yang mendukung ETag, lalu polling setiap kali perlu akses
      Jika tidak ada yang berubah dari sebelumnya, Anda mendapat respons HTTP 304 kosong; jika berubah, Anda menerima lagi file ZIP kurang dari 1MB bersama ETag baru. Saya tidak tahu apa yang kurang dari ini
    • Jika data hanya berubah setahun sekali atau jarang, pengguna yang memakai data itu mungkin juga jarang menggunakan API, jadi kecepatan bisa jadi bukan masalah besar
      Cache menambah kompleksitas dan juga menciptakan risiko harus memvalidasi ulang cache secara manual, jadi mungkin saja solution architect itu benar
  • Jika harus mengunduh file 565 KB hanya untuk mendapatkan satu nilai hasil 2000-10-26, itu API yang mengerikan.
    Kalau tujuannya mengambil data dalam jumlah besar lalu menyajikannya kembali ke pengguna, CSV yang dibundel dalam ZIP itu bagus, dan jauh lebih saya sukai daripada protobuf untuk jadwal kereta real-time transportasi publik yang dukungan multibahasanya kurang baik.
    Namun kalau diperlakukan seperti API untuk mendapatkan satu nilai, itu pemborosan besar, dan saya harap tidak ada orang yang memasukkannya ke aplikasi dengan cara seperti ini.
    Tulisannya sendiri keren, tetapi judulnya terasa terlalu seperti klaim yang provokatif.

    • Ini adalah data historis.
      Sama sekali tidak ada alasan untuk memintanya lebih dari sekali sehari, dan orang yang menggunakan data seperti ini kemungkinan menginginkan filter atau agregasi yang sangat berbeda satu sama lain.
      Kalau dipakai untuk mendapatkan nilai tukar saat ini, memang desainnya buruk, tetapi ada layanan lain untuk tujuan itu, dan file ini cocok dengan kasus penggunaan yang umum.
    • Kalau Anda berharap tidak ada orang yang memasukkannya ke aplikasi seperti itu, ada kabar buruk.
      Tidak terkait langsung dengan API, tetapi dulu saat mendukung aplikasi pengelolaan lahan, sebelum versi baru keluar aplikasi itu berjalan baik bahkan di kantor satelit lambat yang koneksinya mungkin setara ISDN, tetapi versi barunya sama sekali tidak bisa.
      Vendor menyuruh menjalankannya di server RDP, tetapi saya merasa itu tidak masuk akal, jadi saya selidiki. Ternyata satu panggilan tertentu tanpa alasan melakukan SELECT * FROM sometable, sementara panggilan lain dalam eksekusi yang sama memakai klausa SQL select yang benar.
      Saat kami memberi tahu vendor soal ini, awalnya mereka sangat bingung bagaimana kami bisa mengetahuinya, dan akhirnya mereka merilis versi baru yang diperbaiki agar bisa dipakai juga di koneksi lambat.
      Sulit dipahami kenapa pengujian internal mereka tidak menangkap itu dan malah mendorong solusi mahal ke pelanggan.
    • Rasanya seperti argumen dari 20 tahun lalu menelepon lagi dan meminta argumennya dikembalikan.
      Kalau belakangan ini Anda pernah melihat JavaScript sedikit saja, 565 KB beserta logika untuk mencari nilai terbesar di dalamnya tergolong sangat kecil menurut standar wajar apa pun.
    • Sepertinya banyak orang di thread ini mengabaikan begitu saja perbedaan definisi “API”.
      Sebagian orang menganggap “cara mendapatkan data, meskipun menerima seluruh data tanpa pemfilteran” sebagai API, tetapi secara pribadi saya melihat unduhan seluruh tabel sebagai unduhan model data yang logikanya tidak berjalan pada model tersebut, sedangkan API adalah logika yang memfilter sebagian model dan mengembalikannya dengan cara yang saya minati.
    • Saya tidak tahu kenapa diasumsikan 565 KB itu penting.
      Saya cukup banyak membuat software finansial di sisi backend maupun frontend, dan di frontend sayangnya cukup umum mengirim “data” sebanyak itu bahkan sebelum sampai ke data sebenarnya.
      Di backend, ini hanya keputusan desain, dan tidak ada yang lebih cepat daripada cron job malam hari yang mem-parse nilai tukar, membuat todays-rates.json yang disesuaikan untuk tujuan tertentu, lalu menyajikannya sebagai file statis ke aplikasi mobile, web, dan microservice.
      Tidak ada satu pun yang berarti aplikasi mobile harus mengonsumsi ZIP-CSV-over-HTTP ini secara langsung.
  • Ada optimasi yang sangat sederhana untuk orang yang mengeluh harus mengambil file besar setiap kali membutuhkan satu data kecil.
    Jika file dijamin append-only dan menggunakan kompresi seperti HTTP gzip/brotli alih-alih file ZIP, maka dengan range request kita bisa hanya mengambil data baru sejak pembaruan terakhir.
    Tambahkan satu header checksum untuk ketenangan, dan itu menjadi API inkremental yang cukup efisien sekaligus sangat sederhana.
    Tentu saja Anda harus menyimpan state dan menanggung biaya unduhan pertama serta pemeliharaan state, dan ini tidak efisien jika Anda hanya butuh tepat satu nilai tukar EUR/JPY pada 2007-08-22 untuk sekali saja.

    • Betul. Saya sedang membayangkan library klien yang melakukan hal seperti itu dengan ETag dan teknik lain.
      Masih sangat dalam pengerjaan, tetapi kode “kualitas riset” saat ini ada di sini: https://pypi.org/project/csvbase-client/
    • Menambahkan soal range request: jika server mengizinkan range request untuk file ZIP, dan file ZIP-nya sangat besar tetapi Anda hanya perlu beberapa file di dalamnya, Anda bisa mengambil central directory dan data terkompresi dari file yang diperlukan saja tanpa mengambil seluruh ZIP.
      https://github.com/gtsystem/python-remotezip
    • Atau cukup sediakan beberapa file diff saja.
      Dengan satu patch harian saja, bandwidth yang saya perlukan untuk menjaga file tetap mutakhir bisa berkurang drastis.
      Ini kalau mengunduh beberapa ratus KB ekstra per hari memang berarti; kemungkinan besar biasanya tidak.
  • Ada typo pada contoh sqlite.
    Tidak terlihat di tangkapan layar, tetapi argumen -csv harus ditambahkan ke sqlite.

    • Aneh. Awalnya saya memasukkan argumen itu, lalu saya hapus karena sepertinya tidak diperlukan, dan di lingkungan saya itu berjalan.
      Saya akan menambahkannya kembali dan menginvalidasi cache. Nanti setelah menidurkan anak-anak saya akan memeriksa apa yang salah.
      Edit: alasan itu berjalan di lingkungan saya adalah karena ada pengaturan .separator ',' di ~/.sqliterc.
      Sepertinya dulu saya menyadari bahwa saya terutama memasukkan file CSV dan menjadikannya default.
  • Sedikit menyimpang: meskipun euro pada awalnya hanya ada secara elektronik, ia memiliki nilai tukar tetap terhadap mata uang lama negara-negara anggota zona euro.
    Khususnya, ia dipatok ke Deutsche Mark Jerman yang sudah mapan dan tepercaya.
    Jadi untuk menjelaskan “mengapa euro awalnya lemah”, perlu juga dijelaskan mengapa DEM saat itu lemah, dan penjelasan di paragraf tersebut sepertinya tidak lolos uji itu.

  • Untuk masalah kecil di mana Anda bisa mengunduh seluruh database setiap kali dan memperlakukannya sebagai read-only, nilai kesederhanaan tidak boleh diremehkan.
    Saya suka SQLite karena portabel seperti file .json atau .csv, tetapi lebih siap untuk berinteraksi seperti database.

    • Dengan clickhouse-local, file CSV lama pun bisa diperlakukan seperti database.
  • Intinya ada di sini.
    Hal-hal yang dalam kasus ini tidak perlu dilakukan: negosiasi hak akses, misalnya membayar atau berbicara dengan sales, memasukkan alamat email, nama perusahaan, dan jabatan ke database prospek seseorang, mematuhi kuota, autentikasi, membaca dokumentasi API, atau menangani masalah yang lebih serius daripada format dan struktur dasar.

    • Saya skeptis terhadap bagian “tidak perlu mematuhi kuota”.
      Bandwidth tidak gratis.
  • SQLite bisa membaca dan menulis file ZIP.
    https://sqlite.org/zipfile.html
    Saya penasaran apakah dekompresi bisa dilakukan dengan sqlite3 alih-alih gunzip.

    • Poin bagus.
      Jika file boleh disimpan ke disk, bisa dilakukan seperti ini:

sqlite3 -newline '' ':memory:' "SELECT data FROM zipfile('eurofxref-hist.zip')" \
| sqlite3 -csv ':memory:' '.import /dev/stdin stdin' \
"select ...;"

Melakukannya tanpa berkas sementara itu rumit. Misalnya, `readfile('/dev/stdin')` tidak berfungsi karena SQLite mencoba memakai `seek()`  
Cara yang sangat jelek bisa dilakukan: mengubah berkas ZIP menjadi heksadesimal dengan `xxd`, lalu memasukkannya ke literal string dalam kueri SQL:

`curl -s https://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist.zip \`  
`| { printf "SELECT data FROM zipfile(x'"; xxd -p | tr -d '\n'; printf "')"; } \`  
`| sqlite3 -newline '' \`  
`| sqlite3 -csv ':memory:' '.import /dev/stdin stdin' \`  
`"select ...;"`