Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Golang db.Query with sql join

Tags:

join

mysql

go

I would like to query for a question and all of its answers. The following two functions work just fine. The problem is I think this should be done in one function, with one query. (I removed error checking for brevity).

func QuestionById(id string) (*Question, error) {
    question := new(Question)
    _ = db.QueryRow("select * from question where question.id = ?", id).Scan(
        &question.Id,
        &question.LessonId,
        &question.Body,
        &question.Type,
    )

    return question, nil
}

func AnswersByQuestionId(id string) ([]*Answer, error) {
    rows, _ := db.Query("select * from answer where question_id = ?", id)
    defer rows.Close()

    answers := make([]*Answer, 0)

    for rows.Next() {
        answer := new(Answer)

        _ = rows.Scan(&answer.Id, &answer.Body, &answer.QuestionId, &answer.Correct)

        answers = append(answers, answer)
    }
    _ = rows.Err()

    return answers, nil
}

I would like to use a join query in this way (or something similar):

func QuestionByIdAndAnswers(id string) (*Question, []*Answer error) {
    rows, _ := db.Query("select * from question join answer on question.id = answer.question_id where question.id = ?", id)

    // more stuff here

    return question, answers, nil
}
like image 986
twharmon Avatar asked Aug 11 '17 14:08

twharmon


1 Answers

In general it must be something like this:

func QuestionByIdAndAnswers(id string) (*Question, []*Answer, error) {
  query := `
    SELECT q.id, q.body, a.id, a.question_id, a.body
    FROM question AS q
    JOIN answer AS a ON q.id = a.question_id
    WHERE q.id = ?
  `
  rows, err := db.Query(query, id)
  checkErr(err)

  question := &Question{}
  for rows.Next() {
    answer := &Answer{}
    err = rows.Scan(
      &question.ID,
      &question.Body,
      &answer.ID,
      &answer.QuestionID,
      &answer.Body,
    )
    checkErr(err)
    question.Answers = append(question.Answers, answer)
  }

  return question, question.Answers, nil
}

Please pay attention, I intentionally replaced:
SELECT * to SELECT q.id, q.body, a.id, a.question_id, a.body
with purpose to avoid errors like:
panic: sql: expected 6 destination arguments in Scan, not 5
which may occur when some columns added or deleted from table, so this query more robust.

And this is just basic implementation, you can extend it with more fields...

PS: Function checkErr also omitted for brevity.

like image 159
cn007b Avatar answered Sep 19 '22 13:09

cn007b