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

这个例子有些简化,因为我在原始查询中有更多的列。

  1. WITH ins ... 语句是否总是将所有内容都转换为文本?这对我来说似乎是一种奇怪的行为
  2. 有没有一种无需手动对每一列进行类型转换的编码方式?这似乎不雅且计算效率低下的数据被转换为例如。从 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