SQLite、Golang 和联结表

Posted

技术标签:

【中文标题】SQLite、Golang 和联结表【英文标题】:SQLite, Golang and junction table 【发布时间】:2016-02-15 13:22:42 【问题描述】:

我想使用 Go 和 sqlite 创建一个小型图书数据库。我从这个建议SQLite foreign key examples 中提取了主要内容并重新开发了它。

package main                                                                                                                                                

import (
    "database/sql"
...
    _ "github.com/mattn/go-sqlite3"
)

...
db, err := sql.Open("sqlite3", "./foo.db")
if err != nil 
    log.Fatal(err)

defer db.Close()

sqlStmt := `
    create table books (
        id integer primary key autoincrement, 
        title text
    );

    create table booksauthors ( 
        bookid integer references books(id), 
        uthorid integer references authors(id) ON DELETE CASCADE ON UPDATE CASCADE
    );

    create table authors (
        id integer primary key autoincrement, 
        fname text, 
        mname text,
        lname text,
        unique (fname, mname, lname) on conflict ignore
    );
`

所以,我想保留唯一作者的列表并保持与书表的多对多连接(一本书可能有不止一个作者,而作者可能写不止一本书)。

然后我只是循环添加书籍,获取LastIndexID并将其放入连接表(代码为说明而减少,b为书籍结构):

tx, err := db.Begin()
if err != nil 
    log.Fatal(err)


res, err := db.Exec("Insert into books(title) values(?)", b.Title)
if err != nil 
    log.Fatal(err)

b_id, _ := res.LastInsertId()
for _, a := range b.Authors 
    res, err = db.Exec("Insert into authors(fname, mname, lname) values(?, ?, ?)", a.Fname, a.Mname, a.Lname)
    if err != nil 
        log.Fatal(err)
    
    a_id, _ := res.LastInsertId()
    fmt.Println(a_id, b_id, a)

    res, err = db.Exec("Insert into booksauthors(bookid, authorid) values(?, ?)", b_id, a_id)
    if err != nil 
        log.Fatal(err)
    

tx.Commit()

麻烦来了 - 如果我添加多于一本同一作者的书,a_id 会增加,但联结表包含它的旧值。 例如:

Books:                       
id | Title                   
---|--------------------
1  | Atlas Shrugged pt.1 
2  | Atlas Shrugged pt.2
3  | Atlas Shrugged pt.3

Authors:
id | Fname | Mname | Lname 
---|-------|-------|------
702| Ayn   |       | Rand

Junction table:
BookId | AuthorID
-------|---------
 1     |  700
 2     |  701
 3     |  702

What I want - Junction table:
BookId | AuthorID
-------|---------
 1     |  702
 2     |  702
 3     |  702

我怎样才能修复它,以便正确的 AuthorId 会反映在桌子上?我不想使用 GORM 或任何 ORM 工具并尝试使用纯(嗯,或多或少)SQL 来解决它。

我现在看到的解决方案之一是我可以先 SELECT,然后如果没有找到,则 INSERT,然后再 SELECT 一次,但是我不确定这个想法有多惯用。请注意,我有大量记录要添加。

【问题讨论】:

【参考方案1】:

我的建议是SELECT 您的作者列表和INSERT 缺少的那些。解决方案很幼稚,但它有效且简单。 我不是最新的 SQLite 关系特性,但使用它们并不总是处理外键的最简单方法......。

现在,如果您要处理大量数据,请执行相同的操作,但首先要合并您的图书作者,这样您就可以只处理一个 SELECT 和一个 INSERT

【讨论】:

【参考方案2】:

作为一个快速的'n'dirty hack,我在添加作者后做了一个选择:

err = db.QueryRow("select id from authors where fname = ? and mname = ? and lname = ?", a.Fname, a.Mname, a.Lname).Scan(&a_id)
if err != nil 
    log.Fatal(err)

res, err = db.Exec("Insert into booksauthors(bookid, authorid) values(?, ?)", b_id, a_id)
if err != nil 
    enter code herelog.Fatal(err)

但我仍在寻找更好的答案...

【讨论】:

以上是关于SQLite、Golang 和联结表的主要内容,如果未能解决你的问题,请参考以下文章

golang SQLite3性能测试

纯golang+sqlite3 CRUD

golang sqlite3 CRUD

golang操作sqlite数据库

Golang/gin:如何将 db 传递给路由器功能

golang使用json格式实现增删查改