POSTGRESQL 从给定的 JSON 数组中插入多行,使用插入的结果进行其他插入

Posted

技术标签:

【中文标题】POSTGRESQL 从给定的 JSON 数组中插入多行,使用插入的结果进行其他插入【英文标题】:POSTGRESQL Insert Multiple Rows from given JSON array, use results from insert to do other inserts 【发布时间】:2019-09-27 18:31:32 【问题描述】:

TL;DR 音乐流媒体应用程序 - 允许用户上传自己的文件。 文件将存储在 FileSet 中。 我想通过一个 FILESET 传入多个 FILE(fileRecords 数组),然后插入到相应的表中。

对不起,如果这有点令人困惑 - 基本上我正在使用 3 个表:

文件表

CREATE TABLE file
(
    -- Most tables should have a table_id & table_cuid
    file_id         BIGSERIAL   NOT NULL ,
    file_cuid       VARCHAR     NOT NULL,

    -- This section is specific to this table
    user_id         BIGINT      NOT NULL,

    -- File fields
    filename        VARCHAR     NOT NULL,
    last_modified   TIMESTAMP WITHOUT TIME ZONE NOT NULL,
    size_in_bytes   VARCHAR     NOT NULL,
    mime_type       VARCHAR     NOT NULL,

    -- Most tables should also have timestamps
    created_timestamp  TIMESTAMP WITHOUT TIME ZONE  NOT NULL,
    updated_timestamp  TIMESTAMP WITHOUT TIME ZONE  NOT NULL,

    -- Constraints
    PRIMARY KEY (file_id),
    FOREIGN KEY (user_id)   REFERENCES users (user_id),
    CONSTRAINT file_cuid_unique  UNIQUE     (file_cuid)
)

文件集表

CREATE TABLE fileset
(
    -- Most tables should have a table_id & table_cuid
    fileset_id         BIGSERIAL   NOT NULL ,
    fileset_cuid       VARCHAR     NOT NULL,

    user_id         BIGINT      NOT NULL,

    -- Fileset fields TBD

    created_timestamp  TIMESTAMP WITHOUT TIME ZONE  NOT NULL,
    updated_timestamp  TIMESTAMP WITHOUT TIME ZONE  NOT NULL,

    -- Constraints
    PRIMARY KEY (fileset_id),
    FOREIGN KEY (user_id)   REFERENCES users (user_id),
    CONSTRAINT cuid_unique_fileset  UNIQUE     (fileset_cuid)
)

最后是 File_FileSet_Rel 表

CREATE TABLE file_fileset_rel
(
    file_id         BIGSERIAL   NOT NULL,
    fileset_id      BIGSERIAL   NOT NULL,

    FOREIGN KEY (file_id)  REFERENCES file   (file_id),
    FOREIGN KEY (fileset_id)   REFERENCES fileset (fileset_id),
    UNIQUE      (file_id, fileset_id)
)

每首歌曲都会有一个与之关联的 FileSet,并且该文件集有一个/多个关联的文件。

下面的函数(file_upload_process)允许我将文件添加到表中(返回file_id),以及将文件集添加到表中(返回fileset_id) ,最后该函数将这两个值插入到最终表 file_fileset_rel

file_upload_process.sql

CREATE OR REPLACE FUNCTION file_upload_process(
  _file_cuid VARCHAR,
  _fileset_cuid VARCHAR,
  _cognito_id VARCHAR,
  _name VARCHAR,
  _last_modified TIMESTAMP,
  _size DECIMAL,
  _type VARCHAR
)
RETURNS TABLE(
  fileId BIGINT,
  fileSetID BIGINT
)
AS $file_upload_process$
DECLARE
v_user_id BIGINT;
v_file_id BIGINT;
v_fileset_id BIGINT;
BEGIN

-- Create Song/ Update song
  -- store user_id
  v_user_id := (
    SELECT * FROM get_user_id(_cognito_id)
  );

  -- --Insert In FILE table
  v_file_id := (
    SELECT *
    FROM create_file(
    _file_cuid,
    v_user_id,
    _name,
    _last_modified,
    _size,
    _type
    )
  );

  --Insert into fileset table
  v_fileset_id := (
  SELECT * FROM create_fileset(_fileset_cuid, v_user_id)
  );

  RETURN QUERY
  INSERT INTO file_fileset_rel(file_id, fileset_id)
  VALUES (
    v_file_id,
    v_fileset_id
  )
  RETURNING file_fileset_rel.file_id,
            file_fileset_rel.fileset_id;
END;
$file_upload_process$ LANGUAGE PLPGSQL;

其中调用以下函数:

