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 的子查询中选择包含值数组的列?
在 PostgreSQL 中的 jsonb 列中搜索 json 数组,其中数据为 json 数组