【问题标题】:Update or create new row based on Unique ID Google Sheets根据唯一 ID Google 表格更新或创建新行
【发布时间】:2021-04-25 07:16:09
【问题描述】:

我找到了一些接近我想要做的选项,但没有一个完全匹配它。


请求很简单。

“表 A” - 主表(有 1 个标题行)

“表 B” - 输入表(有 1 个标题行)

“C 列” - 唯一 ID(两个工作表上的同一列)


触发器

  • 工作表 B 已编辑

操作

  • 脚本在 C 列的工作表 B 中查找唯一 ID,并在 C 列的工作表 A 中查找它。
  • 如果找到,则工作表 A 上的相应行替换为工作表 B 中的相应行
  • 如果找不到,则在工作表 A 的底部添加一个新行,并将工作表 B 中的整个相应行作为新记录添加到工作表底部的新行中表 A。
  • 工作表 B 上的整行已删除。

重复操作,直到工作表 B 从第 2 行开始没有填充行(即不包括第 1 行标题)。

谢谢

编辑

  1. 为了澄清我为什么要这样做。我有一个表单正在提交并将数据发送到 Google 表格(Cognito -> Zapier -> Google 表格)。此表单的一部分涉及重复部分(行项目)。导入响应的当前方法在正确添加新响应方面没有问题,但是当更新响应时,它无法正确查找/更新重复部分的现有行。所以我打算使用表 A 作为我的主表,然后使用表 B 作为接收表。这样我就可以将每个条目(包括更新的条目)作为工作表 B 上的“新”条目提交,然后让我的脚本进行更新。
  2. 每次提交或更新新表单条目时,都会自动编辑表 B。 “编辑”基本上是添加一个新行并将数据填充到该行中。向触发器添加一个 1 分钟计时器可能是个好主意,这样如果要添加大量数据,它就会有时间让这种情况发生。
  3. 我什至与脚本专家相差甚远。我只是浏览其他人制作的不同脚本并尝试将它们组合起来以使它们为我需要的工作。我找到了将移动一行然后删除它的脚本,但它不检查要更新的匹配值。我找到了其他检查唯一值并复制的脚本,但它们不会删除另一张表上的原始行。我试图将它们结合起来,但由于我没有基础知识,我似乎无法让它发挥作用。

【问题讨论】:

  • 可以使用 onChange 或 onEdit。可能 onEdit 更容易,因为在事件对象中有更多关于您当前位置的信息。
  • 我无法理解你的目标。我为此道歉。那我能问一下你的目标吗? 1.关于Sheet B is edited,你会怎么编辑?例如,包括几行的值被复制和粘贴? 2. 关于The entire respective row on Sheet B is Deleted.,在这种情况下,Sheet B 总是只有一个标题行?在这种情况下,这些值被放入表 B? 3. 可以问一下你目前剧本的问题吗?
  • 当你提到工作表 B 和工作表 A 有共同的 C 列时,我的第一个想法是“你为什么不插入新记录”但事实证明你正在处理工作表 B - C 列像队列一样。你能确认一下吗?到目前为止,您尝试过什么?
  • 感谢您的提问。我将在我的问题中进行编辑来回答他们,因为此回复中的 500 个字符是不够的。
  • 如果我们在您的工作表 B 和工作表 A 中拥有您所拥有的数据类型,这将对社区有所帮助。您能否分享一个没有敏感数据的类似电子表格?

标签: google-apps-script google-sheets copy-paste lookup unique-id


【解决方案1】:

作为一种解决方法,我会使用 onEdit 简单触发器和 O(n) 搜索

这是我的方法:

function onEdit(e) {
  // If it's not the Sheet B it won't make changes
  if (e.range.getSheet().getName() !== "Sheet B") {
    return;
  }

  var range = e.range;
  var numberRow = range.getA1Notation().slice(1);
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheetA = ss.getSheetByName("Sheet A");
  var sheetB = ss.getSheetByName("Sheet B");

  var currentRowB = sheetB.getRange(`A${numberRow}:D${numberRow}`);
  var id = currentRowB.getValues()[0][2];
  // There's to be 4 values in the row (no empty values)
  if(currentRowB.getValues()[0].filter(value => value !== '').length === 4) {

    // Get all the values in Sheet A
    var rows = sheetA.getDataRange().getValues();
    for (row=1; row < rows.length; row++) {
      // If column C matches the ID replace the row
      if(rows[row][2] === id) {
        var currentRowA = sheetA.getRange(`A${row+1}:D${row+1}`);
        currentRowA.setValues(currentRowB.getValues());
        currentRowB.deleteCells(SpreadsheetApp.Dimension.COLUMNS);
        return;
      }
    }

    // If the ID doesn't match then insert a new row
    var newRow = sheetA.getRange(`A${rows.length+1}:D${rows.length+1}`);
    newRow.setValues(currentRowB.getValues());
    currentRowB.deleteCells(SpreadsheetApp.Dimension.COLUMNS);
  }
}

