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

POSTGRESQL Insert Multiple Rows from given JSON array, use results from insert to do other inserts

TL;DR 音乐流媒体应用程序 - 允许用户上传自己的文件。 文件将存储在 FileSet 中。 我想传入多个 FILE(文件记录数组)和一个 FILESET,然后插入到适当的 tables.

抱歉,如果这有点令人困惑 - 基本上我正在使用 3 tables:

文件table

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)
)

文件集Table

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 Table

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)
)

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

下面的函数 (file_upload_process) 允许我将文件添加到 table(返回 file_id),以及将文件集添加到 table(返回 fileset_id),最后函数将这两个值插入到最终的 table 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 table 列中 - 最后允许我将多条记录添加到 file_fileset_rel table.

如果有人有更简单的方法来完成所有这些,或者更重要的是,解决多对一插入的问题将是一个巨大的帮助。

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

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}/文件

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

您可以从 file_upload_process.sql 中的 this PostgREST issue. You could also use a LOOP 改编一个优雅的解决方案,它包装了您所有的函数调用。

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

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