是否可以使用 Google 表格在主电子表格与其部分副本之间应用单向同步?

问题描述 投票:0回答:1

我非常抱歉我不得不问这个问题,主要是因为搜索谷歌总是给我指出错误的方向。此外,这本身不是一个编程问题。事实上,我想要一个涉及最少代码的答案。

图 1 显示了问题的初始阶段:

initial phase

我拥有的是一个主电子表格(大约 5000 行),从中生成了许多部分副本,每个副本针对不同的销售员,以便他只能看到与他相关的行(例如,在图中,电子表格是分成两半。)

如何生成副本与我无关——导演的工作是手动或以其他方式完成。重要的是,每一行都有一个唯一的键(第一列),并且保证在所有电子表格中保留该键。所有电子表格的格式和布局也保持相同。每个电子表格都是一个不同的文件,所有这些文件都应作为 Google 表格在线存储。主电子表格会定期更新(同样,通过与我无关的外部过程),当发生这种情况时,主管开始准备部分副本。一旦准备好,它就会分享给销售人员。

图 2 显示了销售人员开始编辑部分电子表格时发生的情况。

enter image description here

例如,销售员 1 已将键 1 行中的“值 2”更改为“值 22”,销售员 2 已将键 4 行中的“值 12”更改为“值 19”。编辑值时,主电子表格应进行更新以反映更改。更新应该自动或半自动进行。另外,如果更新的单元格能够突出显示(例如使用颜色),这样导演就很容易看到发生了什么变化,那就太好了。

有没有什么机制可以用来实现这个工作流程?如果涉及一些脚本也没关系,只要它对电子表格的用户是透明的。我尝试过使用 Google Apps 脚本,但它更像是一个笑话,而不是一个解决方案:它速度慢且不可靠。无论如何,这是更新主电子表格中单元格的脚本:

// ID of the master spreadsheet
var masterSheetId = 'MASTER_SHEET_ID';

// The name of the tab in each shared sheet
var tabName = 'Sheet1'; 

// Function to sync updates from partial to master
function syncPartial1() {
  syncRowsById(masterSheetId, tabName);
}

// Sync specific rows based on Column 1 (ID) and color the updated cells
function syncRowsById(masterSheetId, sheetName) {
  var partialSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName); 

  // Get data from the partial sheet
  var partialData = partialSheet.getDataRange().getValues();
  var partialIds = partialData.map(row => row[0]); // Get all values from Column 1 (ID)
  
  // Get data from the master sheet
  var masterSpreadsheet = SpreadsheetApp.openById(masterSheetId);
  var masterSheet = masterSpreadsheet.getSheetByName(sheetName);
  var masterData = masterSheet.getDataRange().getValues(); // Get all data from master
  var masterIds = masterData.map(row => row[0]); // Get all values from Column 1 (ID)

  // Loop through partial data
  for (var i = 0; i < partialData.length; i++) {
    var partialRow = partialData[i];
    var partialId = partialIds[0]; // ID from Column 1

    // Check if the ID exists in the master sheet
    var rowIndexInMaster = masterIds.indexOf(partialId);

    if (rowIndexInMaster > -1) {
      // Get the current row in the master sheet
      var masterRow = masterData[rowIndexInMaster];
      
      // Update the corresponding row in the master sheet and color changed cells
      for (var j = 0; j < partialRow.length; j++) {
        if (partialRow[j] !== masterRow[j]) { // If the cell value is different
          // Update the cell
          masterSheet.getRange(rowIndexInMaster + 1, j + 1).setValue(partialRow[j]);
          
          // Color the updated cell (e.g., light yellow)
          masterSheet.getRange(rowIndexInMaster + 1, j + 1).setBackground('#FFFF99');
        }
      }
    }
  }
}

我试图避免的是必须从那些电子表格(每个参与的人都熟悉)转移到数据库+ asp.net core Web应用程序解决方案(这将花费大量的时间和精力来构建。另外,没有网格组件匹配据我所知,“excel”电子表格的灵活性和功能。)

有什么想法吗?

google-sheets synchronization spreadsheet
1个回答
0
投票

您有许多从主文件(“主文件”)创建的电子表格(“部分文件”)。当在任何部分文件中进行更改时,您希望更改和适当的背景颜色反映在主文件中。

