<p>I'd like to know how you guys deal with JOIN queries in Go.</p>
<p>Say you have 2 tables, <code>recipes</code> and <code>ingredients</code> with a one to many relationship, and one struct for each table, with the structs fields mirroring the tables's columns. How would you receive the result of a query like:</p>
<p><code>SELECT i.ingredient_name, r.recipe_name FROM ingredients i JOIN recipes r ON r.id = i.recipe_id</code></p>
<p>The best thing I could come up with right now is to have one struct per query, that's dedicated to receive the SELECTed columns from JOIN queries, but then is that needed every time ? Is there anything better / more idiomatic ?</p>
<p>Thanks for the help :)</p>
<hr/>**评论:**<br/><br/>peterbourgon: <pre><p>Presumably when you make that SELECT statement, you're doing it to satisfy (i.e. fill up) some domain type in your program. You can database/sql.Rows.Scan into it directly, i.e.</p>
<pre><code>type MySQLStore struct {
db *sql.DB
}
func (s *MySQLStore) GetRecipe(recipeID int) (r Recipe, err error) {
row := s.db.QueryRow(
"SELECT i.ingredient_name, r.recipe_name FROM ... WHERE r.recipe_id = ?",
recipeID,
)
if row == nil {
return r, ErrNoSuchRecipe
}
return r, row.Scan(&r.IngredientName, &r.RecipeName)
}
</code></pre></pre>QuietPort: <pre><p>cheers. So I was right when thinking that domain types must be prepared to receive JOIN queries, as opposed to composing the "tables's structs" together somehow.. ?</p></pre>peterbourgon: <pre><p>Hmm, I think "must" is too strong, I think it's a matter of perspective. I've given you one perspective, which is definitely my opinion! </p>
<p>Other people might say it makes sense to have "table structs" or "ORM types" or whatever, which mirror your DB tables, and then either use those types in your program directly, or define mappings or compositions from those types to your domain types. I would personally never structure an application this way, but I can see how people from other backgrounds might prefer it.</p></pre>QuietPort: <pre><p>Gotcha.</p>
<p>I'm indeed mainly working with rails right now (and I'm not a fan), so probably carrying inadequate reflexes along in my Go. I'm interested to hear what are the weaknesses of this approach compared to yours if you don't mind.</p></pre>metamatic: <pre><p>And of course, there's nothing stopping you from scanning into separate Ingredient and Recipe structs, with a different selection of columns going into each.</p></pre>Ploobers: <pre><p>Just make your struct match what your data looks like if that's what you are going for. You'll still have to determine when it's better to do a JOIN in a single query, meaning that you'll be duplicating scanning and using more bandwidth for the parent table, vs doing two separate queries, but that's the same for any language.</p>
<pre><code>type Recipe struct {
Name string
Ingredients []Ingredient
}
type Ingredient struct {
Name string
}
rows, err := db.Query("SELECT i.ingredient_name, r.recipe_name FROM ... WHERE r.recipe_id = ?", recipeID)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
r := Recipe{}
for rows.Next() {
i := Ingredient{}
err := rows.Scan(&i.Name, &r.Name)
if err != nil {
log.Fatal(err)
}
r.Ingredients = append(r.Ingredients, i)
}
</code></pre></pre>skidooer: <pre><blockquote>
<p>You'll still have to determine when it's better to do a JOIN in a single query, meaning that you'll be duplicating scanning and using more bandwidth for the parent table, vs doing two separate queries, but that's the same for any language.</p>
</blockquote>
<p>You could also return the joined values as an array column in RDBMSes with necessary support. You still have to consider the tradeoffs with each approach, but I think it makes reasoning about the data more clear when the array tradeoffs are acceptable.</p></pre>QuietPort: <pre><p>My example is a little contrived as I was especially interested in choosing the right approach for mapping results of JOIN queries and at the same time re-using "model" structs (if ever that was a good idea...).</p>
<p>From what I can see here, it looks like ad-hoc structs will be used wherever there is the need, which is what I wanted to know.</p>
<p>Thank you for the example, it helps :)</p></pre>Ploobers: <pre><p>I'm not using any ad-hoc structs, I am using what you refer to as "model" structs, assuming that <code>Recipe</code> and <code>Ingredient</code> represent your real-world objects. Like <a href="/u/peterbourgon" rel="nofollow">/u/peterbourgon</a>, I have never used a struct created only for the results of a specific query.</p></pre>Brasilberg: <pre><p>This works in Go:</p>
<p>type StructA { }</p>
<p>type Struct B{ }</p>
<p>type StructBoth {</p>
<p>StructA</p>
<p>StructB</p>
<p>...</p>
<p>}</p>
<p>...though many times I have a prefer a custom struct (maybe an anonymous one inside the function - I name them response or output) so I can customize which fields are converted etc.</p></pre>codingconcepts: <pre><p>My company roll our own database code and generate the boilerplate stuff. Currently it’s just MySQL but I’m working on CockroachDB-flavoured Postgres support at the moment :)</p>
<p>All open source of course!</p></pre>monkey_that: <pre><p>Is there anything public?</p></pre>codingconcepts: <pre><p>Yep! <a href="https://github.com/LUSHDigital/modelgen" rel="nofollow">https://github.com/LUSHDigital/modelgen</a></p></pre>: <pre><p>[removed]</p></pre>BrokenPhoneBooth: <pre><p>I'm dad</p></pre>justinlindh: <pre><p>bad bot</p></pre>GoodBot_BadBot: <pre><p>Thank you, justinlindh, for voting on im_bot-hi_bot. </p>
<p>This bot wants to find the best and worst bots on Reddit. <a href="https://goodbot-badbot.herokuapp.com/" rel="nofollow">You can view results here</a>. </p>
<hr/>
<p><sup><sup>Even</sup></sup> <sup><sup>if</sup></sup> <sup><sup>I</sup></sup> <sup><sup>don't</sup></sup> <sup><sup>reply</sup></sup> <sup><sup>to</sup></sup> <sup><sup>your</sup></sup> <sup><sup>comment,</sup></sup> <sup><sup>I'm</sup></sup> <sup><sup>still</sup></sup> <sup><sup>listening</sup></sup> <sup><sup>for</sup></sup> <sup><sup>votes.</sup></sup> <sup><sup>Check</sup></sup> <sup><sup>the</sup></sup> <sup><sup>webpage</sup></sup> <sup><sup>to</sup></sup> <sup><sup>see</sup></sup> <sup><sup>if</sup></sup> <sup><sup>your</sup></sup> <sup><sup>vote</sup></sup> <sup><sup>registered!</sup></sup></p></pre>phoger: <pre><p>bad bot</p></pre>hybsuns: <pre><p>Like others' said, you need a designated struct to do so, and you will need to write the SQL code in Go.</p>
<p>I use a SQL builder, squirrel, for simple select and join statements like the one in your example. For complex join statements, I usually write a draft against the database to make sure it produces the result that I wanted, then I format it to fit Go's need.</p>
<p>Personally, I am not a fan of ORM as I usually don't trust the performance and/or the correctness when it comes to very complex query statements. I prefer to write my own DAL code and take the responsibility.</p>
<p>I hope it helps.</p></pre>
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传