首页 > 解决方案 > 如何提高创建 60 张纸的应用脚本的性能?

问题描述

我在 Google 表格中有一个使用该insertSheet方法的简单应用脚本。它循环一个数组,从而创建 60 个工作表。然而,该脚本非常缓慢- 需要几分钟才能完成。

如何提高此方法的性能?或者是否有另一种方法以更高效的方式创建 60 张工作表 - 也许使用工作表 REST api?

语境

我有一个主表,它就像一个模板。当对这张表进行更改时,需要复制 60 次,因此用户不必一遍又一遍地进行更改。

当前代码

var listOfSheetsToCreate = ['1', '2', '3', ... '60'];
var ss = SpreadsheetApp.getActive();
var templateSheet = ss.getSheetByName('templateSheet');

listOfSheetsToCreate.forEach(function(sheetName) {
    ss.insertSheet(sheetName, ss.getNumSheets(), {template: templateSheet});
});

标签: google-apps-scriptgoogle-sheetsgoogle-sheets-api

解决方案


在得到评论和反馈后,我做了一些实验:

  1. 使用 app-scripts 复制 60 张纸大约需要80 秒
  2. 使用 Sheets REST API 端点batchUpdate复制 60 张工作表大约需要10 秒- (删除 60 张工作表大约需要 1 秒)。使用请求体方法DuplicateSheetRequest
  3. 通过高级 Google 服务运行 batchUpdate。执行时间稍长 -大约 15 秒,但脚本要简单得多 - 感谢@Tanaike。这是我最终使用的解决方案 - 来解决我的问题。我可能还应该提到,运行脚本实际上会导致表格在浏览器中重新加载——我不知道为什么。但是当它重新加载时,所有的工作表都会被创建。

我仍然认为 10 秒很慢,所以如果您有任何改进此性能的想法,请告诉我。我提供了我的代码,以获取以下灵感。真的很期待一些现代的javascript...

表格 REST API Node.js 代码

export async function batchUpdate() {
    // moved authentication to another method.
    const authObect = await getAuthenticated();

    // The batchupdate endpoint needs the sheet id of the template sheet, so that's why we need the following code
    const ssInQuestion = await sheets.spreadsheets.get({
        auth: authObect,
        spreadsheetId: <spreadsheetId>
    });
    const masterSheet = ssInQuestion.data.sheets.find(v => v.properties.title.toLowerCase() === 'master');
    if (!masterSheet) {
        throw new Error(`Could not find master sheet in spreadsheet, will now exit!`);
    }

    // If we want to delete, the requests to the batch update should look like this:
    // const requests: sheets_v4.Schema$Request[] = ssInQuestion.data.sheets
    // .filter(v => v.properties.title.includes(' - test'))
    // .map(v => v.properties.sheetId)
    // .map(v => ({
    //     deleteSheet: {
    //         sheetId: v
    //     }
    // } as sheets_v4.Schema$Request));

    const requests: sheets_v4.Schema$Request[] = [];
    for (let i = 0; i <= 60; i++) {
        requests.push({
            duplicateSheet: {
                sourceSheetId: masterSheet.properties.sheetId,
                newSheetName: `${i} - test`
            }
        })
    }
    const batchUpdateOptions: sheets_v4.Params$Resource$Spreadsheets$Batchupdate = {
        spreadsheetId: <SpreadSheetIdInQuestion>,
        auth: authObect,
        requestBody: {
            includeSpreadsheetInResponse: false,
            requests
        }
    }
    let start = new Date()
    await sheets.spreadsheets.batchUpdate(batchUpdateOptions);
    let end = +new Date() - +start;
    console.info('Execution time: %dms', end)
}

高级 Google 服务应用脚本

// I ended up with something that looks like this:
function deleteListOfSheets(listOfSheets) {
  if (listOfSheets.length == 0) {
    return;
  }
  var ss = SpreadsheetApp.getActive();
  var deleteRequests = listOfSheets.map(function(v) {
    return {
      deleteSheet: {
         sheetId: v.getSheetId()
      }
    }
  });
  Sheets.Spreadsheets.batchUpdate({
            includeSpreadsheetInResponse: false,
             requests: deleteRequests
        }, ss.getId())
}

function createListOfSheets(listOfSheetNames, templateSheetId) {
  if (listOfSheetNames.length == 0) {
    Logger.log("List of sheets to create had 0 length, will now exit")
    return;
  }
  var ss = SpreadsheetApp.getActive();
  // Create the array of 'create requests'
  var createRequests = listOfSheetNames.map(function(v) {
    return {
      duplicateSheet: {
         sourceSheetId: templateSheetId,
         newSheetName: v
      }
    }
  });
  // Finally dispatch!
  Sheets.Spreadsheets.batchUpdate({
            includeSpreadsheetInResponse: false,
            requests: createRequests
        }, ss.getId())
}

推荐阅读