Golang操作DuckDB實戰(zhàn)案例分享
DuckDB的主要優(yōu)點
- 內(nèi)存內(nèi)執(zhí)行:DuckDB主要在內(nèi)存中操作,但也支持內(nèi)存外執(zhí)行。這使得它能夠非??焖儆行У貓?zhí)行計算。
- 完整的SQL支持:DuckDB支持廣泛的SQL特性,這使得它對于各種類型的數(shù)據(jù)操作非常靈活。
- 事務支持:DuckDB支持事務,這是在許多應用程序中維護數(shù)據(jù)完整性和一致性的關鍵特性。
- 向量化執(zhí)行:DuckDB使用向量化查詢執(zhí)行,從而提高CPU利用率和性能。
- 易于集成:DuckDB為多種編程語言提供api,包括Python、R、c++、Rust、Java和Go。這使得將DuckDB集成到現(xiàn)有工作流和系統(tǒng)中變得更加容易。
- 開源:DuckDB是開源的,這意味著它的源代碼可以免費修改或增強。這允許社區(qū)驅(qū)動的改進和對特定用例的適應性。
環(huán)境準備
在開始使用DuckDB和Go之前,需要安裝DuckDB Go驅(qū)動程序。你可以使用Go的包管理器下載。在終端上運行以下命令:
github.com/marcboeker/go-duckdb
- 連接數(shù)據(jù)庫
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()
}
安裝了驅(qū)動程序后,現(xiàn)在可以從Go應用程序建立到DuckDB的連接。sql.Open() 函數(shù)用于連接到DuckDB,空數(shù)據(jù)源名稱表示我們正在使用內(nèi)存中的數(shù)據(jù)庫,你也可以指定數(shù)據(jù)庫文件名稱,實現(xiàn)數(shù)據(jù)持久化,相對于當前項目所在目錄。
初始化表和數(shù)據(jù)
現(xiàn)在連接已經(jīng)建立,你可以執(zhí)行各種數(shù)據(jù)庫操作了。我們首先創(chuàng)建表,然后插入初始化數(shù)據(jù)進行測試:
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)
}
}
在處理較大的數(shù)據(jù)集時,考慮使用事務和預處理語句以提高效率和安全性。記住,總是處理錯誤并在完成后關閉連接。
查詢單行或多行
要獲取數(shù)據(jù),可以使用QueryRow() 函數(shù)來選擇單行:
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)
}
}
不要忘記處理任何錯誤并正確關閉連接和結(jié)果集,如上所示。
要選擇多行,可以使用Query() 函數(shù):
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命令調(diào)用Query()函數(shù),從employee表中選擇所有記錄。
- 然后使用rows.Next()進入循環(huán),該循環(huán)遍歷查詢返回的每一行。
- 在循環(huán)中,我們使用Scan()函數(shù)將當前行的列復制到id、name、startDt和isRemote變量中。
- 然后使用fmt.Println()函數(shù)打印這些變量。
- 循環(huán)結(jié)束后,使用rows.Err()檢查迭代過程中的錯誤。如果有錯誤,我們使用log.Fatal(err)打印它。
錯誤處理和事務
在現(xiàn)實世界中,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)
}
}
此代碼開始事務,嘗試執(zhí)行插入語句,然后提交事務。如果在執(zhí)行期間發(fā)生錯誤,它將回滾在該事務中所做的任何更改。
完整代碼
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)
}
}
最后總結(jié)
DuckDB對Go的支持允許開發(fā)人員直接從他們的Go應用程序中執(zhí)行強大的數(shù)據(jù)分析操作。強大的數(shù)據(jù)管理系統(tǒng)和通用高效的編程語言之間的這種集成為更先進的數(shù)據(jù)處理應用打開了大門。有了本文提供的基礎知識,你就可以開始探索這些可能性了。
以上就是Golang操作DuckDB實戰(zhàn)案例分享的詳細內(nèi)容,更多關于Golang操作DuckDB的資料請關注腳本之家其它相關文章!
相關文章
一文帶你玩轉(zhuǎn)Golang Prometheus Eexporter開發(fā)
本文分兩大塊,一是搞清楚prometheus四種類型的指標Counter,Gauge,Histogram,Summary用golang語言如何構(gòu)造這4種類型對應的指標,二是搞清楚修改指標值的場景和方式,感興趣的可以了解一下2023-02-02
Go中字符串處理?fmt.Sprintf與string.Builder的區(qū)別對比分析
在Go語言中,我們通常會遇到兩種主要的方式來處理和操作字符串:使用fmt.Sprintf函數(shù)和string.Builder類型,本文給大家介紹它們在性能和用法上有一些關鍵區(qū)別,感興趣的朋友跟隨小編一起看看吧2023-11-11
Go的os/exec執(zhí)行超時導致程序死機的解決方案
這篇文章主要介紹了Go的os/exec執(zhí)行超時導致程序死機的幾種解決方案,文中通過代碼示例給大家介紹的非常詳細,對大家的學習或工作有一定的幫助,需要的朋友可以參考下2024-04-04
Go語言中uintptr和unsafe.Pointer的區(qū)別的實現(xiàn)小結(jié)
Go語言中的uintptr和unsafe.Pointer都是用于底層內(nèi)存操作的類型,本文就來介紹一下uintptr和unsafe.Pointer的區(qū)別,具有一定的參考價值,感興趣的可以了解一下2025-10-10

