Golang操作DuckDB实战案例分享

 更新时间:2025年01月23日 10:15:31   作者:梦想画家  
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应用程序中执行强大的数据分析操作。强大的数据管理系统和通用高效的编程语言之间的这种集成为更先进的数据处理应用打开了大门。有了本文提供的基础知识,你就可以开始探索这些可能性了。

以上就是Golang操作DuckDB实战案例分享的详细内容,更多关于Golang操作DuckDB的资料请关注脚本之家其它相关文章!

相关文章

  • GoFrame框架gset交差并补集使用实例

    GoFrame框架gset交差并补集使用实例

    这篇文章主要为大家介绍了GoFrame框架gset交差并补集使用实例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-06-06
  • 一文带你玩转Golang Prometheus Eexporter开发

    一文带你玩转Golang Prometheus Eexporter开发

    本文分两大块,一是搞清楚prometheus四种类型的指标Counter,Gauge,Histogram,Summary用golang语言如何构造这4种类型对应的指标,二是搞清楚修改指标值的场景和方式,感兴趣的可以了解一下
    2023-02-02
  • 一文详解golang中的gmp模型

    一文详解golang中的gmp模型

    这篇文章主要介绍了golang中的gmp模型的诞生、概念及调度讲解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-11-11
  • Go语言如何使用 Viper 来管理配置

    Go语言如何使用 Viper 来管理配置

    在现代软件开发中,良好的配置管理可以极大地提升应用的灵活性和可维护性,下面我们就来看看GO语言中如何使用 Viper 来管理配置吧
    2024-11-11
  • Golang 断言与闭包使用解析

    Golang 断言与闭包使用解析

    这篇文章主要介绍了Golang 断言与闭包使用解析,Go中的断言用于判断变量的类型,更多相关内容需要的朋友可以参考一下
    2022-07-07
  • Golang中的同步工具sync.Map示例详解

    Golang中的同步工具sync.Map示例详解

    sync.Map是Golang标准库提供的并发安全的Map类型,可以在多个goroutine并发读写Map的场景下不需要加锁,这篇文章主要介绍了Golang中的同步工具sync.Map详解,需要的朋友可以参考下
    2023-05-05
  • go语言中切片的长度和容量的区别

    go语言中切片的长度和容量的区别

    这篇文章主要介绍了go语言中切片的长度和容量的区别,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-04-04
  • Go中字符串处理 fmt.Sprintf与string.Builder的区别对比分析

    Go中字符串处理 fmt.Sprintf与string.Builder的区别对比分析

    在Go语言中,我们通常会遇到两种主要的方式来处理和操作字符串:使用fmt.Sprintf函数和string.Builder类型,本文给大家介绍它们在性能和用法上有一些关键区别,感兴趣的朋友跟随小编一起看看吧
    2023-11-11
  • Go的os/exec执行超时导致程序死机的解决方案

    Go的os/exec执行超时导致程序死机的解决方案

    这篇文章主要介绍了Go的os/exec执行超时导致程序死机的几种解决方案,文中通过代码示例给大家介绍的非常详细,对大家的学习或工作有一定的帮助,需要的朋友可以参考下
    2024-04-04
  • Go语言中uintptr和unsafe.Pointer的区别的实现小结

    Go语言中uintptr和unsafe.Pointer的区别的实现小结

    Go语言中的uintptr和unsafe.Pointer都是用于底层内存操作的类型,本文就来介绍一下uintptr和unsafe.Pointer的区别,具有一定的参考价值,感兴趣的可以了解一下
    2025-10-10

最新评论