符合您提出的要求:

  • 脚本在 C 列的工作表 B 中找到唯一 ID,并在 C 列的工作表 A 中查找它。(第 19-28 行)
  • 如果找到,则工作表 A 上的整个相应行将替换为工作表 B 中的整个相应行。(第 22-24 行)
  • 如果找不到,则会在工作表 A 的底部添加一个新行,并将工作表 B 中的整个相应行作为新记录添加到工作表 A 底部的新行中。(第 31-33 行)
  • 工作表 B 上的整个相应行已删除。 (第 22 和 33 行)

我以这种 Sheet 格式为例:

两张表格的格式相同。请记住,此脚本会在替换之前检查是否存在有效行(在此特定情况下,4 列组成一行)。

作为另一种方法(处理空白数据)

总而言之,此脚本应每 X 分钟或您希望的时间运行一次

我编辑了代码,以便将 Z1 单元格用作阻止单元格和基于时间的触发器:

触发器:

代码

function processCells() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheetA = ss.getSheetByName("Sheet A");
  var sheetB = ss.getSheetByName("Sheet B");

  // If it's not the Sheet B or if there's a process running it won't make changes
  if (sheetB.getName() !== "Sheet B" || sheetB.getRange("Z1") === "Running") {
    return;
  }

  // Use the Z1 cell in order to block or unblock this sheet
  sheetB.getRange("Z1").setValue('Running');

  // Process all the rows
  var numCells = sheetB.getDataRange().getValues().length + 1;
  for (numberRow = 2; numberRow <= numCells; numberRow++) {

    var currentRowB = sheetB.getRange(`A${numberRow}:D${numberRow}`);
    var id = currentRowB.getValues()[0][2];

    // Get all the values in Sheet A
    var rows = sheetA.getDataRange().getValues();
    var match = false;
    for (row=1; row < rows.length; row++) {
      // If column C matches the ID replace the row
      if(rows[row][2] === id) {
        var currentRowA = sheetA.getRange(`A${row+1}:D${row+1}`);
        currentRowA.setValues(currentRowB.getValues());
        currentRowB.deleteCells(SpreadsheetApp.Dimension.COLUMNS);
        match = true;
        break;
      }
    }

    if(!match) {
      // If the ID doesn't match then insert a new row
      var newRow = sheetA.getRange(`A${rows.length+1}:D${rows.length+1}`);
      newRow.setValues(currentRowB.getValues());
      currentRowB.deleteCells(SpreadsheetApp.Dimension.COLUMNS);
    }
  }

  sheetB.getRange("Z1").setValue('');
}

请注意,每次脚本运行时,它都会检查是否有另一个使用 Z1 处理行。

参考文献

【讨论】:

  • 嘿@Jose,我做了一个快速测试,它抛出错误“TypeError:无法读取未定义的属性'范围'(onEdit @ Code.gs:3)”。我确保我的工作表名称完全是“工作表 A”和“工作表 B”,我在代码中唯一更改的是从 4 列改为 6,而不是在第 16 行。在此注释中:“必须有 4 个值行(没有空值)”。这是否意味着我不能有空白数据点?因此,在您的屏幕截图中,如果第 3 行在“B”列中没有任何内容,那么此代码会失败吗?
  • 您好,这就是为什么我要求使用工作表作为示例,因为代码依赖于 INPUT 数据的方式。 4 个值只是一个示例,它可以有 5 列或更多列。关于 cannot read property 错误可能是由于您尝试从编辑器运行代码而不是编辑单元格(请记住,这是一个简单的触发代码)。如果您想在输入数据后手动运行此脚本,请告诉我,以便我修改脚本。正如您提到的,您的输入可以有空白数据,如果您必须手动运行此代码(通过 API)会更好。
  • 1/2 通常,一旦我有了可以正常运行的代码,我就可以调整得足够好,让它与我的工作表格式一起工作。没有抱怨必须调整列数,只是提到它以防我不小心弄坏了一些东西。我已经用编辑进行了测试,但它没有工作,但我又做了一次,触发器通过了。我仍在测试一件事,看看为什么它不起作用。如果我将一整行复制/粘贴到工作表 B 中,则该函数将失败,说明再次没有“onEdit”事件。是因为我在复制/粘贴,还是因为一次输入了整行?
  • 2/2 - 我还测试了“空白数据”场景,但它不起作用。它仍然需要以相同的方式工作,我只是强调不会总是填充整行。
  • 尚未进行非常详细的测试,但到目前为止,它似乎在所有基本要求(新/重复/空白单元格)上都表现出色。非常感谢!!!
猜你喜欢
  • 2018-07-28
  • 1970-01-01
  • 2014-10-05
  • 2020-10-14
  • 1970-01-01
  • 2020-09-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多