首页 文章

getNamedRanges()上的Google Sheet Script服务器错误

提问于
浏览
3

在为Google表格编写Google Apps脚本以删除已删除其引用的目标时已成为孤立的命名范围时,会发生意外的“服务器错误” . 有人可以告诉我如何避免它吗?

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

我写了一个演示它的Google电子表格,产生了这样的信息: - "We're sorry, a server error occurred. Please wait a bit and try again. (line 8, file " Code ")" . 此电子表格可在https://docs.google.com/spreadsheets/d/1IKwGb9guw5Ud1q2YJj3Ao1yjjIvMGv35Pcpe_lyq48I/edit?usp=sharing公开发布

发生错误:

  • 调用方法SpreadsheetApp.getNamedRanges()时 .

  • 仅在使用完整行样式引用删除已在命名范围中引用的行之后 .

  • 仅当命名范围引用类似于"1:1"而不是"A1:B1"时 .

  • 仅在删除引用的行后导致命名范围引用"#REF!" .

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

  • 选择菜单项工具>脚本编辑器...

  • 切换到标签"Find Orphans Scripts"

  • 选择菜单项Run> findOrphans

  • 观察红栏说,"We're sorry, a server error occurred. Please wait a bit and try again. (line 8, file "代码")"

  • 选择菜单项视图>日志

  • 选择菜单项视图>执行记录

以下是演示服务器错误的测试脚本:function 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 的附加脚本文件 . 顶部的注释告诉我们如何识别测试Sheets API的其他代码 . 虽然我很欣赏这个有用且有趣的建议,但它仍然不允许我编写一个通用的孤儿删除例程 .

1 回答

  • 3

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

    修改点:

    发现当更新named范围时,会发生错误 . 脚本流程如下 .

    运行 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) 时,以上命名范围会发生如下变化 .

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

    在这种情况下, no error occursvar 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": #####
          }
        }
      ]
    }
    

    此时,各个命名范围在电子表格上定义如下 .

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

    在这种情况下, no error occursvar namedRanges = shtTest.getNamedRanges() .

    这里,当运行 shtTest.deleteRow(1) 时,上面的命名范围变为如下 .

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

    在这种情况下, the error occursvar namedRanges = shtTest.getNamedRanges() . 因此,当使用 shtTest.deleteRow(1) 时,必须删除或更新 row1Name #REF .

    这些表示 1:12:2 之类的范围可能不适合 SpreadsheetApp 的命名范围 .

    解决方法:

    上述反映的解决方法如下 .

    • deleteRow() 删除行后,使用 ss.setNamedRange('row1Name', shtTest.getRange('1:1'))ss.setNamedRange('row2Name', shtTest.getRange('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电子表格中删除孤立范围名称,您可以使用 namedRangeId . namedRangeId 是每个命名范围的个人ID . 通过使用它,可以删除孤立的范围名称 .

    为了使用 namedRangeId ,它使用Sheet API . 因此,请为高级Google服务和Google API控制台启用Sheet API .

    Google表格API v4 https://developers.google.com/sheets/api/高级Google服务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()
    );
    

    流程如下 .

    • 使用 Sheets.Spreadsheets.get() ,检索名称和namedrangeID .

    • 删除与名称匹配的namedrangeID .

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

    如果我误解了你的其他问题,我很抱歉 .

相关问题