SQLite, Golang и соединительная таблица

Я хотел бы создать небольшую базу данных книг, используя Go и sqlite. Я взял основное из этого совета примеры внешнего ключа SQLite и немного переработал его.

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.

Одно из решений, которое я вижу сейчас, я могу сначала ВЫБРАТЬ, затем ВСТАВИТЬ, если ничего не найдено, а затем снова ВЫБРАТЬ, однако я не уверен, насколько эта идея идиоматична. Обратите внимание, что мне нужно добавить значительное количество записей.


person Paul Poloskov    schedule 15.02.2016    source источник


Ответы (2)


Я бы посоветовал SELECT составить список авторов и INSERT тех, кого не хватает. Решение наивное, но оно работает и просто. Я не знаком с реляционными функциями SQLite, но их использование не всегда является самым простым способом работы с внешними ключами….

Теперь, если вам нужно обработать большой объем данных, выполните ту же операцию, но начните с объединения авторов ваших книг, чтобы у вас было только одно SELECT, а затем одно INSERT.

person Elwinar    schedule 15.02.2016

В качестве быстрого и грязного взлома я сделал выбор после добавления автора:

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)
}

Но я все еще ищу лучший ответ...

person Paul Poloskov    schedule 15.02.2016