使用duckdb

最近研究了一下duckdb的使用,也是踩了一些坑,这里记录一下。

duckdb 官网文档

duckdb是一款使用与部署简单的列式数据库,参考于sqlite的设计,使用起来仅需一个动态库。其支持从多种数据格式的文件直接读取数据,如csv、json、parquet等文件。同时提供了丰富的API供开发人员使用,支持多种主流语言。当然,作为一款数据库,对SQL语言的支持也是必不可少,不过其SQL语法有些地方与其他的SQL也稍有不同,建议使用时候翻阅官方文档。

我这边使用的是C++语言,最开始使用的是官网提供的C++的API,实际测试下来发现,C++版本的API性能有很大问题,C++版本API主要是使用SQL语句进行操作,Connection提供的接口几乎仅有一个Query。duckdb虽然支持SQL,但是使用SQL对 其操作性能比较差,经测试下来发现比Mysql还要低一个数量级(本文仅介绍duckdb的使用,性能测试这一块,请参考我的另一篇博客:数据库性能测试:Mysql、Sqlite、Duckdb)。

后来,我改用C版本的API,类似sqlite风格和函数,使用其自定义的 datachunk和vector进行数据的读取操作。我这边写了一个简单的demo,代码如下:

#include <iostream>
#include <vector>
#include "include/duckdb.hpp"

using namespace std;

struct Tick
{
	int64_t UpdateTs;
	double LastPrice;
	double LastTraded;
	double Volume;
};

int main()
{
    duckdb_database db;
    duckdb_connection connection;
	if (duckdb_open(nullptr, &db) == DuckDBError)
	{
		printf("duckdb_open Error.");
		return -1;
	}
	if (duckdb_connect(db, &connection) == DuckDBError)
	{
		printf("duckdb_open Error.");
		return -1;
	}
	std::vector<Tick*> ticks;
	duckdb_result result;
	auto sql = "select UpdateTs, LastPrice, LastTraded, Volume from read_parquet('./*.parquet');";
	if (duckdb_query(connection, sql, &result) == DuckDBError)
	{
		printf("duckdb_query Error. Sql:%s", sql);
		return -1;
	}
	
	while (true)
	{
		duckdb_data_chunk dataChunk = duckdb_fetch_chunk(result);
		if (dataChunk == nullptr)
		{
			break;
		}
		idx_t rowCount = duckdb_data_chunk_get_size(dataChunk);

		duckdb_vector column0 = duckdb_data_chunk_get_vector(dataChunk, 0);
		duckdb_vector column1 = duckdb_data_chunk_get_vector(dataChunk, 1);
		duckdb_vector column2 = duckdb_data_chunk_get_vector(dataChunk, 2);
		duckdb_vector column3 = duckdb_data_chunk_get_vector(dataChunk, 3);

		int64_t* dataColumn0 = (int64_t*)duckdb_vector_get_data(column0);
		duckdb_hugeint* dataColumn1 = (duckdb_hugeint*)duckdb_vector_get_data(column1);
		double* dataColumn2 = (double*)duckdb_vector_get_data(column2);
		double* dataColumn3 = (double*)duckdb_vector_get_data(column3);

		uint64_t* validityColumn0 = duckdb_vector_get_validity(column0);
		uint64_t* validityColumn1 = duckdb_vector_get_validity(column1);
		uint64_t* validityColumn2 = duckdb_vector_get_validity(column2);
		uint64_t* validityColumn3 = duckdb_vector_get_validity(column3);

		for (auto row = 0; row < rowCount; ++row)
		{
			Tick* tick = new Tick();
			memset(tick, 0, sizeof(Tick));
			if (duckdb_validity_row_is_valid(validityColumn0, row))
			{
				tick->UpdateTs = dataColumn0[row];
			}
			if (duckdb_validity_row_is_valid(validityColumn1, row))
			{
				tick->LastPrice = duckdb_decimal_to_double(duckdb_decimal{ 24, 8, dataColumn1[row] });
			}
			if (duckdb_validity_row_is_valid(validityColumn2, row))
			{
				tick->LastTraded = dataColumn2[row];
			}
			if (duckdb_validity_row_is_valid(validityColumn3, row))
			{
				tick->Volume = dataColumn3[row];
			}
			ticks.push_back(tick);
		}

		duckdb_destroy_data_chunk(&dataChunk);
	}
	for (auto tick : ticks)
	{
		printf("UpdateTs:%lld, LastPrice:%f, LastTraded:%f, Volume:%f\n", tick->UpdateTs, tick->LastPrice, tick->LastTraded, tick->Volume);
	}
	printf("RecordCount:%lld", ticks.size());

	duckdb_destroy_result(&result);
	duckdb_disconnect(&connection);
	duckdb_close(&db);
	return 0;
}

需要注意的是,这里的LastPrice字段,在parquet文件中的存储格式为 decimal(24,8) 。因为C++还没有支持decimal类型,我这里使用的是double。在读取该字段时其不能直接使用 duckdb_decimal 类型进行读取,因为其数据块中存储的字段的实际类型是int16,int32,int64或hugeint,具体使用那种类型的整型与定义的decimal的宽度有关,需要使用对应的整型类型来读取,然后通过对 duckdb_decimal 类型构造复制来获得对于的demical类型,要转为double类型,还需要调用 duckdb_decimal_to_double 函数进行转换。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值