【问题标题】:Google Sheet Script server error on getNamedRanges()getNamedRanges() 上的 Google 表格脚本服务器错误
【发布时间】:2018-01-17 23:55:38
【问题描述】:

在为 Google 表格编写 Google Apps 脚本以删除已通过删除它们引用的目标而成为孤立的命名范围时,发生了意外的“服务器错误”。谁能告诉我如何避免它?

编辑:请注意,除了解决附加的演示脚本之外,我每次调用例程 SpreadsheetApp.getNamedRanges() 时都会寻求通用解决方案。我将部署一个命令来从任何 Google 电子表格中删除孤立的范围名称。它将成为 dlbTools 中 NamedRanges 例程的一部分,该例程已在 Google Add-ons Store https://chrome.google.com/webstore/detail/dlbtools./ochdleihpppeoboanknpbmabbdjfihjj 中提供。

我编写了一个 Google 电子表格来演示它,并生成以下消息: - “很抱歉,发生服务器错误。请稍等,然后重试。(第 8 行,文件“代码”)”。 此电子表格可通过https://docs.google.com/spreadsheets/d/1IKwGb9guw5Ud1q2YJj3Ao1yjjIvMGv35Pcpe_lyq48I/edit?usp=sharing公开获取

发生错误:

  • 当方法 SpreadsheetApp.getNamedRanges() 被调用时。
  • 仅在使用全行样式引用删除已在命名范围中引用的行之后。
  • 仅当命名范围引用类似于“1:1”而不是“A1:B1”时。
  • 仅在删除引用的行导致命名范围引用“#REF!”之后。

使用 Google 电子表格演示此意外服务器错误的步骤如下:

  • 选择菜单项工具 > 脚本编辑器...
  • 切换到“查找孤儿脚本”选项卡
  • 选择菜单项 Run > findOrphans
  • 请注意红色条,“很抱歉,出现服务器错误。请稍候,然后重试。(第 8 行,文件“代码”)”
  • 选择菜单项查看 > 日志
  • 选择菜单项查看 > 执行记录

这是演示服务器错误的测试脚本: 函数 findOrphans() {

// Helper function logs Named Range names and references.
  var logNamedRanges = function (scenario) {
    Logger.log('Logging named ranges for scenario: ' + scenario);
    Logger.log('In logNamedRanges() before call to getNamedRanges()');
    var namedRanges = ss.getNamedRanges();
    Logger.log('In logNamedRanges() after call to getNamedRanges()');
    var names = namedRanges.map(function (nr){return nr.getName() + ' --> ' + nr.getRange().getA1Notation();});
    Logger.log('\n' + names.join('\n'));
  } 

// Helper function alters Named Ranges to full row references.
// That is, "A1:B1" becomes "1:1".
  var changeRangeReferencesToFullRow = function () {
    Logger.log('In changeRangeReferencesToFullRow() before call to getNamedRanges()');
    var namedRanges = shtTest.getNamedRanges();
    Logger.log('In changeRangeReferencesToFullRow() after call to getNamedRanges()');
    for (var i = 0; i < namedRanges.length; i++) {
      var n = namedRanges[i].getRange().getA1Notation();
      var num = n.charAt(1);
      var fullRowA1Notation = num + ':' + num;
      var fullRowNameRange = shtTest.getRange(fullRowA1Notation);
      namedRanges[i].setRange(fullRowNameRange);
    }
    Logger.log('Successfully changed Named Ranges to full row references');
  }

// Helper function creates test sheet by copying sheet Static and
// creates Named Ranges for each of the first two rows.
  var setupTestSheet = function () {
    if (ss.getSheetByName('Copy of Static') != null) {
      // Test sheet already exists, so delete it.
      ss.deleteSheet(ss.getSheetByName('Copy of Static'));
      Logger.log('Deleted existing copy of test sheet');
    }
    // Activate sheet Static, duplicate it, and grab reference to it.
    shtStatic.activate();
    ss.duplicateActiveSheet();
    shtTest = ss.getSheetByName('Copy of Static');
    // Create or update Named Nanges pointing to the first two rows.
    ss.setNamedRange('row1Name', shtTest.getRange('1:1'));
    ss.setNamedRange('row2Name', shtTest.getRange('2:2'));
    Logger.log('Successfully set up test sheet');
  }

// Helper function runs test sequence.
function doTest (whichPass) {
  Logger.log('\n\nStarting test using ' + whichPass + '\n\n');
  setupTestSheet();
  if (whichPass.slice(0,3) == '1:1') {
  // next line is only difference between the passes
    changeRangeReferencesToFullRow();
  }
  logNamedRanges('Before row delete')
  shtTest.deleteRow(1);
  if (whichPass.slice(-5) == 'error') {
  // next line stops test before error occurs
    Logger.log('Halted test to avoid error');
    return;
  }
  logNamedRanges('After row delete')
  Logger.log('Finished test using ' + whichPass );
}

// Create variables that will be available to helper functions.
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var shtStatic = ss.getSheetByName('Static');
  var shtTest;

// Execute test sequence three times.
  doTest('A1:B1 style references');
  doTest('1:1 style references with stop before server error');
  doTest('1:1 style references without stopping');
}

