【问题标题】:Psycopg2 execute_values sending all values as textPsycopg2 execute_values 将所有值作为文本发送
【发布时间】:2021-06-01 16:37:39
【问题描述】:

我在 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, x)

当我运行它时,我得到了error: column "a" is of type json but expression is of type text。我试图通过在 SELECT 语句中添加类型转换来解决这个问题,但后来我得到了同样的错误 c 和 b。

最初我认为问题在于 WITH 语句,但根据我之前问题的答案,这似乎不是Postgres `WITH ins AS ...` casting everything as text

似乎execute_values 将所有值作为带有' ' 的文本发送。

主要问题:我怎样才能让execute_values 根据它们的 python 数据类型而不是文本来发送值?

子问题:
如何确认 execute_values 实际上是以带引号的文本形式发送值?
execute_values https://www.psycopg.org/docs/extras.html 的模板参数的目的是什么,有帮助吗?

【问题讨论】:

  • 这个:x = [tuple(u) for u in data] 没必要。 (VALUES %s) 将输入收集为文本,因为没有指定类型。您将需要 CAST 每个 SELECT 出现的正确类型。
  • @AdrianKlaver 你说没有指定类型,我该如何指定类型?

标签: postgresql psycopg2


【解决方案1】:

正如Adrian Klaver 在他们的comment 中指出的以及在this answer 中所看到的那样,问题在于CTE 中的输入丢失了。

我们可以通过psql shell 中的示例来展示这一点:

CREATE TABLE test (col1 json);

WITH cte (c) AS (VALUES ('{"a": 1}'))
INSERT INTO test (col) SELECT c FROM cte; 

导致

ERROR:  column "col" is of type json but expression is of type text

而这个版本,指定类型,成功:

WITH cte(c)  AS  (VALUES ('{"a": 1}'::json))
INSERT INTO test (col) SELECT c FROM cte;

我们可以在 execute_values 中通过在模板参数中提供输入信息来模仿这一点:

extras.execute_values(cursor, query, data, template='(%s::json, %s, %s, %s)')

【讨论】:

  • 感谢您的回答。使用模板时,执行值是先将数据转换为字符串,然后再转换为适当的类型,还是直接转换?我还需要为除字符串以外的所有类型指定数据类型,或者例如int 是自动的,如您的示例所示?此外,它是否相同,例如执行许多?
  • 我不太了解 psycopg2 的内部结构,但我可以告诉你,上面的语句对我来说成功了,使用你的 DDL 和数据(虽然我没有打扰 FK 部分)。所以似乎 Postgresql 需要被告知 JSON,但推断其他类型。
  • 我稍微修改了答案,以证明问题出在 Postgresql 中,而不是 psycopg2 中。
  • 您确定问题出在 postgres 方面吗?在您在回答中给出的第一个示例中,值 '{"a": 1}' 实际上是文本 - 它们不是 json,所以我预计会出现此错误。我们没有进行任何类型转换,也没有进行任何类型转换。但是,当使用 psycopg2 extras.Json 数据类型时,我希望不需要进行类型转换,但不知何故这会转换为文本。这也是出乎意料的,因为 extras_values 似乎可以很好地处理其他数据类型。
  • 是的 - INSERT INTO test (col) VALUES ('{"a": 1}') 插入时没有错误,所以问题一定是 Postgresql 是如何 inferring CTE 中 VALUES 子句中的类型。
猜你喜欢
  • 2021-03-03
  • 2019-02-19
  • 1970-01-01
  • 2021-06-05
  • 1970-01-01
  • 2021-08-09
  • 2021-09-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多