如何在 Supabase 中使用单个 API 调用插入多个表

Boj*_*kic 16 postgresql supabase supabase-database

我的表格的简化结构 - 2 个主表,1 个关系表。

这是我的表的简化结构 - 2 个主表,1 个关系表。
处理插入 API 的最佳方法是什么?如果我只有一个客户端和 Supabase:

- First API call to insert book and get ID
- Second API call to insert genre and get ID
- Third API call to insert book-genre relation
Run Code Online (Sandbox Code Playgroud)

这是我能想到的,但是3个API调用似乎是错误的。
有没有一种方法可以让我通过客户端的单个 API 调用插入到这 3 个表中,就像我可以调用的单个 postgres 函数一样?
请分享一个API的通用示例,谢谢!

Mar*_*raf 19

您是否需要通过一次调用来完成此操作?我从你的结构中假设你不会genre为你创建的每本书创建一个新的,所以大多数时候,你只是插入一条book记录和一条book_gen_rel记录。在现实世界中,您可能会拥有属于多种流派的书籍,因此最终您将更改您的函数以在一次调用中处理单本书和多种流派的插入。

话虽这么说,有两种方法可以解决这个问题。您可以从客户端进行多个 API 调用(这样做确实没有问题 - 这很常见)。其次,如果您创建一个 PostgreSQL 函数并使用.rpc().

仅使用客户端调用在每个表中插入一条记录的示例:

    const { data: genre_data, error: genre_error } = await supabase
      .from('genre')
      .insert([
        { name: 'Technology' }
      ]);
    const genre_id = genre_data[0].id;  
    const { data: book_data, error: book_error } = await supabase
      .from('book')
      .insert([
        { name: 'The Joys of PostgreSQL' }
      ]);
    const book_id = book_data[0].id;
    const { data: book_genre_rel_data, error: book_genre_rel_error } = await supabase
      .from('book_genre_rel_data')
      .insert([
        { book_id, genre_id }
      ]);
Run Code Online (Sandbox Code Playgroud)

以下是一次插入 3 个表的单个 SQL 语句:

WITH genre AS (
  insert into genre (name) values ('horror') returning id
),
book AS (
  insert into book (name) values ('my scary book') returning id
)
insert into book_genre_rel (genre_id, book_id) 
  select genre.id, book.id from genre, book
Run Code Online (Sandbox Code Playgroud)

现在这是一个 PostgreSQL 函数,可以在单个函数调用中完成所有操作:

CREATE OR REPLACE FUNCTION public.insert_book_and_genre(book_name text, genre_name text)
    RETURNS void language SQL AS
$$
  WITH genre AS (
    insert into genre (name) values (genre_name) returning id
  ),
  book AS (
    insert into book (name) values (book_name) returning id
  )
  insert into book_genre_rel (genre_id, book_id) 
    select genre.id, book.id from genre, book
$$
Run Code Online (Sandbox Code Playgroud)

这是一个测试它的例子:

select insert_book_and_genre('how to win friends by writing good sql', 'self-help')
Run Code Online (Sandbox Code Playgroud)

现在,如果您已经创建了该函数(在 Supabase 查询编辑器内),那么您可以从客户端调用它,如下所示:

const { data, error } = await supabase
  .rpc('insert_book_and_genre', {book_name: 'how I became a millionaire at age 3', genre_name: 'lifestyle'})
Run Code Online (Sandbox Code Playgroud)

再次强调,我不推荐这种方法,至少对于该genre部分不推荐。您应该首先插入您的流派(它们可能不会改变)并将其简化为仅插入一个book和一个book_genre_rel记录。

  • 我想补充一点,该解决方案假设这些插入不需要作为事务发生。如果您需要保证两次插入都必须以原子方式发生,则应该使用一个函数并使用客户端的“rpc”函数调用它。否则,您可能会面临客户端调用 API 之间出现故障并导致部分插入的风险(这可能会使您的数据处于意外的中间状态)。 (8认同)
  • 我的荣幸。就我个人而言,我使用的简单解决方案是第一个 - 只需根据需要进行多个客户端调用,然后您可以根据需要(非常快速)在客户端代码中进行调整。正如我所说,你最终会稍微改变一些事情。 (2认同)