如何在Zig中用zuckdb/DuckDB Appender正确写入Timestamp类型K线数据
问题
需要向DuckDB中追加带有Timestamp类型的K线数据,目前用VARCHAR类型存储时间,希望改为TIMESTAMP类型以支持基于时间戳的查询。API返回的时间格式为2024-01-02T05:00:00Z,使用Zig语言和zuckdb库,直接将time列改为TIMESTAMP并追加字符串时会触发Appender运行时错误。
解决方案
要正确存储TIMESTAMP类型数据,需完成两步:修改表结构为TIMESTAMP列,将字符串时间解析为DuckDB兼容的时间类型后再插入。
1. 修改表结构
把historical_bar表的time列类型从VARCHAR改为TIMESTAMP:
pub fn create(conn: *zuckdb.Conn) !void { _ = try conn.exec( \\CREATE TABLE historical_bar ( \\ symbol VARCHAR, \\ time TIMESTAMP, \\ open FLOAT, \\ high FLOAT, \\ low FLOAT, \\ close FLOAT, \\ number UINT32, \\ volume UINT64, \\ vwap FLOAT \\); , .{}); }
2. 解析时间字符串为DuckDB Timestamp类型
DuckDB的TIMESTAMP对应zuckdb库的zuckdb.Timestamp(微秒级Unix时间戳),需将API返回的RFC3339格式字符串解析为该类型:
fn parseRFC3339ToTimestamp(time_str: []const u8) !zuckdb.Timestamp { // 解析RFC3339格式时间 const datetime = try std.time.parseRFC3339(time_str); // 转换为秒级Unix时间戳 const unix_sec = std.time.epoch.datetimeToUnix(datetime); // 转成微秒级,匹配DuckDB TIMESTAMP的精度 return @intCast(zuckdb.Timestamp, unix_sec * 1_000_000); }
3. 修改批量追加逻辑
在插入时先将字符串时间解析为zuckdb.Timestamp,再传入Appender:
pub fn append(conn: *zuckdb.Conn, hbr: *model.HistoricalBarsResult) !void { var appender = try conn.appender(null, "historical_bar"); defer appender.deinit(); for (hbr.bars.items) |bar| { const timestamp = try parseRFC3339ToTimestamp(bar.time); appender.appendRow(.{ bar.symbol, timestamp, bar.open, bar.high, bar.low, bar.close, bar.number, bar.volume, bar.vwap }) catch |err| { std.debug.print("{}\n", .{bar}); if (err == error.DuckDBError) { if (conn.err) |derr| { std.log.err("DuckDB {s}\n", .{derr}); } else { return error.NoErrorAvailable; } } else { return err; } }; } try appender.flush(); }
替代方案:插入时用DuckDB函数转换
如果不想在Zig中解析时间,也可以用DuckDB的STRPTIME函数在插入时直接转换字符串,不过效率略低于提前解析:
// 改用参数化查询插入 pub fn append(conn: *zuckdb.Conn, hbr: *model.HistoricalBarsResult) !void { const stmt = try conn.prepare( \\INSERT INTO historical_bar (symbol, time, open, high, low, close, number, volume, vwap) \\VALUES (?, STRPTIME(?, '%Y-%m-%dT%H:%M:%SZ'), ?, ?, ?, ?, ?, ?, ?) ); defer stmt.deinit(); for (hbr.bars.items) |bar| { _ = try stmt.exec(.{ bar.symbol, bar.time, bar.open, bar.high, bar.low, bar.close, bar.number, bar.volume, bar.vwap }); } }
内容的提问来源于stack exchange,提问作者lele1c
相关产品推荐
相关产品推荐