create_file.sql

CREATE OR REPLACE FUNCTION create_file(
  _file_cuid VARCHAR,
  _user_id BIGINT,
  _name VARCHAR,
  _last_modified TIMESTAMP,
  _size DECIMAL,
  _type VARCHAR
)
RETURNS TABLE (
  _file_id BIGINT
)
AS $create_file$
BEGIN
  RETURN QUERY
-- Create Song/ Update song
  INSERT INTO file(
    file_cuid,
    user_id,
    filename,
    last_modified,
    size_in_bytes,
    mime_type,
    created_timestamp,
    updated_timestamp
  )
  VALUES(
    _file_cuid,
    _user_id,
    _name,
    _last_modified,
    _size,
    _type,
    now(),
    now()
  )
  RETURNING "file".file_id;
END;
$create_file$ LANGUAGE PLPGSQL;

还有 create_fileset 函数:

    CREATE OR REPLACE FUNCTION create_fileset(
  _fileset_cuid VARCHAR,
  _user_id BIGINT
)
RETURNS TABLE (
  _fileset_id BIGINT
)
AS $create_fileset$
BEGIN
-- Create Song/ Update song
  RETURN QUERY
  INSERT INTO fileset(
    fileset_cuid,
    user_id,
    created_timestamp,
    updated_timestamp
  )
  VALUES(
    _fileset_cuid,
    _user_id,
    now(),
    now()
  )
  RETURNING "fileset".fileset_id;
END;
$create_fileset$ LANGUAGE PLPGSQL;

我遇到的问题是,此功能一次只允许您添加 1 个文件,用户可以为 1 首歌曲(1 个文件集)上传 5-10 个文件,所以我需要一种方法来遍历数组fileRecords 并将这些字段的值存储在 FILE 表列中 - 最后允许我向 file_fileset_rel 表添加多条记录。

如果有人有更简单的方法来完成所有这些操作,或者更重要的是,解决 MANY vs One 插入将是一个巨大的帮助。

编辑: CURRENT 正文被发送到 lambda 的示例 - 在传递给 postgresql 函数之前 URL -> 文件集/filesetCuid/file

body: 
"cognitoId" : "9dc766a0-c5c9-4455-932e-46c243c80266",
"fileRecord" : 
    "fileCuid" : "someotherfilecuid23",
    "name" : "file_name",
    "last_modified" : "06/03/1995",
    "size" : 530,
    "type" : "mp3/audio"


最终目标 - 发送多个文件: URL -> 文件集/filesetCuid/file

body: 
"cognitoId" : "9dc766a0-c5c9-4455-932e-46c243c80266",
"fileRecords" : [
    "fileCuid" : "someotherfilecuid23",
    "name" : "file_name",
    "last_modified" : "06/03/1995",
    "size" : 530,
    "type" : "mp3/audio"
, ..., ...]

【问题讨论】:

为什么是mysql 标签?您是否需要针对这两种 DBMS 产品的解决方案? 我认为这可能不是特定于语言的问题,或者更确切地说,mysql 中的解决方案会阐明我如何在 postgresql 中做到这一点 看看你开始使用的 json 的结构会很有帮助。 道歉 - 编辑反映。 每首歌曲都会有一个与之关联的 FileSet,并且该文件集有一个/多个关联的文件。 不知道为什么你首先要有这个链接表,因为你显然不需要多对多关系。似乎您可以简单地使用将被声明为FOREIGN KEY (fileset_id) REFERENCES fileset (fileset_id)file.fileset_id。 (为什么不像往常一样简单地调用主键id?) 【参考方案1】:

您可以从this PostgREST issue 调整一个优雅的解决方案。您还可以在 file_upload_process.sql 中使用 LOOP 来包装您的所有函数调用。

前者更好,因为它是一个很好的一体化 upsert 逻辑:

它依赖于目标表的定义(例如,DEFAULT 值是即时设置的) 您可以直接传递 JSON 数组(即您可以省略不需要的值) 您可以处理 UNIQUE 约束的冲突(并自动检索现有行的 id!)

【讨论】:

以上是关于POSTGRESQL 从给定的 JSON 数组中插入多行,使用插入的结果进行其他插入的主要内容,如果未能解决你的问题,请参考以下文章

如何从PostgreSQL json中提取数组

如何从 PostgreSQL 的子查询中选择包含值数组的列?

用Python实现单链表的头插,尾插和中插

在 PostgreSQL 中的 jsonb 列中搜索 json 数组,其中数据为 json 数组

如何在 Postgresql 中获取数组的 JavaScript/JSON 数组?

PostgreSQL的NoSQL特性