如何使用迭代器和异步等待查询 google cloud spanner table T1,同时为 T1 中的每条记录查询第二个 table T2

How to use iterators and Async await to query a google cloud spanner table T1 while querying a second table T2 for each record in T1

我在 Google 云扳手数据库中有两个表 - 作者和书籍。

const 请求 = { 模式:[ CREATE TABLE Authors ( AuthorId INT64 NOT NULL, FirstName STRING(1024), LastName STRING(1024), AuthorInfo BYTES(MAX) ) PRIMARY KEY (AuthorId), CREATE TABLE Books ( AuthorId INT64 NOT NULL, BookId INT64 NOT NULL, BookTitle STRING(MAX) ) PRIMARY KEY (AuthorId, BookId), INTERLEAVE IN PARENT Authors ON DELETE CASCADE, ], };

export async function findAuthorBooks(authorId) {
  // [START spanner_find_author_books]
  const database = instance.database(databaseId);
  const query = {
    sql: "SELECT * FROM Books As t WHERE t.AuthorId = @authorId",
    params: { authorId },
    types: { authorId: "string" },
  };

  const results = await database.run(query);
  const rows = results[0];
  const result = [];
  rows.forEach((row) => {
    const json = row.toJSON();
    result.push(json);
  });
  database.close();
  if (Array.isArray(result)) return result;
  throw new Error("err");
  // [END spanner_find_author_books]
}

现在我想查询所有作者,对于每个作者,我查询他的所有书籍并将其像对象一样添加。函数 findAuthor1() 工作得很好。

export async function findAuthors1() {
  // [START spanner_find_authors]
  const database = instance.database(databaseId);
  const results = await database.run({ sql: "SELECT * FROM Authors" });
  const rows = results[0];
  const result = [];
  for (const row of rows) {
    const json = row.toJSON();
    const id = json.vendorId;
    json.notification = await findAuthorBooks(id);
    result.push(json);
  }
  database.close();
  if (Array.isArray(result)) return result;
  throw new Error("err");
  // [END spanner_find_authors]
}

但这里的问题是,iterators/generators 需要 regenerator-runtime,这对于本指南来说太重量级了,无法允许它们。另外,应该避免循环以支持数组迭代(根据 Eslint)。 因此,我决定使用 Promise.all,如下面的函数 findAuthors2() 所示:

export async function findAuthors2() {
  // [START spanner_find_authors]
  const database = instance.database(databaseId);
  const results = await database.run({ sql: "SELECT * FROM Authors" });
  const rows = results[0];
  const result = [];

  await Promise.all(rows.map(async (row) => {
    const json = row.toJSON();
    const id = json.vendorId;
    json.notification = await findAuthorBooks(id);
    result.push(json);
  }));
  database.close();
  if (Array.isArray(result)) return result;
  throw new Error("err");
  // [END spanner_find_authors]
}

不幸的是,它不起作用。那么我怎样才能使它工作或者是否有一种不同的(更好的)方式来实现它而不必使用 Promise.all? 要么 有没有办法编写一个子查询,将 select Books 作为结构数组添加到主查询 selecting Authors?

Or Is there a way to write a subquery that will select Books as Array of structs and add to the main query selecting Authors?

是- 使用子查询,其中 returns 一个结构数组: (请参阅 Spanner SQL 文档中的 Notes about Subqueries

例如:

SELECT 
  a.AuthorId, 
  a.FirstName, 
  a.LastName, 
  a.AuthorInfo,
  ARRAY(SELECT AS STRUCT
          b.BookID, b.BookTitle
        FROM
          Books b
        WHERE
          a.AuthorId = b.AuthorId) as Books
FROM
  Authors a;

您将必须解压缩作为代码中的 Books 列返回的结构数组...

还有一个更简单的方法:只需连接 2 个表并为每个 author/book 组合获取一行,并在 AuthorID 更改时在您的代码中检测

SELECT 
  a.AuthorId, 
  a.FirstName, 
  a.LastName, 
  a.AuthorInfo,
  b.BookID,
  b.BookTitle
FROM
  Authors a, Books b
WHERE
  a.AuthorID = b.BookID
ORDER BY
  a.AuthorID;