将对象的插值数组转换为复合类型的 postgresql 数组的正确方法是什么?

What is the proper way of converting interpolated array of objects to a postgresql array of composite type?

提问人:Biller Builder 提问时间:10/20/2022 更新时间:11/16/2022 访问量:153

问:

我正在使用 SQL 函数来执行多次插入,但由于它们不能接受记录集作为参数,因此我必须先将它们转换为数组。它适用于基元数组,因为它们可以简单地用它来转换和完成。
但是,多插入查询需要复合类型数组,并且看起来不适用于它们,因为它需要单列输入。
所有查询都显示在此小提琴上:https://dbfiddle.uk/w_Qbq-lw
CAST (${value} as primitive_type[])CAST()

表和类型

CREATE TABLE accounts (
  id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  created_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP,
  login text NOT NULL,
  password text NOT NULL,
  email text
);

CREATE TYPE account_init AS (
  login text,
  password text,
  email text
);

功能

CREATE FUNCTION get_accounts(
  pagination_limit bigint DEFAULT 25,
  pagination_offset bigint DEFAULT 0,
  account_ids bigint[] DEFAULT NULL
)
RETURNS TABLE (
  id bigint,
  created_at timestamptz,
  login text,
  password text,
  email text
)
LANGUAGE SQL
AS $BODY$
  WITH input_accounts AS (
    SELECT
      id,
      created_at,
      login,
      password,
      email
    FROM
      accounts
    WHERE
      account_ids IS NULL OR id = ANY (account_ids)
    ORDER BY
      id
    LIMIT pagination_limit
    OFFSET pagination_offset
  )
  SELECT
    id,
    created_at,
    login,
    password,
    email
  FROM
    input_accounts
  ORDER BY
    id
$BODY$;

CREATE FUNCTION create_accounts(
  account_inits account_init[]
)
RETURNS TABLE (
  id bigint,
  created_at timestamptz,
  login text,
  password text,
  email text
)
LANGUAGE SQL
AS $BODY$
  WITH new_accounts AS (
    INSERT INTO accounts ( 
      login, 
      password, 
      email 
    )
    SELECT 
      login, 
      password, 
      email
    FROM 
      unnest(account_inits)
    RETURNING
      id
  )
  SELECT
    id,
    created_at,
    login,
    password,
    email
  FROM
    get_accounts(
      NULL,
      NULL,
      ARRAY(
        SELECT
          id
        FROM
          new_accounts
      )
    )
  ORDER BY
    id
$BODY$;

初始化数据

const account_inits = [
  {
    login:"EC4A42323F", 
    password: "3DF1542F23A29B73281EEC5EBB55FFE18C253A7E800E7A541B"
  },
  {
    login:"1D771C1E52", 
    password: "2817029563CC722FBC3D53F9F29F0000898F9843518D882E4A", 
    email: "a@b"
  },
  {
    login:"FB66381D3A", 
    password: "C8F865AC1D54CFFA56DEBDEEB671C8EF110991BBB3B9EE57D2", 
    email: null
  }
]

用法

--- insert data
WITH input_inits AS (
  SELECT
    login,
    password,
    email
  FROM
    json_to_recordset(${account_inits:json}) AS input_init(
      login text,
      password text,
      email text
    )
),
input_data AS (
  SELECT
    array_agg(
      CAST (
        (
          login,
          password,
          email
        ) AS account_init
      )
    ) AS account_inits
  FROM
    input_inits
)
SELECT
  new_accounts.id,
  new_accounts.created_at,
  new_accounts.login,
  new_accounts.password,
  new_accounts.email
FROM
  input_data
  CROSS JOIN
  create_accounts(input_data.account_inits) AS new_accounts
ORDER BY
  new_accounts.id ASC
;

目前,我将其插值为 CTE 中的记录集,然后将其转换为 CTE 中的记录集,然后在第二个 CTE 中将其转换为复合类型数组,以作为参数传递给函数。将对象数组传递给函数参数似乎非常繁重。我尝试在不转换的情况下工作,但遇到了相关错误或语法错误。:json:jsonarray[]malformed object literal

数组 postgresql pg-promise postgresql-13 复合类型

评论

0赞 vitaly-t 10/22/2022
你的问题没有得到任何回应,也许是因为它太宽泛了,即缺乏对你所遇到的实际问题的关注。
0赞 Biller Builder 10/23/2022
实际上它非常具体,这就是为什么没有回应。问题在于将对象数组转换为postgresql复合类型数组,这是插值的结果(或任何在不将其转换为json的情况下转换对象数组的结果)。该例程有效,但它非常冗长,并且当对象的键本身可以是对象数组时,它不会很好地缩放。pg-promisepg-promiseinterpolate as json -> turn into a set of records -> aggregate into array
0赞 vitaly-t 10/24/2022
涉及的部分 - 在生成多行插入时,但我看到您甚至没有使用这些插入,因此很可能在我可以提供帮助的地方(作为作者)。pg-promisepg-promise
0赞 Biller Builder 10/25/2022
是的,我没有使用多重插入,因为它们需要在应用程序代码和 SQL 之间来回摆弄和来回摆弄。更不用说语法在插入与字符串连接/原始插值传递的内容上更加清晰。并且不能在存储过程中使用。尽管如此,当插值对象数组未值为时,什么是 postgresql 类型?pg-promiseINSERT INTO ... ( ... ) SELECT ... FROM ... RETURNING ...:json
0赞 vitaly-t 10/25/2022
摆弄太多了?它们是插入内存中数据的最有效方法。至于最后一件事,元组是除了 json 之外唯一的插值数据类型。

答:

0赞 Biller Builder 11/16/2022 #1

转而没有。“适当的”替代方案需要知道输入数组中对象的预期键(以及所有嵌套对象/对象数组),以便构造正确的元组数组。
这基本上需要某种运行时引用模式,即使完美实现,仍然需要为每个新的查询文件编写样板代码。充其量,它不会比模块中的函数或查询中的强制转换更好。
helpersjson -> record set -> composite type array