在Linux上使用DuckCP实现从csv文件汇总数据到SQLite数据库的表

pypi网站Duckcp页面下载duckcp-0.1.1-py3-none-any.whl
一开始用的Python 3.11.2环境。
继续沿用上文打补丁的方法,得到一个支持python3.11.1的安装包。
因为缺少zip压缩工具,使用python程序来完成对修改后文件的重新压缩。

import os
import zipfile
from pathlib import Path

def zip_directory_contents(source_dir, output_zip):
    """
    压缩目录下的所有内容到ZIP文件(不包括根目录本身)
    
    :param source_dir: 要压缩的目录路径
    :param output_zip: 输出的ZIP文件路径
    """
    source_path = Path(source_dir).resolve()
    
    with zipfile.ZipFile(output_zip, 'w', zipfile.ZIP_DEFLATED) as zipf:
        for root, dirs, files in os.walk(source_dir):
            # 计算相对于源目录的路径
            rel_path = os.path.relpath(root, start=source_path)
            
            for file in files:
                file_path = Path(root) / file
                # 在ZIP中存储的相对路径(去掉最外层的a/)
                arcname = os.path.join(rel_path, file)
                zipf.write(file_path, arcname=arcname)
                
            # 确保空目录也被包含
            if not files and not dirs:
                # 添加空目录(必须以/结尾)
                rel_dir = os.path.relpath(root, start=source_path) + '/'
                zipf.writestr(rel_dir, '')


zip_directory_contents('/par/whl/', '/par/whloutput_zip')

将得到的zip文件重命名为原始文件名,然后安装

mv /par/whloutput_zip /par/duckcp-0.1.1-py3-none-any.whl

python3 pip.pyz install  /par/duckcp-0.1.1-py3-none-any.whl --break-system-packages -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple

安装过程没有报错。
1.创建配置元数据库

duckcp meta create
Traceback (most recent call last):
  File "/usr/local/bin/duckcp", line 5, in <module>
    from duckcp import main
  File "/usr/local/lib/python3.11/dist-packages/duckcp/__init__.py", line 5, in <module>
    from duckcp.boot import app, meta_command, repository_command, storage_command, transformer_command, task_command
  File "/usr/local/lib/python3.11/dist-packages/duckcp/boot/__init__.py", line 5, in <module>
    from duckcp.configuration.meta_configuration import enable_metadata_configuration
  File "/usr/local/lib/python3.11/dist-packages/duckcp/configuration/meta_configuration.py", line 9, in <module>
    from duckcp.entity.executor import Executor
  File "/usr/local/lib/python3.11/dist-packages/duckcp/entity/executor.py", line 69
    def records[T: tuple[Any, ...]](self, sql: str, *parameters: Any, constructor: RecordConstructorProtocol[T] = None) -> list[T]:
               ^
SyntaxError: expected '('

创建元数据库报错,应该就是不支持python3.11版本。改用Python 3.13.1,这一步顺利完成。需要说明,这些步骤都是由于我的环境和软件要求的Python版本不同引起的,如果版本正确, 这些问题都不会发生。

duckcp meta create

2025-07-28 07:02:08.448 INFO duckcp.service.meta_service#meta_create : 配置文件(/root/.config/com.yinfn.duckcp/configuration.db)初始化                                      ]8;id=117526;file:///usr/local/lib/python3.13/site-packages/duckcp/service/meta_service.pymeta_service.py]8;;:]8;id=511219;file:///usr/local/lib/python3.13/site-packages/duckcp/service/meta_service.py#2020]8;;
2025-07-28 07:02:08.452 INFO duckcp.service.meta_service#meta_create : 执行脚本(001-repositories.sql)                                                                       ]8;id=289077;file:///usr/local/lib/python3.13/site-packages/duckcp/service/meta_service.pymeta_service.py]8;;:]8;id=303663;file:///usr/local/lib/python3.13/site-packages/duckcp/service/meta_service.py#2424]8;;