考虑这个答案:

  • 在测试中(记录数量较少),主文件会在有效的部分编辑后一秒内更新。
  • 必须将脚本复制到每个部分电子表格的项目文件中。
  • 该脚本由可安装的
    onEdit
    触发器在每个部分电子表格中触发
  • 触发器必须由OP或总监或任何有权访问“Master”的人安装。这样脚本在运行时就可以访问主服务器。
    • 根据我的测试,触发器必须手动安装;理论上可以从另一个电子表格管理/安装触发器,但似乎有一个错误使得这变得不可能。
  • 由于销售人员是编辑,他们可以访问项目文件和脚本。
    • 可以选择使用库或中间文件来存储脚本,这样销售人员就无法查看脚本的详细信息。 OP 应考虑这是否可取并研究替代方案。 StackOverflow 上有很多主题涵盖这些问题。

逻辑

处理很简单,没有什么奇怪的,并且使用了事件对象

  • 测试以确认编辑是在“适当的”工作表名称、行和列上进行的
  • 获取编辑的Row上的key;如果没有键,则编辑将被忽略。
  • 背景颜色在数组中定义。
  • 已编辑单元格的背景颜色已更新。
  • indexOf 用于查找“Master”上的键的匹配项。
    • 如果找不到匹配项,OP 可以发送消息或采取其他适当的操作。
  • “母版”上的相关单元格将使用编辑的值进行更新,并设置背景颜色。

其他注意事项:

  • 如果导演更新了“局部”,则会触发脚本,并且“局部”和“主”的背景颜色都会更新。

function updateMaster(e) {

  // Logger.log(JSON.stringify(e)) // DEBUG

  // get Event Objects
  var editedValue=e.value
  var editedSheet = e.range.getSheet().getName()
  var editedRow = e.range.rowStart
  var editedCol = e.range.columnStart
  var spreadsheetName = e.source.getName()

  // get variables
  var sheet2Watch = "Partial"

  // Logger.log("DEBUG: edited value = "+editedValue+", edited sheet = "+editedSheet+", edited row = "+editedRow+", edited Column = "+editedCol+", spreadsheet Name = "+spreadsheetName)
   if (editedSheet != sheet2Watch){
    // no match, do nothing
    // Logger.log("DEBUG: no match on sheet do nothing")
    return
  }
  else if ( editedRow < 2){
    // no match, do nothing
    // Logger.log("DEBUG: no match on row, do nothing")
    return
  }
  else if (editedCol !=2 && editedCol !=3){
    // no match, do nothing
    // Logger.log("DEBUG: no match on column, do nothing")
    return
  }

  // Logger.log("DEBUG: valid edit")
    
  // test for key in that row
  var partial = e.source.getSheetByName(sheet2Watch)
  var key = partial.getRange(editedRow,1).getValue()
  // Logger.log("DEBUG: the key range = "+partial.getRange(editedRow,1).getA1Notation()+", and key = "+key+", key length = "+key.length)
  if (key.length !=0 ){
    // key exists
    // Logger.log("DEBUG: There's a key in this row")
  }
  else{
    // no key in this row, do nothing
    // Logger.log("DEBUG: no key in this row, do nothing")
    return
  }
  // get the colour to update
  var colors = ["#FAD437","#7CF926"]
  var updateColor = colors[editedCol-2]
  // update color of the edited cell
  // Logger.log("DEBUG: the edited cell = "+partial.getRange(editedRow,editedCol).getA1Notation())
  partial.getRange(editedRow,editedCol).setBackground(updateColor)

  // get the master data
  var masterId = "<insert master ID>"
  var masterSpreadsheet = SpreadsheetApp.openById(masterId)
  var masterSheet = masterSpreadsheet.getSheetByName("Master")
  // get master data
  var masterData = masterSheet.getDataRange().getValues()
  // remove the headers
  masterData.shift()
  // get column A (keys)
  var masterKeys = masterData.map(function(e){return e[0]})
  // Logger.log(masterKeys) // DEBUG
  // find the index of the partial key on the master
  var masterIndex = masterKeys.indexOf(key)
  if (masterIndex == -1){
    // if result = -1 then key not found
    // send message to someone.
    return
  }
  // Logger.log("DEBUG: key index on master = "+masterIndex)

  // get the range to update
  var masterUpdateRange = masterSheet.getRange((+masterIndex+2),editedCol)
  // Logger.log("DEBUG: Master range to update = "+masterUpdateRange.getA1Notation())

  // update the value and color in the Master  
  masterUpdateRange.setValue(editedValue).setBackground(updateColor)
}

之前 - 大师

master_before

之前 - 部分

partial_before


之后 - 大师

master_after

之后 - 部分

partial_after



© www.soinside.com 2019 - 2024. All rights reserved.