英文:
inserting into postgresql decimal field fails with error "cannot convert {125.00} to Int2"
问题
我是你的中文翻译助手,以下是翻译好的内容:
我对golang还比较新手。我正在尝试使用gin-gonic的web应用程序向一个具有数字字段的postgresql表中插入数据。
postgres=# \d user_txns;
Table "public.user_txns"
Column | Type | Collation | Nullable | Default
-------------+-----------------------+-----------+----------+---------
user_id | character varying(15) | | not null |
txn_code | smallint | | not null |
description | character varying(64) | | not null |
txn_amount | numeric(15,4) | | not null |
txn_type | smallint | | not null |
voucher | character varying(16) | | |
我正在使用jackc pgxpool将数据插入到表中,代码如下:
109 ▏ sql := `INSERT INTO user_txns VALUES ($1,$2, $3, $4, $5)`
▎ 110 ▏ _, err = tx.Exec(context.Background(), sql,
▎ 111 ▏ ▏ ▏ ▏ ▏ ▏ claims["phone"],
▎ 112 ▏ ▏ ▏ ▏ ▏ ▏ recharge,
▎ 113 ▏ ▏ ▏ ▏ ▏ ▏ "User recharge",
▎ 114 ▏ ▏ ▏ ▏ ▏ ▏ recharge.Amount,
▎ 115 ▏ ▏ ▏ ▏ ▏ ▏ credit,
▎ 116 ▏ )
▎ 117 ▏ if err != nil {
▎ 118 ▏ ▏ c.JSON(http.StatusInternalServerError, gin.H{"msg": err.Error()})
▎ 119 ▏ ▏ return
▎ 120 ▏ },
请求的有效载荷是一个具有以下结构的JSON请求:
{
"amount": 125.00
}
我将请求解析为以下定义的结构体:
type Recharge struct {
Amount string `json:"amount" binding:"required"`
}
插入操作失败,报错信息为:
"msg": "cannot convert {125} to Int2"
在向十进制字段插入数据时,应该使用哪种正确的golang数据类型?
谢谢。
英文:
I am fairly new to golang. I am trying to insert into a postgresql table which has a numeric field, using a web app gin-gonic.
postgres=# \d user_txns;
Table "public.user_txns"
Column | Type | Collation | Nullable | Default
-------------+-----------------------+-----------+----------+---------
user_id | character varying(15) | | not null |
txn_code | smallint | | not null |
description | character varying(64) | | not null |
txn_amount | numeric(15,4) | | not null |
txn_type | smallint | | not null |
voucher | character varying(16) | | |
I am using jackc pgxpool to insert into the table as below.
109 ▏ sql := `INSERT INTO user_txns VALUES ($1,$2, $3, $4, $5)`
▎ 110 ▏ _, err = tx.Exec(context.Background(), sql,
▎ 111 ▏ ▏ ▏ ▏ ▏ ▏ claims["phone"],
▎ 112 ▏ ▏ ▏ ▏ ▏ ▏ recharge,
▎ 113 ▏ ▏ ▏ ▏ ▏ ▏ "User recharge",
▎ 114 ▏ ▏ ▏ ▏ ▏ ▏ recharge.Amount,
▎ 115 ▏ ▏ ▏ ▏ ▏ ▏ credit,
▎ 116 ▏ )
▎ 117 ▏ if err != nil {
▎ 118 ▏ ▏ c.JSON(http.StatusInternalServerError, gin.H{"msg": err.Error()})
▎ 119 ▏ ▏ return
▎ 120 ▏ },
The payload is a json request that has the following structure:
{
"amount": 125.00
}
I unmarshal the request into a struct defined as below.
type Recharge struct {
Amount string `json:"amount" binding:"required"`
}
The insert fails with error
> "msg": "cannot convert {125} to Int2"
What is the right golang data type to use for inserting into a decimal field?
thanx
答案1
得分: 1
将值125.00
插入到PostgreSQL的numeric
类型列中的最简单方法是在Go中使用float类型。这可以直接使用,因此无需实现任何自定义接口。
例如:
CREATE TABLE t (
id serial PRIMARY KEY
, amount numeric(15,4) NOT NULL
-- ...
);
data := []byte(`{"amount": 125.00}`)
var obj struct {
Amount float64 `json:"amount"`
}
if err := json.Unmarshal(data, &obj); err != nil {
panic(err)
}
_, err := db.Exec(`INSERT INTO t (amount) VALUES ($1)`, obj.Amount)
然而,浮点类型存在舍入误差的问题,因此存储货币金额的常见做法是使用表示以分为单位的整数。例如,125.00
变为12500
。这种方法也可以直接使用。
例如:
CREATE TABLE t (
id serial PRIMARY KEY
, amount int8 NOT NULL
-- ...
);
data := []byte(`{"amount": 12500}`)
var obj struct {
Amount int64 `json:"amount"`
}
if err := json.Unmarshal(data, &obj); err != nil {
panic(err)
}
_, err := db.Exec(`INSERT INTO t (amount) VALUES ($1)`, obj.Amount)
如果你想要使用pgtype.Numeric
来存储和检索数据库中的金额,则需要进行一些额外的工作,因为pgtype.Numeric
不知道如何编码/解码JSON中的125.00
/"125.00"
值。
你可以声明一个自定义的结构类型,将其嵌入pgtype.Numeric
类型,然后让自定义的结构类型实现json.Marshaler
和json.Unmarshaler
接口。
例如:
CREATE TABLE t (
id serial PRIMARY KEY
, amount numeric(15,4) NOT NULL
-- ...
);
type MyNumeric struct {
pgtype.Numeric
}
func (n *MyNumeric) UnmarshalJSON(data []byte) error {
var s json.Number
if err := json.Unmarshal(data, &s); err != nil {
return err
}
return n.Numeric.Set(s.String())
}
func (n MyNumeric) MarshalJSON() ([]byte, error) {
var f float64
if err := n.Numeric.AssignTo(&f); err != nil {
return nil, err
}
return []byte(strconv.FormatFloat(f, 'f', -1, 64)), nil
}
data := []byte(`{"amount": 125.00}`)
var obj struct {
Amount MyNumeric `json:"amount"`
}
if err := json.Unmarshal(data, &obj); err != nil {
panic(err)
}
_, err := db.Exec(`INSERT INTO t (amount) VALUES ($1)`, obj.Amount)
英文:
The easiest way to insert a value like 125.00
into a postgres column of type numeric
would be to use a float type in Go. This works out of the box and so there's no need to implement any kind of custom interface.
For example:
CREATE TABLE t (
id serial PRIMARY KEY
, amount numeric(15,4) NOT NULL
-- ...
);
data := []byte(`{"amount": 125.00}`)
var obj struct {
Amount float64 `json:"amount"`
}
if err := json.Unmarshal(data, &obj); err != nil {
panic(err)
}
_, err := db.Exec(`INSERT INTO t (amount) VALUES ($1)`, obj.Amount)
Float types are subject to rounding errors however, and because of that a common practice for storing monetary amounts is to use integers that represent the value in cents. E.g. 125.00
becomes 12500
. This also works out of the box.
For example:
CREATE TABLE t (
id serial PRIMARY KEY
, amount int8 NOT NULL
-- ...
);
data := []byte(`{"amount": 12500}`)
var obj struct {
Amount int64 `json:"amount"`
}
if err := json.Unmarshal(data, &obj); err != nil {
panic(err)
}
_, err := db.Exec(`INSERT INTO t (amount) VALUES ($1)`, obj.Amount)
If you want to use the pgtype.Numeric
for storing and retrieving the amount to and from the database then you'll have to do some extra work because pgtype.Numeric
doesn't know how to encode/decode JSON 125.00
/"125.00"
values.
One thing you can do is to declare a custom struct type, have it embed the pgtype.Numeric
type, and then have the custom struct type implement the json.Marshaler
and json.Unmarshaler
interfaces.
For example:
CREATE TABLE t (
id serial PRIMARY KEY
, amount numeric(15,4) NOT NULL
-- ...
);
type MyNumeric struct {
pgtype.Numeric
}
func (n *MyNumeric) UnmarshalJSON(data []byte) error {
var s json.Number
if err := json.Unmarshal(data, &s); err != nil {
return err
}
return n.Numeric.Set(s.String())
}
func (n MyNumeric) MarshalJSON() ([]byte, error) {
var f float64
if err := n.Numeric.AssignTo(&f); err != nil {
return nil, err
}
return []byte(strconv.FormatFloat(f, 'f', -1, 64)), nil
}
data := []byte(`{"amount": 125.00}`)
var obj struct {
Amount MyNumeric `json:"amount"`
}
if err := json.Unmarshal(data, &obj); err != nil {
panic(err)
}
_, err := db.Exec(`INSERT INTO t (amount) VALUES ($1)`, obj.Amount)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论