命令输出的信息有点多,向软件作者张泽鹏先生请教,他说可以用-q选项减少输出。但我们首次使用时,信息多更有利于排查问题。
2.创建源和目标存储库
因为我要实现从csv文件汇总数据,所以要在配置元数据库中登记csv文件存储位置。
通过以下命令完成

duckcp repository create 文件仓库 -k file --folder data

作为数据源的csv文件,直接复制自duckcp例子,因为复制出来的字符分隔符是tab字符,所以改名为progs.tsv

id	name	language
1	Joe	Java
2	Alice	JavaScript
3	Leon	C/C++
4	William	Java
5	James	C/C++
6	Enson	C/C++

对于目标,我一开始用的命令是,

duckcp repository create sqlite -k sqlite  --folder data --file sqlite.db

以为这样就能把data目录下的sqlite.db数据库作为目标,其实不然,打开配置元数据库检查,里面的内容如下。

./sqlite3 $HOME/.config/com.yinfn.duckcp/configuration.db
sqlite> .tables
credentials         snapshots           tasks               transformers      
repositories        storages            tasks_transformers

sqlite> .header on
sqlite> select * from repositories;
id|kind|code|properties|created_at|updated_at
1|file|文件仓库|{"folder":"/par/data"}|2025-07-28 07:18:32|2025-07-28 07:18:32
2|sqlite|sqlite|{"file":"/par/sqlite.db","folder":"/par/data"}|2025-07-28 07:20:25|2025-07-28 07:20:25

sqlite所在行的properties列显示不符合预期。
张泽鹏先生说,数据库无需使用–folder参数,只要在–file中写明完整路径即可。所以正确命令如下:

duckcp repository create sqlite2 -k sqlite --file data/sqlite.db

因为duckcp本质上是一个ETL工具,它不会帮我们建库,所以要事先手工用sqlite软件实际创建data目录下的sqlite.db数据库,并建立保存目标数据的表。

./sqlite3 data/sqlite.db
sqlite> create table prog_sum(language varchar,cnt int);
sqlite> .exit

3.创建存储单元
存储单元这个名字的含义是数据库里的表的别名,我一开始漏掉了这一步,执行后面的命令就出错了main : 目标仓库(sqlite2)的存储单元(prog_sum)不存在

duckcp storage create prog_sum -r sqlite2 --table prog_sum

4.创建数据迁移任务
第一步是建立一个sql脚本,它负责读取源表(本例是Csv文件)执行汇总。
我直接照搬duckcp例子中的脚本,改了文件名。保存为data/trans.sql

select
  "language" as "编程语言",
  count(*) as "程序员人数"
from
  read_csv('progs.tsv')
group by
  "language"
order by
  "程序员人数" desc

然后执行命令

duckcp transformer create 数据统计 -s 文件仓库 -t sqlite2 -o prog_sum -f data/trans.sql

5.执行数据迁移任务

duckcp transformer execute 数据统计
2025-07-28 08:28:36.086 INFO duckcp.transform.database_transform#database_transform : 清空表(DELETE FROM "prog_sum")                                                  ]8;id=758441;file:///usr/local/lib/python3.13/site-packages/duckcp/transform/database_transform.pydatabase_transform.py]8;;:]8;id=170976;file:///usr/local/lib/python3.13/site-packages/duckcp/transform/database_transform.py#3030]8;;
2025-07-28 08:28:36.221 INFO duckcp.transform.database_transform#database_transform : 批量添加数据(INSERT INTO "prog_sum" ("编程语言", "程序员人数") VALUES (?, ?))   ]8;id=941332;file:///usr/local/lib/python3.13/site-packages/duckcp/transform/database_transform.pydatabase_transform.py]8;;:]8;id=508716;file:///usr/local/lib/python3.13/site-packages/duckcp/transform/database_transform.py#3535]8;;
2025-07-28 08:28:36.224 ERROR duckcp#main : table prog_sum has no column named 编程语言  

又报错了,但这是最后一个错误,提示信息也很明确,只要把目标表的列名改成和上述sql一模一样的列名就行了。

./sqlite3 data/sqlite.db
sqlite> alter table prog_sum rename language to "编程语言";
sqlite> alter table prog_sum rename cnt to "程序员人数";
sqlite> .exit

再次执行任务,当看到transformer_execute : 从仓库(文件仓库)迁移数据到仓库(sqlite2)的存储单元(prog_sum) 信息就表明数据迁移成功,此时可以用sqlite打开目标表,结果符合预期。

sqlite> select * from prog_sum;
C/C++|3
Java|2
JavaScript|1

下面验证源文件更新后,数据重新汇总的结果被转移到目标表。

./duckdb131
DuckDB v1.3.1 (Ossivalis) 2063dda3e6
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.

D select * from 'data/progs.tsv';
┌───────┬─────────┬────────────┐
│  id   │  name   │  language  │
│ int64 │ varcharvarchar   │
├───────┼─────────┼────────────┤
│     1 │ Joe     │ Java       │
│     2 │ Alice   │ JavaScript │
│     3 │ Leon    │ C/C++      │
│     4 │ William │ Java       │
│     5 │ James   │ C/C++      │
│     6 │ Enson   │ C/C++      │
└───────┴─────────┴────────────┘
D copy( from 'data/progs.tsv' union all select 7,'zhang3','sql') to 'data/progs.tsv'; 
D select * from 'data/progs.tsv';
┌───────┬─────────┬────────────┐
│  id   │  name   │  language  │
│ int64 │ varcharvarchar   │
├───────┼─────────┼────────────┤
│     1 │ Joe     │ Java       │
│     2 │ Alice   │ JavaScript │
│     3 │ Leon    │ C/C++      │
│     4 │ William │ Java       │
│     5 │ James   │ C/C++      │
│     6 │ Enson   │ C/C++      │
│     7 │ zhang3  │ sql        │
└───────┴─────────┴────────────┘
D .exit
root@217449ea9d61:/par# duckcp transformer execute 数据统计
2025-07-28 08:51:51.005 INFO duckcp.transform.database_transform#database_transform : 清空表(DELETE FROM "prog_sum")                                                  ]8;id=353910;file:///usr/local/lib/python3.13/site-packages/duckcp/transform/database_transform.pydatabase_transform.py]8;;:]8;id=259843;file:///usr/local/lib/python3.13/site-packages/duckcp/transform/database_transform.py#3030]8;;
2025-07-28 08:51:51.123 INFO duckcp.transform.database_transform#database_transform : 批量添加数据(INSERT INTO "prog_sum" ("编程语言", "程序员人数") VALUES (?, ?))   ]8;id=595510;file:///usr/local/lib/python3.13/site-packages/duckcp/transform/database_transform.pydatabase_transform.py]8;;:]8;id=232644;file:///usr/local/lib/python3.13/site-packages/duckcp/transform/database_transform.py#3535]8;;
2025-07-28 08:51:51.323 INFO duckcp.service.transformer_service#transformer_execute : 从仓库(文件仓库)迁移数据到仓库(sqlite2)的存储单元(prog_sum)                   ]8;id=489086;file:///usr/local/lib/python3.13/site-packages/duckcp/service/transformer_service.pytransformer_service.py]8;;:]8;id=370598;file:///usr/local/lib/python3.13/site-packages/duckcp/service/transformer_service.py#273273]8;;
root@217449ea9d61:/par# ./sqlite3 data/sqlite.db
SQLite version 3.42.0 2023-05-16 12:36:15
Enter ".help" for usage hints.
sqlite> .header on
sqlite> select * from prog_sum;
编程语言|程序员人数
C/C++|3
Java|2
sql|1
JavaScript|1

可见,结果多出了sql|1这行,它就是数据源插入zhang3的结果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值