测试DuckDB插件对不同格式xlsx文件的读写效率
我们知道,xlsx是符合OOXML规范的格式,但其实这种格式对其组成部分的容忍度很高。
同一个表格,以不带标题行的3行*3列整数和浮点数据为例。
2659 0.116233238 0.802683908
2734 0.884122563 0.932195575
2745 0.458549115 0.30044512
在duckdb输出的xlsx中的sheet1.xml, 其sheetData标签包裹的部分如下:
如下
<sheetData>
<row r="1">
<c r="A1">
<v>2659</v>
</c>
<c r="B1">
<v>0.1162332380673584</v>
</c>
<c r="C1">
<v>0.8026839081352738</v>
</c>
</row>
<row r="2">
<c r="A2">
<v>2734</v>
</c>
<c r="B2">
<v>0.8841225634634741</v>
</c>
<c r="C2">
<v>0.9321955751461917</v>
</c>
</row>
<row r="3">
<c r="A3">
<v>2745</v>
</c>
<c r="B3">
<v>0.45854911529715814</v>
</c>
<c r="C3">
<v>0.30044511989122274</v>
</c>
</row>
</sheetData>
同一个文件,用wps打开后,再保存为xlsx, 同样的部分变成了
<sheetData>
<row r="1" spans="1:3">
<c r="A1">
<v>2659</v>
</c>
<c r="B1">
<v>0.116233238067358</v>
</c>
<c r="C1">
<v>0.802683908135274</v>
</c>
</row>
<row r="2" spans="1:3">
<c r="A2">
<v>2734</v>
</c>
<c r="B2">
<v>0.884122563463474</v>
</c>
<c r="C2">
<v>0.932195575146192</v>
</c>
</row>
<row r="3" spans="1:3">
<c r="A3">
<v>2745</v>
</c>
<c r="B3">
<v>0.458549115297158</v>
</c>
<c r="C3">
<v>0.300445119891223</v>
</c>
</row>
</sheetData>
比较发现,它给每个row标签增加了spans=“1:3”, duckdb原来生成的文件也能被wps打开,说明这个新增的部分不是必需的。不仅于此,像row和c标签中表示行号列号的r属性,其实也能省略,手工将sheet1.xml这些属性删除,再把它放回xlsx中,文件依然能正常打开,内容完全一致。
<sheetData>
<row>
<c>
<v>2659</v>
</c>
<c>
<v>0.1162332380673584</v>
</c>
<c>
<v>0.8026839081352738</v>
</c>
</row>
<row>
<c>
<v>2734</v>
</c>
<c>
<v>0.8841225634634741</v>
</c>
<c>
<v>0.9321955751461917</v>
</c>
</row>
<row>
<c>
<v>2745</v>
</c>
<c>
<v>0.45854911529715814</v>
</c>
<c>
<v>0.30044511989122274</v>
</c>
</row>
</sheetData>
在文本编辑器,分别选择以上部分,得到大小分别为453、481、372字节,简单数学计算可得,当数据规模扩大时,这个比例大体不变,而部分占整个xml文件的比例会越来越大,因此最终xml文件的大小也差不多这个比例。
下面就实际测试一下4万行256列的随机数据,读取不同标签格式xlsx的用时。
create table t as select i//256 r, i%256 c, random() v from range(10240000)t(i);create table t40000_256 as pivot t on substr((1000+c)::varchar, -3) using sum(v);copy t40000_256 to 'duck40000_256.xlsx' with(header 0);create table duck2 as from 'duck40000_256.xlsx';
Run Time (s): real 5.172 user 4.703125 sys 0.421875
D from duck2 limit 2;
┌────────┬────────────────────┬────────────────────┬───┬─────────────────────┬────────────────────┬────────────────────┐
│ A1 │ B1 │ C1 │ … │ JU1 │ JV1 │ JW1 │
│ double │ double │ double │ │ double │ double │ double │
├────────┼────────────────────┼────────────────────┼───┼─────────────────────┼────────────────────┼────────────────────┤
│ 2659.0 │ 0.1162332380673584 │ 0.8026839081352738 │ … │ 0.35541860334927 │ 0.6544321929522837 │ 0.4958646194732272 │
│ 2734.0 │ 0.8841225634634741 │ 0.9321955751461917 │ … │ 0.25984720734224387 │ 0.8055294819314434 │ 0.1164436018095856 │
├────────┴────────────────────┴────────────────────┴───┴─────────────────────┴────────────────────┴────────────────────┤
│ 2 rows 257 columns (6 shown) │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
Run Time (s): real 0.073 user 0.062500 sys 0.000000
再将它用wps保存一次,保存前后的大小
2025/08/20 21:49 130,892,489 duck40000_256.xlsx2025/08/20 22:01 138,414,845 wps40000_256.xlsxD create table t3 as from 'wps40000_256.xlsx';Run Time (s): real 4.136 user 4.062500 sys 0.062500
不可思议,居然多了spans=标签,文件大的wps格式xlsx读取更快,还快了20%。这是excel插件的个例,还是各种读取插件的共性?
再用rusty_sheet插件测试
D load rusty_sheet;D create table duck3 as from read_sheet('duck40000_256.xlsx',header=0);
Run Time (s): real 8.584 user 8.250000 sys 0.312500D create table wps3 as from read_sheet('wps40000_256.xlsx',header=0);
Run Time (s): real 8.531 user 8.140625 sys 0.375000
这次两种格式的差距可以忽略不计。
再用spatial插件测试
load spatial;
D create table wps4 as from st_read('wps40000_256.xlsx');
Run Time (s): real 10.561 user 10.359375 sys 0.203125D create table duck4 as from st_read('duck40000_256.xlsx');
Run Time (s): real 10.746 user 10.828125 sys 0.078125
差距同样可以忽略不计。
再用sheetreader插件测试
load sheetreader;
D create table wps5 as from sheetreader('wps40000_256.xlsx');
Run Time (s): real 1.964 user 4.921875 sys 0.390625
D from wps5 limit 2;
┌──────────┬───────────────────┬───────────────────┬───┬───────────────────┬───────────────────┬───────────────────┐
│ Numeric0 │ Numeric1 │ Numeric2 │ … │ Numeric254 │ Numeric255 │ Numeric256 │
│ double │ double │ double │ │ double │ double │ double │
├──────────┼───────────────────┼───────────────────┼───┼───────────────────┼───────────────────┼───────────────────┤
│ 2659.0 │ 0.116233238067358 │ 0.802683908135274 │ … │ 0.35541860334927 │ 0.654432192952284 │ 0.495864619473227 │
│ 2734.0 │ 0.884122563463474 │ 0.932195575146192 │ … │ 0.259847207342244 │ 0.805529481931443 │ 0.116443601809586 │
├──────────┴───────────────────┴───────────────────┴───┴───────────────────┴───────────────────┴───────────────────┤
│ 2 rows 257 columns (6 shown) │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
D create table duck5 as from sheetreader('duck40000_256.xlsx');
Run Time (s): real 1.679 user 8.046875 sys 0.093750
Invalid Error:
Failed to retrieve shared strings file
奇怪,同样没有sharedstring.xml,wps格式就能读取,而duckdb格式报错,因为duckdb格式xlsx读取失败,没法比较两种格式的读取速度差异。
明天再测一下没有其他标签的速度。