【问题标题】:Postgres `WITH ins AS ...` casting everything as textPostgres `WITH ins AS ...` 将所有内容转换为文本
【发布时间】:2021-08-19 18:02:27
【问题描述】:

我在 postgres 中有这张表

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 整数。

【问题讨论】:

标签: python sql postgresql psycopg2


【解决方案1】:

问题不在于CTE,而在于您如何将值传递给VALUES 子句。不知何故,在 VALUESCTE 内部创建的所有值都作为文本发送(也许查询是使用单引号之间的所有值创建的?)。以下示例使用纯 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&lt;&gt;fiddle

【讨论】:

  • 非常感谢您的回复,这已经很有帮助了!上面的 SQL 代码中有什么可以解释为什么 CTE 中的值作为文本发送吗?如果不是,那么我唯一能想象的就是execute_values
  • 老实说,我对 python 了解不多,所以我不知道它为什么会这样。但我敢打赌,这些值在单引号之间被发送到服务器,例如'1' 而不是 1,postgres 将看到 text
  • 好的,我会调查 python 端,但你没有在我的 sql 语句中看到问题,对吧?
  • @sev 不,我认为您的 sql 代码没有问题。在我的小提琴中,您可以看到我复制了您的环境,并且效果很好。尝试找出实际发送到服务器的内容,我的意思是 sql 的样子。
  • 我认为用这些信息重新表述这个问题是有意义的,所以我会将此标记为答案并打开一个新的。
猜你喜欢
  • 2022-01-04
  • 1970-01-01
  • 2018-08-26
  • 1970-01-01
  • 2011-02-18
  • 2013-09-18
  • 1970-01-01
  • 2021-12-31
相关资源
最近更新 更多