使用 Sheets API 进行测试

@Tanaike 建议使用 Sheets API 会有所帮助,因为它成功地检索了 SpreadsheetAPP 调用失败的命名范围列表。不幸的是,它不包括最感兴趣的人——孤儿。

https://docs.google.com/spreadsheets/d/1IKwGb9guw5Ud1q2YJj3Ao1yjjIvMGv35Pcpe_lyq48I/edit?usp=sharing 的演示电子表格现在包括一个名为 trySheetsAPI.gs 的附加脚本文件。顶部的 cmets 说明如何识别测试 Sheets API 的附加代码。虽然我很欣赏这个有用且有趣的建议,但它仍然不允许我编写通用的孤儿删除例程。

【问题讨论】:

  • 我想我自己回答了这个问题。我找到了 Google 问题跟踪系统。它包含以下项目:issuetracker.google.com/36764977 调用 getNamedRanges() 总是导致“我们很抱歉,发生服务器错误。请稍等,然后重试。”

标签: google-apps-script google-sheets server-error


【解决方案1】:

这个考虑怎么样?我不知道这是否是最好的。对不起。

修改点:

发现在更新namedrange时,出现错误。您的脚本流程如下。

setupTestSheet() 运行时,命名范围由ss.setNamedRange('row1Name', shtTest.getRange('1:1'))ss.setNamedRange('row2Name', shtTest.getRange('2:2')) 定义,如下所示。以下值是使用 Sheet API 检索到的。

{
  "namedRanges": [
    {
      "namedRangeId": "#####",
      "name": "row1Name",
      "range": {
        "endColumnIndex": 26,
        "startRowIndex": 0,
        "endRowIndex": 1,
        "sheetId": #####,
        "startColumnIndex": 0
      }
    },
    {
      "namedRangeId": "#####",
      "name": "row2Name",
      "range": {
        "endColumnIndex": 26,
        "startRowIndex": 1,
        "endRowIndex": 2,
        "sheetId": #####,
        "startColumnIndex": 0
      }
    }
  ]
}

此时,各个命名范围在电子表格上定义如下。这可以使用您的浏览器在电子表格中看到。

row1Name 'Copy of Static'!A1:Z1
row2Name 'Copy of Static'!A2:Z2

这里,当shtTest.deleteRow(1)运行时,上面的namedranges变化如下。

row1Name #REF
row2Name 'Copy of Static'!A2:Z2

在这种情况下,var namedRanges = shtTest.getNamedRanges()不会发生错误

setupTestSheet()ss.setNamedRange('row1Name', shtTest.getRange('1:1'));ss.setNamedRange('row2Name', shtTest.getRange('2:2')); 都可以正常工作。

但是,changeRangeReferencesToFullRow() 不能完全工作。当changeRangeReferencesToFullRow() 运行时,命名范围如下。发现列数据丢失。

{
  "namedRanges": [
    {
      "namedRangeId": "#####",
      "name": "row1Name",
      "range": {
        "startRowIndex": 0,
        "endRowIndex": 1,
        "sheetId": #####
      }
    },
    {
      "namedRangeId": "#####",
      "name": "row2Name",
      "range": {
        "startRowIndex": 1,
        "endRowIndex": 2,
        "sheetId": #####
      }
    }
  ]
}

此时,Spreadsheet 上分别定义了如下的命名范围。

row1Name 'Copy of Static'!1:1
row2Name 'Copy of Static'!2:2

在这种情况下,var namedRanges = shtTest.getNamedRanges()不会发生错误

