As you can see in my Golang application I have an array called layers
.
x
type Details struct {
Total int `json:"total"`
Gender string `json:"gender"`
}
type Layer struct {
ID int `json:"id"`
City string `json:"city"`
Details []Details `json:"details"`
}
layers := []Layer{
{
ID: 107509018555,
City: "London",
Details: []Details{
{
Total: 158,
Gender: "Male",
},
{
Total: 689,
Gender: "Female",
},
},
},
{
ID: 108509018556,
City: "New York",
Details: []Details{
{
Total: 756,
Gender: "Male",
},
{
Total: 356,
Gender: "Female",
},
},
},
}
I want to insert data of that array to the table of the PostgreSQL database. My question is how to create such SQL query in the application?
QUERY:
INSERT INTO layers (ID, CITY, DETAILS) VALUES
(107509018555, 'London', '[{"total":158,"gender":"Male"},{"total":689,"gender":"Female"}]'::json),
(108509018556, 'New York', '[{"total":756,"gender":"Male"},{"total":356,"gender":"Female"}]':json);
Advertisement
Answer
Because I cannot comment, I assume that:
- You’re using golang’s database/sql or similar package.
- In your database,
details
column has typeJSONB
A simple approach is to loop your slice layers
and build query string to this:
"INSERT INTO layers (id,city,details) VALUES ($1,$2,$3), ($4,$5,$6)"
For id
and city
, you can pass the params easily, however you need to pass JSON bytes for details
. Which means, you’ll need to marshal Details struct to JSON bytes for insert/update and unmarshal ‘details’ result to struct when SELECT
You will need to:
- Define new struct that encapsulates the slice of
Detail
(we’ll call itDetails
) then theDetails
should implement these interfaces. - Implements driver.Valuer interface to convert
Details
to JSON byte slice that can be understood by the database - Implements sql.Scanner interface to unmarshal JSON byte slice from database to your struct
The code should look like this:
type Detail struct {
Total int `json:"total"`
Gender string `json:"gender"`
}
// this will implement driver.Valuer and sql.Scanner
type Details []Detail
// so that the database can understand your value, useful for INSERT/UPDATE
func (d Details) Value() (driver.Value, error) {
return json.Marshal(d)
}
// so that the database can convert db value to your struct, useful for SELECT
func (d *Details) Scan(value interface{}) error {
b, ok := value.([]byte)
if !ok {
return errors.New("type assertion to []byte failed for scanning Details")
}
return json.Unmarshal(b, &d)
}
The complete code:
package main
import (
"database/sql"
"database/sql/driver"
"encoding/json"
"errors"
"fmt"
"log"
"strings"
_ "github.com/lib/pq"
)
type Layer struct {
ID int `json:"id"`
City string `json:"city"`
Details Details `json:"details"`
}
// this will implement driver.Valuer and sql.Scanner
type Details []Detail
// so that the database can understand your value, useful for INSERT/UPDATE
func (d Details) Value() (driver.Value, error) {
return json.Marshal(d)
}
// so that the database can convert db value to your struct, useful for SELECT
func (d *Details) Scan(value interface{}) error {
b, ok := value.([]byte)
if !ok {
return errors.New("type assertion to []byte failed for scanning Details")
}
return json.Unmarshal(b, &d)
}
type Detail struct {
Total int `json:"total"`
Gender string `json:"gender"`
}
func main() {
db, err := sql.Open("postgres", "postgres://user:pass@host:port/db?sslmode=disable")
exitIfError(err)
query, params := prepareQuery([]Layer{
{
ID: 107509018555,
City: "London",
Details: []Detail{{Total: 158, Gender: "Male"}, {Total: 689, Gender: "Female"}},
},
{
ID: 108509018556,
City: "New York",
Details: []Detail{{Total: 756, Gender: "Male"}, {Total: 356, Gender: "Female"}},
},
})
log.Println(query)
// INSERT INTO layers (id, city, details) VALUES ($1, $2, $3),($4, $5, $6)
log.Println(params)
// [107509018555 London [{158 Male} {689 Female}] 108509018556 New York [{756 Male} {356 Female}]]
result, err := db.Exec(query, params )
exitIfError(err)
rows, _ := result.RowsAffected()
log.Println(rows) // 2 rows inserted
}
func exitIfError(err error) {
if err != nil {
log.Fatal(err)
}
}
func prepareQuery(layers []Layer) (string, []interface{}) {
query := "INSERT INTO layers (id, city, details) VALUES "
params := []interface{}{}
x := 1
for _, layer := range layers {
query += fmt.Sprintf("($%d, $%d, $%d),", x, x+1, x+2)
params = append(params, layer.ID, layer.City, layer.Details)
x += 3
}
query = strings.TrimSuffix(query, ",")
return query, params
}
Reference: