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

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 main


import (
  "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 here
  db, err := sql.Open("duckdb", "")
  if err != nil {
    log.Fatal("Failed to connect to database:", err)
  }
  defer db.Close()
}

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

初始化表和数据

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

package main

import (
	"database/sql"
	"fmt"
	"log"
	"time"

	_ "github.com/marcboeker/go-duckdb"
)

var db *sql.DB
var err error

func main() {
	// Empty datasource means, that DB will be solely in-memory, otherwise you could specify a filename here
	db, 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 result
	var id int
	var name string
	var startDt time.Time
	var isRemote bool

	// Query single row
	if 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 result
	var id int
	var name string
	var startDt time.Time
	var isRemote bool

	// Query multiple rows
	rows, err := db.Query("SELECT id, name, start_dt, is_remote FROM employee")
	if err != nil {
		log.Fatal(err)
	}
	defer rows.Close()

	// Print the results
	fmt.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 transactions
	tx, 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 main

import (
	"database/sql"
	"fmt"
	"log"
	"time"

	_ "github.com/marcboeker/go-duckdb"
)

var db *sql.DB
var err error

func main() {
	// Empty datasource means, that DB will be solely in-memory, otherwise you could specify a filename here
	db, 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 transactions
	tx, 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 result
	var id int
	var name string
	var startDt time.Time
	var isRemote bool

	// Query single row
	if 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 result
	var id int
	var name string
	var startDt time.Time
	var isRemote bool

	// Query multiple rows
	rows, err := db.Query("SELECT id, name, start_dt, is_remote FROM employee")
	if err != nil {
		log.Fatal(err)
	}
	defer rows.Close()

	// Print the results
	fmt.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.kler.cn/a/514092.html

相关文章:

  • 深度学习中Batch Normalization(BN)原理、作用浅析
  • 解决后端接口返回Long类型参数导致的精度丢失问题
  • 华为EC6110T-海思Hi3798MV310_安卓9.0_通刷-强刷固件包
  • 路径规划之启发式算法之二十八:候鸟优化算法(Migrating Birds Optimization, MBO)
  • 鸿蒙Harmony json转对象(1)
  • 媒体新闻发稿价格怎么算?移动端发稿价格低的原因有哪些?
  • 两个React项目部署在同一个域名,一个主地址,一个子地址,二级白屏等问题
  • 鸿蒙参考文档和问题记录
  • Python Web开发:使用FastAPI构建社交网络系统
  • 戴尔电脑用u盘重装系统_戴尔电脑用u盘重装win10系统教程
  • HTML 文本格式化详解
  • redis 5.0版本和Redis 7.0.15的区别在哪里
  • C#高级:用Csharp操作鼠标和键盘
  • 记一次 SpringBoot 静态资源加载慢的问题
  • SQLLOADER小实验
  • openharmony电源管理子系统
  • win32汇编环境,怎么得到磁盘的盘符
  • 【数据结构-堆】力扣1054. 距离相等的条形码
  • 写一个类似Chatgpt或豆包的交换界面详解
  • 【C++】在线五子棋对战项目网页版
  • springboot中配置logback-spring.xml
  • 什么是三高架构?
  • 小程序,uniapp中map的使用
  • 鸿蒙开发中的骨架图:提升用户体验的关键一环
  • 兼职全职招聘系统架构与功能分析
  • 过年远控家里电脑打游戏,哪款远控软件最好用?