当前位置: 首页 > news >正文

DuckDB:Golang操作DuckDB实战案例

DuckDB是一个嵌入式SQL数据库引擎。它与众所周知的SQLite非常相似,但它是为olap风格的工作负载设计的。DuckDB支持各种数据类型和SQL特性。凭借其在以内存为中心的环境中处理高速分析的能力,它迅速受到数据科学家和分析师的欢迎。在这篇博文中,我们将探索在Go中使用DuckDB。

在这里插入图片描述

DuckDB的主要优点

  • 内存内执行:DuckDB主要在内存中操作,但也支持内存外执行。这使得它能够非常快速有效地执行计算。
  • 完整的SQL支持:DuckDB支持广泛的SQL特性,这使得它对于各种类型的数据操作非常灵活。
  • 事务支持:DuckDB支持事务,这是在许多应用程序中维护数据完整性和一致性的关键特性。
  • 向量化执行:DuckDB使用向量化查询执行,从而提高CPU利用率和性能。
  • 易于集成:DuckDB为多种编程语言提供api,包括Python、R、c++、Rust、Java和Go。这使得将DuckDB集成到现有工作流和系统中变得更加容易。
  • 开源:DuckDB是开源的,这意味着它的源代码可以免费修改或增强。这允许社区驱动的改进和对特定用例的适应性。
    在这里插入图片描述

环境准备

在开始使用DuckDB和Go之前,需要安装DuckDB Go驱动程序。你可以使用Go的包管理器下载。在终端上运行以下命令:

github.com/marcboeker/go-duckdb
  • 连接数据库
package mainimport ("database/sql""log"_ "github.com/marcboeker/go-duckdb"
)func main() {// Empty datasource means, that DB will be solely in-memory, otherwise you could specify a filename heredb, err := sql.Open("duckdb", "")if err != nil {log.Fatal("Failed to connect to database:", err)}defer db.Close()
}

安装了驱动程序后,现在可以从Go应用程序建立到DuckDB的连接。sql.Open() 函数用于连接到DuckDB,空数据源名称表示我们正在使用内存中的数据库,你也可以指定数据库文件名称,实现数据持久化,相对于当前项目所在目录。

初始化表和数据

现在连接已经建立,你可以执行各种数据库操作了。我们首先创建表,然后插入初始化数据进行测试:

package mainimport ("database/sql""fmt""log""time"_ "github.com/marcboeker/go-duckdb"
)var db *sql.DB
var err errorfunc main() {// Empty datasource means, that DB will be solely in-memory, otherwise you could specify a filename heredb, err = sql.Open("duckdb", "data.db")if err != nil {log.Fatal("Failed to connect to database:", err)}defer db.Close()init_data()
}func init_data() {// Create table_, err := db.Exec(`CREATE TABLE employee (id INTEGER,name VARCHAR(20),start_dt TIMESTAMP,is_remote BOOLEAN)`)if err != nil {log.Fatal(err)}// Insert some data in table_, err = db.Exec(`INSERT INTO employee (id, name, start_dt, is_remote)VALUES(1, 'John Doe', '2022-01-01 09:00:00', true),(2, 'Jane Smith', '2023-03-15 10:00:00', false)`)if err != nil {log.Fatal(err)}
}

在处理较大的数据集时,考虑使用事务和预处理语句以提高效率和安全性。记住,总是处理错误并在完成后关闭连接。

查询单行或多行

要获取数据,可以使用QueryRow() 函数来选择单行:

func query_one() {// Variables to store query resultvar id intvar name stringvar startDt time.Timevar isRemote bool// Query single rowif err := db.QueryRow("SELECT id, name, start_dt, is_remote FROM employee WHERE id = ?", 1).Scan(&id, &name, &startDt, &isRemote); err != nil {if err == sql.ErrNoRows {log.Println("No rows found.")} else {log.Fatalf("unable to execute query: %v", err)}} else {fmt.Println("Select 1 row result:\nID:", id, "Name:", name, "Start Datetime:", startDt, "Is Remote:", isRemote)}
}

不要忘记处理任何错误并正确关闭连接和结果集,如上所示。

要选择多行,可以使用Query() 函数:

func query_all() {// Variables to store query resultvar id intvar name stringvar startDt time.Timevar isRemote bool// Query multiple rowsrows, err := db.Query("SELECT id, name, start_dt, is_remote FROM employee")if err != nil {log.Fatal(err)}defer rows.Close()// Print the resultsfmt.Println("Results:")for rows.Next() {err = rows.Scan(&id, &name, &startDt, &isRemote)if err != nil {log.Fatal(err)}fmt.Println("ID:", id, "Name:", name, "Start Datetime:", startDt, "Is Remote:", isRemote)}err = rows.Err()if err != nil {log.Fatal(err)}
}

我们用SQL命令调用Query()函数,从employee表中选择所有记录。

  • 然后使用rows.Next()进入循环,该循环遍历查询返回的每一行。
  • 在循环中,我们使用Scan()函数将当前行的列复制到id、name、startDt和isRemote变量中。
  • 然后使用fmt.Println()函数打印这些变量。
  • 循环结束后,使用rows.Err()检查迭代过程中的错误。如果有错误,我们使用log.Fatal(err)打印它。

错误处理和事务

在现实世界中,Go代码必须准备好处理错误和处理事务。SQL包提供了所有必要的工具:

