Postgres `WITH ins AS ...` 将所有内容转换为文本
Postgres `WITH ins AS ...` casting everything as text
我在 postgres
中有这个 table
CREATE TABLE target (
a json
b integer
c text []
id integer
CONSTRAINT id_fkey FOREIGN KEY (id)
REFERENCES public.other_table(id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE NO ACTION,
)
我想使用
从 psycopg2 插入数据
import psycopg2
import psycopg2.extras as extras
# data is of the form dict, integer, list(string), string <- used to get fkey id
data = [[extras.Json([{'a':1,'b':2}, {'d':3,'e':2}]), 1, ['hello', 'world'], 'ident1'],
[extras.Json([{'a':4,'b':3}, {'d':1,'e':9}]), 5, ['hello2', 'world2'], 'ident2']]
# convert data to list of tuples containing objects
x = [tuple(u) for u in data]
# insert data to the database
query = ('WITH ins (a, b, c, ident) AS '
'(VALUES %s) '
'INSERT INTO target (a, b, c, id) '
'SELECT '
'ins.a '
'ins.b '
'ins.c '
'other_table.id'
'FROM '
'ins '
'LEFT JOIN other_table ON ins.ident = other_table.ident;')
cursor = conn.cursor()
extras.execute_values(cursor, query, data)
当我 运行 这是我得到的错误:column "a" is of type json but expression is of type text
我试图通过在 SELECT 语句中添加类型转换来解决这个问题
'SELECT '
'ins.a::json '
'ins.b '
'ins.c '
'other_table.id'
但是我收到错误 column "c" is of type text[] but expression is of type text
所以我用同样的方法解决了这个问题:
'SELECT '
'ins.a::json '
'ins.b '
'ins.c::text[]'
'other_table.id'
所以现在我收到错误 column "b" is of type integer but expression is of type text
这个例子有些简化,因为我在原始查询中有更多的列。
WITH ins ...
语句是否总是将所有内容都转换为文本?这对我来说似乎是一种奇怪的行为
- 有没有一种无需手动对每一列进行类型转换的编码方式?这似乎不雅且计算效率低下的数据被转换为例如。从 python int 到 postgres 文本到 postgres 整数。
问题不在于 CTE
,而在于如何将值传递到 VALUES
子句。在 CTE
at VALUES
中创建的所有值都以某种方式作为文本发送(也许查询是用单引号之间的所有值创建的?)。以下示例使用纯 SQL 语句重现您的查询,并且它按预期工作:
WITH ins (a, b, c, id) AS (
VALUES ('{"answer":42}'::json,42,array['foo','bar'],1)
)
INSERT INTO target (a,b,c,id)
SELECT ins.a,ins.b,ins.c,other_table.id
FROM ins
LEFT JOIN other_table ON ins.id = other_table.id;
请注意,我将 json 的值转换为 CTE
中的值,而不是 SELECT
子句中的值。因此,如果来源是正确的,那么 postgres 将无法在您不告知的情况下将其转换为文本 ;)
演示:db<>fiddle
我在 postgres
中有这个 tableCREATE TABLE target (
a json
b integer
c text []
id integer
CONSTRAINT id_fkey FOREIGN KEY (id)
REFERENCES public.other_table(id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE NO ACTION,
)
我想使用
从 psycopg2 插入数据import psycopg2
import psycopg2.extras as extras
# data is of the form dict, integer, list(string), string <- used to get fkey id
data = [[extras.Json([{'a':1,'b':2}, {'d':3,'e':2}]), 1, ['hello', 'world'], 'ident1'],
[extras.Json([{'a':4,'b':3}, {'d':1,'e':9}]), 5, ['hello2', 'world2'], 'ident2']]
# convert data to list of tuples containing objects
x = [tuple(u) for u in data]
# insert data to the database
query = ('WITH ins (a, b, c, ident) AS '
'(VALUES %s) '
'INSERT INTO target (a, b, c, id) '
'SELECT '
'ins.a '
'ins.b '
'ins.c '
'other_table.id'
'FROM '
'ins '
'LEFT JOIN other_table ON ins.ident = other_table.ident;')
cursor = conn.cursor()
extras.execute_values(cursor, query, data)
当我 运行 这是我得到的错误:column "a" is of type json but expression is of type text
我试图通过在 SELECT 语句中添加类型转换来解决这个问题
'SELECT '
'ins.a::json '
'ins.b '
'ins.c '
'other_table.id'
但是我收到错误 column "c" is of type text[] but expression is of type text
所以我用同样的方法解决了这个问题:
'SELECT '
'ins.a::json '
'ins.b '
'ins.c::text[]'
'other_table.id'
所以现在我收到错误 column "b" is of type integer but expression is of type text
这个例子有些简化,因为我在原始查询中有更多的列。
WITH ins ...
语句是否总是将所有内容都转换为文本?这对我来说似乎是一种奇怪的行为- 有没有一种无需手动对每一列进行类型转换的编码方式?这似乎不雅且计算效率低下的数据被转换为例如。从 python int 到 postgres 文本到 postgres 整数。
问题不在于 CTE
,而在于如何将值传递到 VALUES
子句。在 CTE
at VALUES
中创建的所有值都以某种方式作为文本发送(也许查询是用单引号之间的所有值创建的?)。以下示例使用纯 SQL 语句重现您的查询,并且它按预期工作:
WITH ins (a, b, c, id) AS (
VALUES ('{"answer":42}'::json,42,array['foo','bar'],1)
)
INSERT INTO target (a,b,c,id)
SELECT ins.a,ins.b,ins.c,other_table.id
FROM ins
LEFT JOIN other_table ON ins.id = other_table.id;
请注意,我将 json 的值转换为 CTE
中的值,而不是 SELECT
子句中的值。因此,如果来源是正确的,那么 postgres 将无法在您不告知的情况下将其转换为文本 ;)
演示:db<>fiddle