PostgreSQL插入多个表和多行数据

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

PostgreSQL inserting multiple tables and rows

问题

我有两个结构体,一个是Company,另一个是Service。它们之间有一个"一对多"的关系,即一个公司可以有多个服务。我想编写一个SQL查询,可以在一次查询中插入一个公司和多个附属于该公司的服务。

原始的SQL查询如下:

  1. WITH company AS (INSERT INTO companies(id, name) VALUES('1', 'acme') RETURNING id)
  2. INSERT INTO services(id, company_id, name) VALUES
  3. ('1', (SELECT company.id FROM company), 'cool service'),
  4. ('2', (SELECT company.id FROM company), 'cooler service');

我正在尝试使用Go的sql包来模拟这个查询。以下是我目前的尝试,我在顶部添加了结构体以增加清晰度:

  1. c := &Company{
  2. ID: uuid.NewV4().String(),
  3. Name: "test comp",
  4. }
  5. s := []*Service{
  6. &Service{
  7. ID: uuid.NewV4().String(),
  8. CompanyID: c.ID,
  9. Name: "test svc",
  10. },
  11. }
  12. c.Services = s
  13. values := []interface{}{
  14. c.ID,
  15. c.Name,
  16. }
  17. q := `
  18. WITH company AS (INSERT INTO companies(id, name) VALUES ($1, $2) RETURNING id)
  19. INSERT INTO services(id, company_id, name) VALUES
  20. `
  21. for _, row := range c.Services {
  22. q += "($1, (SELECT company.id FROM company), $2),"
  23. values = append(values, row.ID, row.Name)
  24. }
  25. q = strings.TrimSuffix(q, ",")
  26. stmt, err := s.DB.Prepare(q)
  27. if err != nil {
  28. return err
  29. }
  30. if _, err := stmt.Exec(values...); err != nil {
  31. return err
  32. }

但是,我不确定还有其他方法可以实现这个目标。使用这种方法,我得到以下错误:

  1. ERROR #08P01 bind message supplies 5 parameters, but prepared statement "1" requires 3

这是有道理的,因为我在执行exec时传递了5个参数,而预处理语句"1"只需要3个参数。但是,有没有办法在不将查询拆分为多个查询的情况下执行我的查询呢?

英文:

I have 2 structs one Company the other Service. They have a has-many relationship company to service. I'm trying to write an SQL query that will insert a company and multiple services attached to that company in one query.

RAW SQL:

  1. WITH company AS ( INSERT INTO companies(id, name) VALUES('1', 'acme') RETURNING id)
  2. INSERT INTO services(id, company_id, name) VALUES
  3. ('1', (select company.id from company), 'cool service'),
  4. ('2', (select company.id from company), 'cooler service');

I'm trying to imitate this using go's sql package. This is my attempt thus far: I've added the structs at the top just for clarity

  1. c := &Company{
  2. ID: uuid.NewV4().String(),
  3. Name: "test comp",
  4. }
  5. s := []*Service{
  6. &Service{
  7. ID: uuid.NewV4().String(),
  8. CompanyID: c.ID,
  9. Name: "test svc",
  10. },
  11. }
  12. c.Service = s
  13. values := []interface{}{
  14. c.ID,
  15. c.Name,
  16. }
  17. q := `
  18. WITH company as (INSERT INTO companies(id, name) VALUES ($1, $2)) INSERT INTO services(id, company_id, name) VALUES
  19. `
  20. for _, row := range c.Services {
  21. q += "($1, $2, $3),"
  22. values = append(values, row.ID, row.CompanyID)
  23. }
  24. q = strings.TrimSuffix(q, ",")
  25. stmt, err := s.DB.Prepare(q)
  26. if err != nil {
  27. return err
  28. }
  29. if _, err := stmt.Exec(values...); err != nil {
  30. return err
  31. }

I'm not sure how else to go about this but with this method I get this error:

  1. ERROR #08P01 bind message supplies 5 parameters, but prepared statement "1" requires 3

Which makes sense I'm passing 5 parameters to exec when prepared statement "1" which I'm guessing is the second one only requires 3. But how can I perform my query without having to split it up into more than 1 query?

答案1

得分: 0

正如你的错误消息所说,你的SQL语句只使用了3个绑定变量:

  1. WITH company as (INSERT INTO companies(id, name) VALUES ($1, $2)) INSERT INTO services(id, company_id, name) VALUES

和:

  1. q += "($1, $2, $3),"

你只是在每一行中重复使用$1$2$3。所以你构建的查询将会是这样的:

  1. WITH company as (INSERT INTO companies(id, name) VALUES ($1, $2)) INSERT INTO services(id, company_id, name) VALUES
  2. ($1, $2, $3),($1, $2, $3),($1, $2, $3),($1, $2, $3),

显然这不是你想要的结果。你需要在每次循环迭代中递增你的绑定变量编号。

我的方法可能是这样的:

  1. var i int = 3
  2. for _, row := range c.Services {
  3. q += fmt.Sprintf("($%d, $%d, $%d),", i, i+1, i+2)
  4. values = append(values, row.ID, row.CompanyID)
  5. i += 3
  6. }
英文:

Just as your error message says, your SQL statement only uses 3 bind variables:

  1. WITH company as (INSERT INTO companies(id, name) VALUES ($1, $2)) INSERT INTO services(id, company_id, name) VALUES

and:

  1. q += "($1, $2, $3),"

You're simply repeating $1, $2, and $3 for each of your rows. So your constructed query will look something like:

  1. WITH company as (INSERT INTO companies(id, name) VALUES ($1, $2)) INSERT INTO services(id, company_id, name) VALUES
  2. ($1, $2, $3),($1, $2, $3),($1, $2, $3),($1, $2, $3),

Clearly that's not what you want. You need to increment your bind variable numbering with each iteration through the loop.

My approach would probably be something like:

  1. var i int = 3
  2. for _, row := range c.Services {
  3. q += fmt.Sprintf("($%d, $%d, $%d),", i, i+1, i+2)
  4. values = append(values, row.ID, row.CompanyID)
  5. i += 3
  6. }

huangapple
  • 本文由 发表于 2017年2月26日 05:42:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/42461941.html
匿名

发表评论

匿名网友

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

确定