这里,当shtTest.deleteRow(1)运行时,上面的namedranges变成如下。

row1Name #REF
row2Name 'Copy of Static'!2:2

在这种情况下,错误发生var namedRanges = shtTest.getNamedRanges()。所以当使用shtTest.deleteRow(1) 时,row1Name #REF 必须被删除或更新。

这些表示1:12:2 这样的范围可能不适合SpreadsheetApp 的命名范围。

解决方法:

上述考虑的解决方法如下。

  1. deleteRow() 删除行后,使用ss.setNamedRange('row1Name', shtTest.getRange('1:1'))ss.setNamedRange('row2Name', shtTest.getRange('2:2')) 重新定义行的命名范围。
  2. 使用以下修改后的脚本删除由deleteRow() 删除的行的命名范围。只修改doTest() 如下脚本。

脚本:

function doTest (whichPass) {
  Logger.log('\n\nStarting test using ' + whichPass + '\n\n');
  setupTestSheet();

  var r = shtTest.getNamedRanges(); // <--- Added

  if (whichPass.slice(0,3) == '1:1') {
  // next line is only difference between the passes
    changeRangeReferencesToFullRow();
  }
  logNamedRanges('Before row delete')

  var dummy = [i.remove() for each (i in r) if (i.getName() == "row1Name")]; // <--- Added

  shtTest.deleteRow(1);
  if (whichPass.slice(-5) == 'error') {
  // next line stops test before error occurs
    Logger.log('Halted test to avoid error');
    return;
  }
  logNamedRanges('After row delete')
  Logger.log('Finished test using ' + whichPass );
}

在我的环境中,上面的脚本可以正常工作。如果这些在您的环境中不起作用。随时告诉我。

其他问题

Sheet API 怎么用?

要从任何 Google 电子表格中删除孤立的范围名称,您可以使用 namedRangeIdnamedRangeId 是每个命名范围的单独 ID。通过使用它,可以删除孤立的范围名称。

为了使用namedRangeId,它使用了Sheet API。所以请为 Advanced Google Services 和 Google API Console 启用 Sheet API。

Google 表格 API v4 https://developers.google.com/sheets/api/

高级谷歌服务https://developers.google.com/apps-script/guides/services/advanced

示例脚本:

var ss = SpreadsheetApp.getActiveSpreadsheet();
var r = Sheets.Spreadsheets.get(ss.getId(), {fields: "namedRanges(name,namedRangeId)"});
Sheets.Spreadsheets.batchUpdate(
  {"requests": [{
      "deleteNamedRange": {
        "namedRangeId": [i.namedRangeId for each (i in r.namedRanges) if (i.name == "### name ###")][0]
  }}]},
  ss.getId()
);

流程如下。

  1. 使用Sheets.Spreadsheets.get(),检索名称和namedrangeID。
  2. 与名称匹配的 namedrangeID 被移除。

在使用Sheet API的情况下,如果范围数据丢失,虽然getNamedRanges()出现错误,但Sheet API可以检索到namedrange。

如果我误解了您的附加问题,我很抱歉。

【讨论】:

  • 感谢您的透彻分析和清晰的解释,Tanaike。我特别感谢在您的“var dummy =”行中学习我以前从未见过的编码技术。虽然您的解决方法解决了演示脚本,但它仍然留下了通用解决方案的更广泛问题。由于我之前没有提到这一点,因此我编辑了问题以添加它。也许您也可以为此提供帮助。如果是这样,我会更快乐。再次感谢您。
  • @Dave Bezaire 我刚刚更新了我的答案。请确认。
  • 感谢@Tanaike 的其他建议。同样,它们内容丰富且乐于助人。不幸的是,他们仍然没有帮助我找到我在对您的消息进行编辑中描述的孤儿。再次感谢!
  • @Dave Bezaire 请将您的附加信息添加到您自己的问题中。我找不到你说的trySheetsAPI.gx。所以我不能尝试运行你的脚本。我查看了您的电子表格示例,但无法打开它。在哪里可以找到您的示例脚本?
  • 你删除了我的cmets?你有没有在某个地方保存它们的副本?也许缓存在某个地方?
猜你喜欢
  • 1970-01-01
  • 2015-01-21
  • 2015-05-19
  • 1970-01-01
  • 1970-01-01
  • 2014-12-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多