func trans_insert() {// Error handling and transactionstx, err := db.Begin()if err != nil {log.Fatal(err)}defer tx.Rollback()_, err = tx.Exec(`INSERT INTO employee (id, name, start_dt, is_remote)VALUES(3000000000, 'id int64 instead of int32', '2022-06-17 11:00:00', true)`)if err != nil {log.Printf("ERROR: %s\n", err.Error()) // Do not fail, just print the error in output}err = tx.Commit()if err != nil {log.Fatal(err)}
}

此代码开始事务,尝试执行插入语句,然后提交事务。如果在执行期间发生错误,它将回滚在该事务中所做的任何更改。

完整代码

package mainimport ("database/sql""fmt""log""time"_ "github.com/marcboeker/go-duckdb"
)var db *sql.DB
var err errorfunc main() {// Empty datasource means, that DB will be solely in-memory, otherwise you could specify a filename heredb, err = sql.Open("duckdb", "data.db")if err != nil {log.Fatal("Failed to connect to database:", err)}defer db.Close()// init_data()query_one()// trans_insert()query_all()
}func init_data() {// Create table_, err = db.Exec(`CREATE TABLE employee (id INTEGER,name VARCHAR(20),start_dt TIMESTAMP,is_remote BOOLEAN)`)if err != nil {log.Fatal(err)}// Insert some data in table_, err = db.Exec(`INSERT INTO employee (id, name, start_dt, is_remote)VALUES(1, 'John Doe', '2022-01-01 09:00:00', true),(2, 'Jane Smith', '2023-03-15 10:00:00', false)`)if err != nil {log.Fatal(err)}
}func trans_insert() {// Error handling and transactionstx, err := db.Begin()if err != nil {log.Fatal(err)}defer tx.Rollback()_, err = tx.Exec(`INSERT INTO employee (id, name, start_dt, is_remote)VALUES(3000000000, 'id int64 instead of int32', '2022-06-17 11:00:00', true)`)if err != nil {log.Printf("ERROR: %s\n", err.Error()) // Do not fail, just print the error in output}err = tx.Commit()if err != nil {log.Fatal(err)}
}func query_one() {// Variables to store query resultvar id intvar name stringvar startDt time.Timevar isRemote bool// Query single rowif err := db.QueryRow("SELECT id, name, start_dt, is_remote FROM employee WHERE id = ?", 1).Scan(&id, &name, &startDt, &isRemote); err != nil {if err == sql.ErrNoRows {log.Println("No rows found.")} else {log.Fatalf("unable to execute query: %v", err)}} else {fmt.Println("Select 1 row result:\nID:", id, "Name:", name, "Start Datetime:", startDt, "Is Remote:", isRemote)}
}func query_all() {// Variables to store query resultvar id intvar name stringvar startDt time.Timevar isRemote bool// Query multiple rowsrows, err := db.Query("SELECT id, name, start_dt, is_remote FROM employee")if err != nil {log.Fatal(err)}defer rows.Close()// Print the resultsfmt.Println("Results:")for rows.Next() {err = rows.Scan(&id, &name, &startDt, &isRemote)if err != nil {log.Fatal(err)}fmt.Println("ID:", id, "Name:", name, "Start Datetime:", startDt, "Is Remote:", isRemote)}err = rows.Err()if err != nil {log.Fatal(err)}
}

最后总结

DuckDB对Go的支持允许开发人员直接从他们的Go应用程序中执行强大的数据分析操作。强大的数据管理系统和通用高效的编程语言之间的这种集成为更先进的数据处理应用打开了大门。有了本文提供的基础知识,你就可以开始探索这些可能性了。

http://www.lryc.cn/news/526162.html

相关文章:

  • MySQL入门(数据库、数据表、数据、字段的操作以及查询相关sql语法)
  • kotlin的协程的基础概念
  • Spring--SpringMVC使用(接收和响应数据、RESTFul风格设计、其他扩展)
  • 隐藏php版本信息x-powered-by
  • 哈夫曼树(构建、编码、译码)(详细分析+C++代码实现)
  • C++ 二叉搜索树
  • docker构建Java项目镜像常用的Java版本,国内私有仓库公网快速下载,解决从docker.io无法下载的问题
  • 低代码系统-氚云、简道云表单控件对比
  • 为什么IDEA提示不推荐@Autowired❓️如果使用@Resource呢❓️
  • Unity在WebGL中拍照和录视频
  • 爬虫基础之爬取某站视频
  • mongoDB常见指令
  • 人工智能之深度学习_[5]-神经网络优化学习率衰减优化正则化方法
  • Oracle之Merge into函数使用
  • 深度解析:哪种心磁图技术是心脏检查的精准之选?
  • SpringBoot--基本使用(配置、整合SpringMVC、Druid、Mybatis、基础特性)
  • 单片机-STM32 IIC通信(OLED屏幕)(十一)
  • 观察者模式 - 观察者模式的应用场景
  • 【C++】详细讲解继承(下)
  • 消息队列篇--原理篇--Pulsar(Namespace,BookKeeper,类似Kafka甚至更好的消息队列)
  • 扬帆数据结构算法之舟,启航C++探索征途——LeetCode深度磨砺:顺序表技术精进实践
  • 基于本地事务表+MQ实现分布式事务
  • 数据结构:二叉树—面试题(一)
  • 【Wordpress网站制作】切换语言的问题
  • 【第二天】零基础入门刷题Python-算法篇-数据结构与算法的介绍-五种常见的排序算法(持续更新)
  • Neural networks 神经网络
  • 汽车免拆诊断案例 | 2007 款日产天籁车起步加速时偶尔抖动
  • 代码随想录day3
  • Spring 面试题【每日20道】【其一】
  • leetcode刷题记录(八十九)——35. 搜索插入位置