getNamedRanges()

时间:2017-08-09 22:44:50

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

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

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

我写了一个演示它的Google电子表格,产生了这样的信息:   - "我们很抱歉,发生了服务器错误。请稍等一下再试一次。 (第8行,文件"代码")"。 此电子表格可在https://chrome.google.com/webstore/detail/dlbtools./ochdleihpppeoboanknpbmabbdjfihjj

公开发布

发生错误:

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

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

  • 选择菜单项工具>脚本编辑器......
  • 切换到标签"查找孤儿脚本"
  • 选择菜单项“运行”> findOrphans
  • 观察红栏说,"我们很抱歉,发生了服务器错误。请稍等一下再试一次。 (第8行,文件"代码")"
  • 选择菜单项视图>日志
  • 选择菜单项视图>执行记录

以下是演示服务器错误的测试脚本:     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');
}

使用表格API

进行测试

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

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

1 个答案:

答案 0 :(得分: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

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

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

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

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

此处,当shtTest.deleteRow(1)运行时,以上命名范围变为如下。

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()以下脚本。
  3. 脚本:

    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。因此,请为高级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()
    );
    

    流程如下。

    1. 使用Sheets.Spreadsheets.get(),检索名称和namedrangeID。
    2. 将删除与该名称匹配的namedrangeID。
    3. 在使用Sheet API的情况下,如果范围数据丢失,虽然错误发生getNamedRanges(),但Sheet API可以检索命名范围。

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