- eurofxref-hist.zip ของ ECB เป็นเพียงชุดไฟล์ CSV ของอัตราแลกเปลี่ยน แต่สามารถใช้แค่
curl, gunzip และ sqlite3 เพื่อหาวันที่ดอลลาร์แข็งค่าที่สุดเมื่อเทียบกับยูโรได้ทันที คือ 2000-10-26
- ต้นฉบับอยู่ในรูปแบบ wide format โดยมีคอลัมน์สกุลเงินต่อท้าย
Date ทำให้วิเคราะห์ได้ไม่สะดวก และต้องจัดรูปใหม่เป็น long format ในรูป Date,Currency,Rate
- เนื่องจากมี trailing comma ที่ท้ายแต่ละบรรทัด ตัวแยก CSV จะอ่านคอลัมน์ว่างเพิ่มเข้ามา และใน Pandas ต้องใช้
.iloc[:,:-1] เพื่อลบคอลัมน์สุดท้ายออกก่อน จึงจะได้ผลลัพธ์ melt ที่สะอาด
- CSV ที่จัดรูปแล้วสามารถอัปโหลดไปยัง csvbase ด้วย HTTP PUT แล้วนำไปต่อกับเครื่องมืออย่าง
gnuplot, DuckDB และ sqlite3 เพื่อทำกราฟ คำนวณค่าเฉลี่ยเคลื่อนที่ และโหลด CSV ผ่าน HTTP ได้
- ข้อมูลสาธารณะที่ดาวน์โหลดได้โดยไม่ต้องเจรจาเรื่องการเข้าถึง การยืนยันตัวตน โควตา หรืออ่านเอกสาร API ที่ซับซ้อน สามารถทำงานได้เหมือน open API และแม้แต่ไฟล์ zip ธรรมดาก็เป็นฐานสำหรับการแลกเปลี่ยนข้อมูลของแอปการเงินได้
สอบถามอัตราแลกเปลี่ยนด้วยไฟล์ zip เพียงไฟล์เดียว
- ECB เผยแพร่ข้อมูลอัตราแลกเปลี่ยนย้อนหลังระหว่างยูโรกับสกุลเงินอื่นในรูป ไฟล์ zip ทางการ
- ไปป์ไลน์ด้านล่างจะดาวน์โหลดข้อมูล แตกไฟล์ แล้วให้ SQLite แบบอินเมมโมรีอ่าน CSV จากนั้นจัดเรียงตามค่า USD และคืนวันที่แรกออกมา
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;"
- ผลลัพธ์คือ
2000-10-26
curl -s ช่วยลดข้อความรบกวนจาก standard error และ gunzip ใช้คลายบีบอัดไฟล์ zip
- บน Mac OS หรือ BSD นั้น
gunzip สาย BSD ไม่รองรับไฟล์ zip จึงต้องใช้ bsdtar -xOf - แทน
sqlite3 ':memory:' ใช้ฐานข้อมูลในหน่วยความจำ และ .import /dev/stdin stdin จะนำข้อมูลจาก standard input เข้าเป็นตาราง stdin
จัดรูป CSV และใช้ Pandas melt
- ส่วนหัวของ CSV ต้นฉบับเป็น wide format เช่น
Date,USD,JPY,BGN,CYP,CZK,DKK,... โดยมีคอลัมน์วันที่ตามด้วยคอลัมน์ของแต่ละสกุลเงิน
- หากต้องการกรองและสรุปข้อมูล รูปแบบ long format อย่าง
Date,Currency,Rate จะจัดการได้ง่ายกว่า
- การแปลงจาก wide format เป็น long format มักเรียกว่า melt
- ฐานข้อมูล SQL ส่วนใหญ่ไม่มีโอเปอเรชันที่ตรงกับ melt จึงทำให้ Pandas มีประโยชน์มากในการจัดรูปข้อมูล
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)'
- ไฟล์ของ ECB มี trailing comma อยู่ท้ายทุกบรรทัด ทำให้ตัวแยก CSV อ่านคอลัมน์ว่างเพิ่มเข้ามาที่ท้าย
- คอลัมน์ว่างนี้ทำให้เกิดแถวที่ไม่มีประโยชน์ตอนท้ายของผลลัพธ์
melt จึงจำเป็นต้องลบออก
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] คือการเลือกทุกแถวและทุกคอลัมน์ยกเว้นคอลัมน์สุดท้าย
- แม้ข้อมูลอัตราแลกเปลี่ยนของ ECB จะต้องจัดรูปก่อนใช้งาน แต่ก็สามารถใช้งานได้ทันทีโดยไม่ต้องเจรจาเรื่องการเข้าถึง การชำระเงิน คุยกับฝ่ายขาย ส่งอีเมล ชื่อบริษัท หรือตำแหน่งงาน รวมถึงไม่ต้องเจอกับโควตา การยืนยันตัวตน หรือการอ่านเอกสาร API
- เพราะต้องแค่จัดการรูปแบบพื้นฐานกับปัญหาเรื่องโครงสร้างข้อมูลเท่านั้น จึงถือว่าค่อนข้างดีเมื่อเทียบกับการเผยแพร่ข้อมูลสาธารณะอื่น ๆ
อัปโหลดข้อมูลที่จัดรูปแล้วไปยัง csvbase
- CSV ที่จัดรูปแล้วสามารถอัปโหลดไปยัง ตาราง csvbase เพื่อหลีกเลี่ยงการจัดรูปซ้ำ ๆ
- เพียงต่อ
curl อีกตัวไว้ท้ายไปป์ไลน์เดิม ก็สามารถอัปโหลด CSV ด้วย 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 - จะอัปโหลดข้อมูลที่รับมาจาก standard input ไปยัง URL ที่ระบุ
- หากยังไม่มีตารางใน csvbase ระบบจะสร้างใหม่ และถ้ามีอยู่แล้วก็จะนำข้อมูลใส่เข้าไปในตารางนั้น
-n ใช้ ข้อมูลยืนยันตัวตน จาก ~/.netrc
วาดกราฟอัตราแลกเปลี่ยนด้วย gnuplot
- ตาราง csvbase ที่จัดรูปแล้วสามารถดึง CSV ด้วย
curl แล้วต่อกับ grep, cut และ 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'"
- คำสั่งนี้วาดจุดข้อมูลมากกว่า 6,000 จุดให้อ่านพอได้ในรูป ASCII art บน เทอร์มินัลตัวอักษรขนาด 80x25
- การตั้งค่า
gnuplot ถูกปรับให้รับอินพุตแบบ CSV และวาดกราฟเส้นของวันที่กับอัตราแลกเปลี่ยน
set datafile separator ',': ระบุว่าอินพุตเป็น CSV
set term dumb: วาดเป็น ASCII art
plot -: รับข้อมูลจาก standard input
using 1:2 with lines: ใช้คอลัมน์ 1 และ 2 คือวันที่และอัตราแลกเปลี่ยนมาวาดเส้น
title 'usd': ตั้งชื่อเส้นเป็น usd
- ยังสามารถส่งออกเป็นภาพ SVG ได้ และหากต้องการให้ดูเป็นข้อมูลอนุกรมเวลา ควรกำหนดให้แกน x เป็นเวลา พร้อมตั้งค่ารูปแบบเวลาและการหมุนฉลากบนแกน x
- เพื่อใช้ซ้ำ สามารถรวมไว้เป็นฟังก์ชัน Bash ชื่อ
plot_timeseries_to_svg ได้
คำนวณค่าเฉลี่ยเคลื่อนที่ด้วย DuckDB
- หากต้องการดูเส้นแนวโน้มของอัตราแลกเปลี่ยน USD สามารถใช้ 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
- หากไม่มี
duckdb ก็แปลงคิวรีเดียวกันให้ใช้กับ sqlite3 ได้ไม่ยาก
- DuckDB คล้าย SQLite แต่เป็นแบบ column-oriented ไม่ใช่แบบ row-oriented
- DuckDB สามารถอ่าน CSV จาก HTTP ได้โดยตรงและสร้างเป็นไฟล์ตารางได้
CREATE TABLE eurofxref_hist AS SELECT * FROM
read_csv_auto("https://csvbase.com/calpaterson/eurofxref-hist");
- DuckDB อนุมานชนิดข้อมูลได้ค่อนข้างดี และตรวจจับขนาดเทอร์มินัลเพื่อย่อการแสดงผลลัพธ์ขนาดใหญ่โดยอัตโนมัติ
- สำหรับคิวรีขนาดใหญ่ยังแสดงแถบความคืบหน้าได้ และสามารถส่งออกเป็นตาราง Markdown ได้ด้วย
วิธีที่ข้อมูลสาธารณะทำงานเหมือน open API
- เพียงมี CSV ในไฟล์ zip และเครื่องมือที่ติดตั้งได้ง่ายด้วย
brew install หรือ apt install ก็ทำงานได้หลายอย่างแล้ว
eurofxref-hist.zip เป็นรูปแบบที่เรียบง่ายมากสำหรับโปรโตคอลแลกเปลี่ยนข้อมูลระหว่างองค์กร
- แม้ไฟล์ zip นี้จะดูเล็ก แต่มีแอปการเงินจำนวนมากใช้งานทุกวัน
- เหตุผลที่ ECB ยังปล่อย trailing comma ไว้ อาจเป็นเพราะถ้าลบออกตอนนี้ โค้ดจำนวนมากอาจพังได้
- เมื่อข้อมูลสาธารณะถูกเผยแพร่อย่างเข้าถึงง่ายมาก มันก็ทำหน้าที่เป็น open API ได้เช่นกัน
- หาก API จำนวนมากมีลักษณะใกล้เคียงกับการแลกเปลี่ยนข้อมูลมากกว่าการเรียกใช้ฟังก์ชันระยะไกล มันก็แทบไม่ต่างในเชิงการใช้งานจากข้อมูลสาธารณะที่ดาวน์โหลดได้ง่ายนัก
URL แบบเรียบง่ายและ HTTP verbs ของ csvbase
- csvbase ใช้หนึ่ง URL ต่อหนึ่งตาราง
https://csvbase.com/<username>/<table_name>
https://csvbase.com/calpaterson/eurofxref-hist
- แต่ละ URL รองรับ HTTP verbs หลัก 4 แบบ
GET: รับ CSV และในเบราว์เซอร์สามารถรับเป็นหน้าเว็บได้
PUT: สร้างตารางใหม่ด้วย CSV ใหม่ หรือเขียนทับตารางเดิม
POST: เพิ่มแถว CSV จำนวนมากเข้าไปในตารางเดิม
DELETE: ลบตารางนั้น
- การยืนยันตัวตนใช้ HTTP Basic Auth
หมายเหตุเกี่ยวกับการจัดรูปข้อมูลและไปป์ไลน์
- ในบรรดาฐานข้อมูล SQL ฟังก์ชันที่เทียบได้กับ melt มี UNPIVOT ของ Snowflake และ PIVOT/UNPIVOT ของ MS SQL Server
- หนึ่งในเหตุผลสำคัญที่ R และ Pandas ถูกใช้อย่างแพร่หลายคือมีความสามารถด้าน การจัดรูปข้อมูล ที่แข็งแรง
- ไปป์ไลน์ Bash ทำงานแบบหลายโปรเซส โดยแต่ละโปรแกรมจะรันแบบขนานกันในโปรเซสแยกอิสระ
- ในเดือนตุลาคม 2000 อัตราแลกเปลี่ยนดอลลาร์เทียบยูโรอยู่ที่
0.8252 ซึ่งหมายความว่าดอลลาร์ 1 หน่วยสามารถซื้อยูโรได้ 1.21 หน่วย
- ยูโรเริ่มใช้ในเดือนมกราคม 1999 โดยยังไม่มีธนบัตรและเหรียญในตอนแรก ช่วงแรกมีอยู่เฉพาะในระบบธนาคาร และธนบัตรกับเหรียญจึงตามมาภายหลัง
1 ความคิดเห็น
ความคิดเห็นบน Hacker News
ผมจำไฟล์นี้ได้ ตอนทำงานที่ ECB เมื่อราว 15 ปีก่อน
ไฟล์นี้เป็นไฟล์ที่ถูกดาวน์โหลดจากเว็บไซต์ ECB มากที่สุดอย่างท่วมท้น และผู้คนจำนวนมากรวมถึงสถาบันการเงินต่าง ๆ จะดาวน์โหลดทุกวันเพื่อเอาไปอัปเดตระบบของตัวเอง
ในแต่ละวัน ช่วงไม่กี่นาทีหลังเวลาประกาศที่กำหนดไว้ ทราฟฟิกจะพุ่งขึ้นมาก และการทำให้เมื่อแตกไฟล์แล้วได้เป็นไฟล์ CSV ธรรมดา ๆ นั้นเป็นการตัดสินใจโดยตั้งใจ
ด้วยเหตุนี้จึงสามารถให้บริการไฟล์ได้อย่างเสถียรและรวดเร็วโดยใช้ทรัพยากรน้อย และทีมเล็ก ๆ ที่ดูแลเว็บไซต์สาธารณะของ ECB ในตอนนั้นก็น่าจะภูมิใจกับการตัดสินใจเชิงเทคนิคที่ให้บริการข้อมูลนี้ด้วยไฟล์สแตติกเพียงไฟล์เดียวได้มาก
มันไม่หวือหวา และไม่มีเฟรมเวิร์ก
เมื่อราว 15 ปีก่อน ผมเคยดูแลการแลกเปลี่ยนข้อมูลระหว่างระบบบันทึกข้อมูลผลิตภัณฑ์กับระบบย่อย/ระบบคู่ขนานที่เหลือมาจากการควบรวมและซื้อกิจการ ในบริษัทขนาดใหญ่เก่าแก่แห่งหนึ่งที่ทุกคนน่าจะเคยซื้อผลิตภัณฑ์ของเขา ส่วนใหญ่เป็นการนำเข้า/ส่งออกจำนวนมากผ่านไฟล์ความกว้างคงที่หรือไฟล์มีตัวคั่นที่ส่งกันทางเซิร์ฟเวอร์ SFTP
ตอนนั้นผลิตภัณฑ์นั้นมีอายุ 15 ปีแล้ว และมีแหล่งข้อมูลหรือการส่งออกแบบนั้นราว 20–30 รายการวิ่งไปมา แต่ก็ทำงานได้ดีมาก
มีความเป็นไปได้สูงว่ายังใช้อยู่จนถึงตอนนี้โดยแทบไม่เปลี่ยนแปลง และตอนนั้นฝั่งฟรอนต์เอนด์กำลังถูกเขียนใหม่จากของเดิมที่เป็น Smalltalk
ในบรรดาแหล่งข้อมูลที่เราใช้ มันเป็นตัวที่จัดการได้ง่ายที่สุด
สถาปนิกคงบอกว่า ZIP ไม่ใช่ฟอร์แมตที่ตรงกับสเปกของวัตถุประสงค์นี้ ฝ่ายคอมพลายแอนซ์คงบอกว่าต้องตรวจการรั่วไหลของข้อมูลส่วนบุคคล และฝ่ายความเสี่ยงก็คงบอกว่าต้องป้องกันไม่ให้ผู้ไม่ประสงค์ดีดาวน์โหลดไฟล์ได้
ฝ่ายเว็บก็น่าจะบอกว่าถ้าจะเพิ่มอะไรลงในไซต์ ต้องผ่านกระบวนการเปลี่ยนแปลงที่ได้รับอนุมัติก่อน
การดาวน์โหลดไฟล์ธรรมดา ๆ กับไฟล์ CSV นั้นยอดเยี่ยม
อยากให้มีที่อื่นเปิดเผยข้อมูลด้วยรูปแบบเรียบง่ายแบบนี้มากขึ้น และทุกครั้งที่ต้องเติม “ตะกร้าสินค้า” ในการดาวน์โหลดข้อมูลรัฐบาลสหรัฐฯ ก็รู้สึกเหมือนตายลงทีละนิด
มีเครื่องมือ wrapper มากมายที่ช่วยให้ไปป์ไลน์เฉพาะนี้ทำได้ง่ายขึ้น และถ้าต้องการมุมมองบนเว็บกับฟีเจอร์ขั้นสูงขึ้นอีกเล็กน้อย ของอย่าง Datasette ก็ใช้ได้ดี
อ่านไฟล์ ZIP เป็นสตรีม ประมวลผล CSV ทีละบรรทัดเพื่อแปลงข้อมูล แล้วโหลดเข้าฐานข้อมูลด้วย COPY FROM stdin ในกรณีของ Postgres ได้
มันดูสมเหตุสมผลและมีประโยชน์มาก แต่ผมเพิ่งเคยเจอจนถึงตอนนี้
เรามีรายงานเป็น CSV เยอะ เลยอยากลองเอาไปใช้รันคิวรีอย่างรวดเร็วเร็ว ๆ นี้
ตัวอย่างเช่น วิธีจัดการเครื่องหมายอัญประกาศอาจแตกต่างกันแบบ
"Look, this contains \"quotes\"!",012345กับ"Look, this contains ""quotes""!",012345และตัวอย่างที่พังยิ่งกว่าก็อาจเป็น"Look, this contains "quotes"!",012345หรือLook, this contains "quotes"!,012345ร่องรอยจากสเปรดชีตยังอาจทำให้เลข 0 ข้างหน้าถูกตัดทิ้ง เช่น
"Look, this contains ""quotes""!",12345ในทางทฤษฎี JSON ก็อาจถูกแก้ด้วยมือจนกลายเป็นไฟล์ที่พังครึ่ง ๆ กลาง ๆ ได้เหมือนกัน แต่ในความเป็นจริงผมแทบไม่เคยเห็นใครทำแบบนั้นกับไฟล์ JSON และค่าประเภทเลขซีเรียลก็มักจะคงเป็นสตริงใน JSON ไม่ใช่จำนวนเต็มที่แอป “ใจดี” จะตัดเลข 0 ข้างหน้าออก
ทำไมถึงต้องเป็นแบบนั้นกันแน่ มีเหตุผลที่สมควรอยู่บ้างไหม?
ต่อให้เปลี่ยน CSV เป็นเอกสาร JSON ที่บีบใน ZIP ข้อดีก็เหมือนเดิม
ปัญหาจริง ๆ คือมีอุปสรรคมากเกินไปในการดาวน์โหลดไฟล์เดียวที่ให้บริการแบบสแตติกอย่างเรียบง่าย
ผมเคยทำ API สำหรับหน่วยงานรัฐแห่งหนึ่ง ซึ่งข้อมูลเปลี่ยนแค่ปีละครั้งหรือแก้ไขนาน ๆ ครั้งเท่านั้น
ชุดข้อมูลทั้งหมดสามารถรวมเป็นไฟล์ ZIP ขนาดต่ำกว่า 1MB ได้ไฟล์เดียว แต่พอโซลูชันอาร์คิเท็กต์เข้ามากำหนดข้อกำหนด งานก็ขยายใหญ่ขึ้น
เขาไม่ให้ใช้แคช ด้วยเหตุผลว่าข้อมูลอาจเปลี่ยนขึ้นมาได้ ณ วินาทีที่มีคำขอพอดี ทำให้กลายเป็น API ที่ช้า และยังมีระบบเว็บฮุคที่ซับซ้อนเกินจำเป็นเพื่อแจ้งสมาชิกเมื่อข้อมูลเปลี่ยน
ไฟล์ ZIP ไฟล์เดียวอาจจะเรียบง่ายเกินไป แต่ก็ไม่ได้ต่างจากสิ่งที่ต้องการจริง ๆ มากนัก
ถ้าอยากทำให้ดูดีขึ้น ก็เพิ่มเว็บฮุคที่ถูกทริกเกอร์เมื่อไฟล์เปลี่ยน เพื่อให้ไคลเอนต์รู้ว่าควรดาวน์โหลดใหม่เมื่อไหร่ โดยไม่ต้องโพลวันละครั้ง
หรือแค่ทำสคริปต์ส่งอีเมลที่กำหนดไว้ล่วงหน้าไปยังเมลลิงลิสต์เมื่อมีการเปลี่ยนแปลงก็เพียงพอแล้ว
ถ้าไม่มีอะไรเปลี่ยนจากครั้งก่อน ก็จะได้คำตอบ HTTP 304 เปล่า ๆ กลับมา และถ้าเปลี่ยนแล้วก็รับไฟล์ ZIP ขนาดต่ำกว่า 1MB พร้อม ETag ใหม่อีกครั้ง ผมไม่รู้ว่าขาดอะไรไปตรงไหน
แคชเพิ่มความซับซ้อนและทำให้มีความเสี่ยงว่าต้อง revalidate แคชด้วยมือ ดังนั้นโซลูชันอาร์คิเท็กต์อาจจะถูกก็ได้
ถ้าต้องดาวน์โหลดไฟล์ 565KB เพียงเพื่อให้ได้ผลลัพธ์ค่าเดียวคือ
2000-10-26ก็ถือเป็น API ที่แย่มากถ้าเป็นกรณีที่ต้องดึงข้อมูลจำนวนมากแล้วนำไปให้ผู้ใช้อีกที CSV ที่มัดรวมเป็น ZIP นั้นยอดเยี่ยม และผมชอบมันมากกว่า protobuf สำหรับตารางเวลารถไฟแบบเรียลไทม์ของขนส่งสาธารณะที่รองรับหลายภาษาได้ไม่ดีเสียอีก
แต่ถ้าปฏิบัติกับมันเหมือน API สำหรับดึงค่าเดียว ก็เป็นการสิ้นเปลืองมหาศาล และหวังว่าจะไม่มีใครเอาไปใส่ในแอปด้วยวิธีนี้
ตัวบทความเองยอดเยี่ยม แต่ชื่อเรื่องให้ความรู้สึกเหมือนเป็น ข้ออ้างเชิงยั่วยุ เกินไป
ไม่มีเหตุผลเลยที่จะร้องขอมากกว่าวันละครั้ง และคนที่ใช้ข้อมูลแบบนี้ก็น่าจะต้องการตัวกรองหรือการรวมข้อมูลที่แตกต่างกันมาก
ถ้าใช้เพื่อดึงอัตราแลกเปลี่ยนปัจจุบัน ก็ใช่ เป็นการออกแบบที่ไม่ดี แต่สำหรับกรณีนั้นมีบริการอื่นอยู่แล้ว และไฟล์นี้ก็เหมาะกับกรณีใช้งานทั่วไปของมัน
แม้จะไม่เกี่ยวกับ API โดยตรง แต่เมื่อก่อนตอนผมซัพพอร์ตแอปพลิเคชันจัดการที่ดิน ก่อนเวอร์ชันใหม่ออกมันยังทำงานได้ดีแม้ในสำนักงานสาขาที่เชื่อมต่อผ่านดาวเทียมช้า ๆ ซึ่งอาจเร็วแค่ระดับ ISDN แต่เวอร์ชันใหม่กลับใช้ไม่ได้เลย
ผู้ขายบอกให้รันบนเซิร์ฟเวอร์ RDP แต่ผมคิดว่าเหลวไหล จึงตรวจสอบดู แล้วพบว่ามีการเรียกครั้งหนึ่งทำ
SELECT * FROM sometableโดยไม่มีเหตุผลใด ๆ ในขณะที่การเรียกอื่น ๆ ในการรันเดียวกันใช้ส่วน select ของ SQL อย่างเหมาะสมพอบอกเรื่องนี้กับผู้ขาย ตอนแรกพวกเขาสับสนมากว่าเรารู้ได้อย่างไร และสุดท้ายก็ออกเวอร์ชันใหม่ที่แก้ให้ใช้งานบนสายสัญญาณช้าได้
เข้าใจยากว่าทำไมการทดสอบภายในของพวกเขาถึงไม่จับเรื่องนี้ได้ แล้วกลับผลักภาระโซลูชันราคาแพงให้ลูกค้า
ถ้าคุณเคยดู JavaScript สมัยนี้แม้แต่นิดเดียว 565KB กับลอจิกสำหรับหาค่าขนาดใหญ่ในนั้นถือว่าเล็กมากตามมาตรฐานที่สมเหตุสมผลใด ๆ
บางคนมองว่า “วิธีได้มาซึ่งข้อมูล แม้จะรับข้อมูลทั้งหมดโดยไม่มีการกรอง” ก็คือ API แต่ส่วนตัวผมมองว่าการดาวน์โหลดทั้งตารางคือการดาวน์โหลด โมเดลข้อมูล ที่ไม่มีลอจิกทำงานกับโมเดลนั้น ส่วน API คือโลจิกที่กรองและส่งคืนบางส่วนของโมเดลตามวิธีที่ผมสนใจ
ผมเคยทำซอฟต์แวร์การเงินมามากทั้งฝั่งแบ็กเอนด์และฟรอนต์เอนด์ และฝั่งฟรอนต์เอนด์ก็พบได้บ่อยอย่างน่าเสียดายที่จะส่ง “ข้อมูล” ปริมาณประมาณนั้นตั้งแต่ก่อนจะไปถึงข้อมูลจริงด้วยซ้ำ
ฝั่งแบ็กเอนด์มันก็เป็นแค่การตัดสินใจด้านการออกแบบ และไม่มีอะไรเร็วไปกว่างานครอนทุกคืนที่พาร์สอัตราแลกเปลี่ยน สร้าง
todays-rates.jsonที่ปรับให้ตรงวัตถุประสงค์ แล้วเสิร์ฟเป็นไฟล์สแตติกให้แอปมือถือ เว็บ และไมโครเซอร์วิสไม่มีตรงไหนบอกว่าแอปมือถือต้องบริโภค ZIP-CSV-over-HTTP นี้โดยตรง
มีการปรับให้เหมาะสมแบบง่ายมากสำหรับคนที่บ่นว่าต้องดาวน์โหลดไฟล์ใหญ่ทุกครั้งที่ต้องการข้อมูลเล็ก ๆ เพียงค่าเดียว
ถ้ารับประกันว่าไฟล์เป็นแบบ เพิ่มต่อท้ายเท่านั้น และใช้การบีบอัดอย่าง HTTP gzip/brotli แทนไฟล์ ZIP ก็สามารถใช้ range request เพื่อรับเฉพาะข้อมูลใหม่หลังการอัปเดตครั้งล่าสุดได้
เพิ่มเฮดเดอร์ checksum ไว้เพื่อความอุ่นใจอีกสักตัว ก็จะกลายเป็น incremental API ที่ค่อนข้างมีประสิทธิภาพและเรียบง่ายมาก
แน่นอนว่าต้องเก็บสถานะ ต้องจ่ายต้นทุนการดาวน์โหลดครั้งแรกและการดูแลสถานะ และถ้าต้องการแค่อัตรา EUR/JPY ของวันที่ 2007-08-22 เพียงครั้งเดียว ก็ยังไม่มีประสิทธิภาพอยู่ดี
ยังอยู่ระหว่างทำมาก ๆ แต่โค้ดระดับ “คุณภาพสำหรับงานวิจัย” ตอนนี้อยู่ที่นี่: https://pypi.org/project/csvbase-client/
https://github.com/gtsystem/python-remotezip
แค่มีแพตช์รายวันหนึ่งไฟล์ ก็ลดแบนด์วิดท์ที่ฝั่งผมต้องใช้เพื่อทำให้ไฟล์เป็นปัจจุบันได้มากแล้ว
ทั้งนี้หมายถึงกรณีที่การดาวน์โหลดเพิ่มอีกไม่กี่ร้อย KB ต่อวันมีความหมาย ซึ่งส่วนใหญ่ก็น่าจะไม่ใช่แบบนั้น
ตัวอย่าง
sqliteมีพิมพ์ตกแม้ในสกรีนช็อตจะไม่มี แต่ต้องเพิ่ม อาร์กิวเมนต์ -csv ให้กับ
sqliteผมจะเพิ่มกลับเข้าไปและทำให้แคชหมดอายุ จะลองดูว่าเกิดอะไรผิดพลาดหลังจากกล่อมเด็ก ๆ เข้านอนแล้ว
แก้ไข: เหตุผลที่มันทำงานในสภาพแวดล้อมของผมคือมีการตั้งค่า
.separator ','อยู่ใน~/.sqlitercน่าจะเคยตั้งเป็นค่าเริ่มต้นไว้หลังจากตระหนักว่าผมมักใส่ไฟล์ CSV เป็นหลัก
ขอออกนอกเรื่องสักนิด แม้ยูโรในตอนแรกจะมีอยู่แค่ในรูปแบบอิเล็กทรอนิกส์ แต่ก็มี อัตราแลกเปลี่ยนคงที่ กับสกุลเงินเดิมของประเทศสมาชิกยูโรโซน
โดยเฉพาะอย่างยิ่ง มันถูกตรึงกับ Deutsche Mark ของเยอรมนีที่เป็นที่ยอมรับและเชื่อถือได้
ดังนั้นถ้าจะอธิบายว่า “ทำไมยูโรช่วงแรกถึงอ่อนค่า” ก็ต้องอธิบายด้วยว่า DEM ในเวลานั้นอ่อนค่าเพราะอะไร แต่คำอธิบายในย่อหน้านั้นดูเหมือนจะไม่ผ่านการตรวจสอบข้อนี้
สำหรับปัญหาขนาดเล็กที่สามารถดาวน์โหลดฐานข้อมูลทั้งหมดทุกครั้งแล้วจัดการแบบอ่านอย่างเดียวได้ อย่าประเมินค่าความเรียบง่ายต่ำไป
ผมชอบ SQLite เพราะพกพาได้เหมือนไฟล์
.jsonหรือ.csvแต่พร้อมสำหรับ การโต้ตอบแบบฐานข้อมูล มากกว่าclickhouse-localก็จัดการไฟล์ CSV เก่า ๆ เหมือนเป็นฐานข้อมูลได้ประเด็นหลักอยู่ตรงนี้
สิ่งที่ไม่ต้องทำในกรณีนี้: ต่อรองสิทธิ์การเข้าถึง เช่น ต้องจ่ายเงินหรือคุยกับฝ่ายขาย, เอาอีเมล ชื่อบริษัท ตำแหน่งงานไปใส่ในฐานข้อมูลลูกค้าเป้าหมายของใครสักคน, คอยรักษาโควตา, ยืนยันตัวตน, อ่านเอกสาร API, จัดการปัญหาที่ร้ายแรงกว่ารูปแบบและโครงสร้างพื้นฐาน
แบนด์วิดท์ ไม่ได้ฟรี
SQLite อ่านและเขียนไฟล์ ZIP ได้
https://sqlite.org/zipfile.html
สงสัยว่าสามารถแตกไฟล์ด้วย
sqlite3แทนgunzipได้ไหมถ้าอนุญาตให้บันทึกไฟล์ลงดิสก์ได้ ก็ทำแบบนี้ได้:
sqlite3 -newline '' ':memory:' "SELECT data FROM zipfile('eurofxref-hist.zip')" \| sqlite3 -csv ':memory:' '.import /dev/stdin stdin' \"select ...;"