golang mysql store arbitary number of rows in structure

huangapple go评论117阅读模式
英文:

golang mysql store arbitary number of rows in structure

问题

所以我有一个类似这样的 SQL 查询:

  1. SELECT accounts.id, accounts.username, accounts.password,
  2. accounts.created, accounts.last_logged_in, accounts.access,
  3. banned.reason, banned.expires,
  4. player.x, player.y, player.zone
  5. FROM accounts
  6. LEFT JOIN banned
  7. ON accounts.id = banned.account_Id
  8. INNER JOIN player
  9. ON accounts.id = player.account_Id
  10. WHERE accounts.username = username

如果我想在 Go 中将其存储在一个结构体中,我通常会这样做:

  1. type Account struct {
  2. Id int
  3. Username string
  4. Password string
  5. Email string
  6. Created time.Time
  7. LastLoggedIn time.Time
  8. AccessLevel int
  9. Location struct {
  10. Zone string
  11. }
  12. Banned []*Banned
  13. }
  14. type Banned struct {
  15. Reason string
  16. Expires time.Time
  17. }
  18. reply := new(Account)
  19. stmt, err := this.Database.Prepare("CALL findUser(?)")
  20. defer stmt.Close()
  21. if err != nil {
  22. logger.ERROR.Println(err)
  23. return err
  24. }
  25. err = stmt.QueryRow(args).Scan(&reply.Id, &reply.Username ... 你明白我的意思)
  26. 然而这样做不起作用因为 scan 会期望每个参数都有一个值而我们在 banned 上进行了左连接由于用户可能有 0 N 个封禁记录有什么最好的解决方法呢
  27. 非常感谢
  28. Zidsal
  29. <details>
  30. <summary>英文:</summary>
  31. so I have sql that looks like this
  32. SELECT accounts.id, accounts.username, accounts.password,
  33. accounts.created, accounts.last_logged_in, accounts.access,
  34. banned.reason, banned.expires,
  35. player.x, player.y, player.zone
  36. FROM accounts
  37. LEFT JOIN banned
  38. ON accounts.id = banned.account_Id
  39. INNER JOIN player
  40. ON accounts.id = player.account_Id
  41. WHERE accounts.username = username
  42. If I wanted to store this in a struct in go I would normally do this:
  43. type Account struct {
  44. Id int
  45. Username string
  46. Password string
  47. Email string
  48. Created time.Time
  49. LastLoggedIn time.Time
  50. AccessLevel int
  51. Location struct {
  52. Zone string
  53. }
  54. Banned []*Banned
  55. }
  56. type Banned struct {
  57. Reason string
  58. Expires time.Time
  59. }
  60. reply := new(Account)
  61. stmt, err := this.Database.Prepare((&quot;CALL findUser(?)&quot;))
  62. defer stmt.Close()
  63. if err != nil {
  64. logger.ERROR.Println(err)
  65. return err
  66. }
  67. err = stmt.QueryRow(args).Scan(&amp;reply.Id, &amp;reply.Username ... you get the idea)
  68. however this is not going work because scan is going to expect a value for every argument and we have left joined onto banned! As the user could have 0 - N bans whats the best way to tackle this?
  69. Many thanks
  70. Zidsal
  71. </details>
  72. # 答案1
  73. **得分**: 2
  74. 我觉得你的例子或问题可能并不是你真正想描述的因为它们并没有真正解决同一个问题
  75. 根据提供的例子你有两种不同的类型需要扫描一个是`Account`一个是左连接的`Banned`),它们将在结果的每一行中重复出现所以你只需要在扫描`Account`结构体的同时创建一个新的`Banned`结构体并将其用于扫描数值然后将其添加到`Account.Banned`切片中循环处理每一行就完成了
  76. 根据你的问题我认为你的SQL查询可能有些问题你有多个*账户*每个账户都有多个*封禁记录*你希望每一行结果中只有一个账户并包含该账户的所有封禁记录为了实现这个目标你需要在查询中使用`GROUP BY`语句以便每个账户只有一行结果然后最聪明的方法是使用`GROUP_CONCAT`将每个封禁记录合并到一个属性中然后你可以相应地解析它以下是一个简化的示例
  77. SELECT accounts.id, GROUP_CONCAT(banned.id SEPARATOR ',') as bans
  78. FROM accounts
  79. LEFT JOIN banned
  80. ON accounts.id = banned.account_Id
  81. WHERE accounts.username = username
  82. GROUP BY accounts.id
  83. 你只需要将`bans`列扫描为一个字符串然后按逗号分割它等等在你的情况下解析会更加复杂因为你需要在一个列中获取两个值但原理是相同的
  84. <details>
  85. <summary>英文:</summary>
  86. I feel that either your example or question isn&#39;t what you really wanted to describe, since they don&#39;t really ask for the same problem.
  87. Reading the provided example, you have two different types to scan into (an `Account`, left join a `Banned`), that will be repeated for each row of the result. So you just have to create a new `Banned` struct at the same time that your `Account` struct and use it to scan the values, then add it to the `Account.Banned` slice. Loop for each row, and you&#39;re done.
  88. Reading your question, I think that your sql query is somewhat wrong: you have multiples *accounts* that each have several *bans*, and you would like to have one account by result row with every ban in it. To do that, you will need to tweak your query with a `GROUP BY` statement to get one row by account, then the smartest way would be to do a `GROUP_CONCAT` to get every ban into one attribute that you could then parse accordingly. Example (overly simplified to better expose principle):
  89. SELECT accounts.id, GROUP_CONCAT(banned.id SEPARATOR &#39;,&#39;) as bans
  90. FROM accounts
  91. LEFT JOIN banned
  92. ON accounts.id = banned.account_Id
  93. WHERE accounts.username = username
  94. GROUP BY accounts.id
  95. You just have to scan the `bans` column to a string, split it around `,`, etc. In your case, the parsing will be more complex, as you need 2 values in one column, but the principle is the same.
  96. </details>

huangapple
  • 本文由 发表于 2014年6月26日 01:56:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/24415428.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定