如果此列的所有值为 NULL,则具有 NULL 值的数字类型的 Postgresql 更新列失败
Postgresql update column of numeric type with NULL value fails if all value of this column is NULL
我有一个这样的数据库table:
idx[PK]
a[numeric]
b[numeric]
1
1
1
2
2
2
3
3
3
4
4
4
...
...
...
在 pgadmin4 中,我尝试用一些空值更新此 table,我注意到以下查询失败:
UPDATE test as t SET
a = e.a,b = e.b
FROM (VALUES (1,NULL,NULL),(2,NULL,NULL),(3,NULL,NULL))
AS e(idx, a, b)
WHERE t.idx = e.idx
UPDATE test as t SET
a = e.a,b = e.b
FROM (VALUES (1,NULL,1),(2,NULL,2),(3,NULL,NULL))
AS e(idx, a, b)
WHERE t.idx = e.idx
报错信息是这样的:
ERROR: column "a" is of type numeric but expression is of type text
LINE 2: a = e.a,b = e.b
^
HINT: You will need to rewrite or cast the expression.
SQL state: 42804
Character: 43
但是,这会成功:
UPDATE test as t SET
a = e.a,b = e.b
FROM (VALUES (1,NULL,1),(2,2,NULL),(3,NULL,NULL))
AS e(idx, a, b)
WHERE t.idx = e.idx
似乎如果我正在更新的其中一列的新值都是 NULL
,则查询失败。但是,只要至少有一个值是数字而不是 NULL
,查询就会成功。这是为什么?
我确实在这里简化了我的真实案例,因为我的实际 table 有数百万行和超过 10 列。使用 Python 和 psycopg2,当我尝试在一个查询中更新 50,000 行时,即使列中的值不是 NULL
,之前的错误仍然会出现。我猜这是因为系统扫描一定数量的行来决定类型是否正确,而不是所有 50,000 行。
因此,如何在我的现实世界中避免这种失败?有没有更好的查询来代替 UPDATE
?
非常感谢!
更新
根据@Marth 和@Gordon Linoff 的评论,由于我使用的是 psycopg2,所以我在代码中执行了以下操作:
from psycopg2.extras import execute_values
sql = """UPDATE test as t SET
a = (e.a::numeric),
b = (e.b::numeric)
FROM (VALUES %s)
AS e(idx, a, b)
WHERE t.idx = e.idx"""
execute_values(cursor, sql, data)
cursor
来自数据库连接。 data
是我的值 (idx, a, b)
形式的元组列表。
这是由于 NULL
在这些情况下的工作方式的默认行为。 NULL
通常是未知类型,然后将其视为任何必要的类型。
在 values()
语句中,Postgres 尝试破译类型。它像处理 union
一样处理单个记录。但如果都是 NULL
。 . .好吧,那就没有信息了。 Postgres 决定使用 text
作为通用默认值。
同样重要的是要了解这失败并出现相同的错误:
UPDATE test t
SET a = ''
WHERE t.id = 1;
问题在于 Postgres 不会将空字符串转换为数字(与其他一些数据库不同)。
无论如何,这很容易通过将 NULL
转换为适当的类型来解决:
UPDATE test t
SET a = e.a,b = e.b
FROM (VALUES (1, NULL::numeric, NULL::numeric),
(2, NULL, NULL),
(3, NULL, NULL)
) e(idx, a, b)
WHERE t.idx = e.idx ;
您可以明确表示所有出现的 NULL
,但这不是必需的。
Here 是一个 db<>fiddle 说明了其中的一些。
我有一个这样的数据库table:
idx[PK] | a[numeric] | b[numeric] |
---|---|---|
1 | 1 | 1 |
2 | 2 | 2 |
3 | 3 | 3 |
4 | 4 | 4 |
... | ... | ... |
在 pgadmin4 中,我尝试用一些空值更新此 table,我注意到以下查询失败:
UPDATE test as t SET
a = e.a,b = e.b
FROM (VALUES (1,NULL,NULL),(2,NULL,NULL),(3,NULL,NULL))
AS e(idx, a, b)
WHERE t.idx = e.idx
UPDATE test as t SET
a = e.a,b = e.b
FROM (VALUES (1,NULL,1),(2,NULL,2),(3,NULL,NULL))
AS e(idx, a, b)
WHERE t.idx = e.idx
报错信息是这样的:
ERROR: column "a" is of type numeric but expression is of type text
LINE 2: a = e.a,b = e.b
^
HINT: You will need to rewrite or cast the expression.
SQL state: 42804
Character: 43
但是,这会成功:
UPDATE test as t SET
a = e.a,b = e.b
FROM (VALUES (1,NULL,1),(2,2,NULL),(3,NULL,NULL))
AS e(idx, a, b)
WHERE t.idx = e.idx
似乎如果我正在更新的其中一列的新值都是 NULL
,则查询失败。但是,只要至少有一个值是数字而不是 NULL
,查询就会成功。这是为什么?
我确实在这里简化了我的真实案例,因为我的实际 table 有数百万行和超过 10 列。使用 Python 和 psycopg2,当我尝试在一个查询中更新 50,000 行时,即使列中的值不是 NULL
,之前的错误仍然会出现。我猜这是因为系统扫描一定数量的行来决定类型是否正确,而不是所有 50,000 行。
因此,如何在我的现实世界中避免这种失败?有没有更好的查询来代替 UPDATE
?
非常感谢!
更新
根据@Marth 和@Gordon Linoff 的评论,由于我使用的是 psycopg2,所以我在代码中执行了以下操作:
from psycopg2.extras import execute_values
sql = """UPDATE test as t SET
a = (e.a::numeric),
b = (e.b::numeric)
FROM (VALUES %s)
AS e(idx, a, b)
WHERE t.idx = e.idx"""
execute_values(cursor, sql, data)
cursor
来自数据库连接。 data
是我的值 (idx, a, b)
形式的元组列表。
这是由于 NULL
在这些情况下的工作方式的默认行为。 NULL
通常是未知类型,然后将其视为任何必要的类型。
在 values()
语句中,Postgres 尝试破译类型。它像处理 union
一样处理单个记录。但如果都是 NULL
。 . .好吧,那就没有信息了。 Postgres 决定使用 text
作为通用默认值。
同样重要的是要了解这失败并出现相同的错误:
UPDATE test t
SET a = ''
WHERE t.id = 1;
问题在于 Postgres 不会将空字符串转换为数字(与其他一些数据库不同)。
无论如何,这很容易通过将 NULL
转换为适当的类型来解决:
UPDATE test t
SET a = e.a,b = e.b
FROM (VALUES (1, NULL::numeric, NULL::numeric),
(2, NULL, NULL),
(3, NULL, NULL)
) e(idx, a, b)
WHERE t.idx = e.idx ;
您可以明确表示所有出现的 NULL
,但这不是必需的。
Here 是一个 db<>fiddle 说明了其中的一些。