以编程方式从 Google SpreadSheet 中删除空单元格以避免 5000000 个单元格限制错误

Deleting Empty Cells from Google SpreadSheet Programatically to avoid 5000000 cell limit error

我用 Scala 语言编写了小批量作业,我首先从数据库中提取数据并将其转储到 google 电子表格中。

最初这项工作工作得很好,但突然开始失败并出现以下错误:

 ERR com.google.api.client.googleapis.json.GoogleJsonResponseException: 400 Bad Request
 ERR   "code" : 400,
 ERR   "errors" : [ {
 ERR     "domain" : "global",
 ERR     "reason" : "badRequest"
 ERR {
 ERR   } ],
 ERR   "message" : "This action would increase the number of cells in the workbook above the limit of 5000000 cells.",
 ERR    at com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest.newExceptionOnError(AbstractGoogleJsonClientRequest.java:113)
 ERR    at 

令人惊讶的是,我插入了一个包含 1800 条记录的列表,每条记录有 20 列。因此,在我的案例中,单元格总数为 36000,但与错误中提到的 50,00,000 个单元格的限制相去甚远。

我还没有找到任何具体的解决方案,但我从电子表格中删除了几个空列,批处理作业开始正常工作。因此,这些空行和空列可能是限制溢出的原因。

我已经在插入数据之前清理了整个电子表格。但我认为,我需要删除空单元格而不是清理它。

请找到以下代码片段来检查我当前的实现。请注意,在这里,我使用了范围值“A2:Z”,因为第一行是 header,我不想删除它。

def  clearAndInsertRowsToSpreadSheet(itemsToAppend : util.List[util.List[AnyRef]],
                              range : String,
                              spreadSheetId : String){

    clearSpreadSheet(range,spreadSheetId)

    insertRowsToSpreadSheet(itemsToAppend,range,spreadSheetId)

  }

  private def insertRowsToSpreadSheet(itemsToAppend : util.List[util.List[AnyRef]], range : String,
                                      spreadSheetId : String) ={

    val data : ValueRange = new ValueRange()
    data.setValues(itemsToAppend)

    val appendRequest = googleSpreadSheetService.getSheets().spreadsheets()
      .values().append(spreadSheetId, range,data)

    appendRequest.setValueInputOption("USER_ENTERED")
    appendRequest.setInsertDataOption("INSERT_ROWS")

    val appendValuesResponse = appendRequest.execute()

    appendValuesResponse.getUpdates

  }
  private def clearSpreadSheet(range : String, spreadSheetId : String) : String = {

    val clearDataRequest = googleSpreadSheetService.
        getSheets().spreadsheets().
        values().clear(spreadSheetId,range, new ClearValuesRequest())

    val clearResponse = clearDataRequest.execute()

    clearResponse.getClearedRange
  }

但是,我如何编写代码来删除所有在转储数据之前为空的行和列。我尝试使用 BatchClearValuesByDataFilterRequest 但它没有用。我正在寻找一种机制,我可以在其中提供条件来检查具有空单元格的行和列。

任何建议表示赞赏!

  • 问题的根本原因

经过几轮测试和观察,我发现问题出在我使用“append”函数的方式上。在我的代码中,我首先执行电子表格清理,然后使用 Append 方法附加新数据。

这里需要注意两点

  1. clear函数只是清除单元格的值,不会删除单元格,也不会释放内存。所以那些被清除的单元格仍然已经被占用。

  2. Google API 追加函数实际上是在新数据前加上现有的行(单元格),而不是使用现有的空单元格。

为了更清楚,假设我们有 100 行 12 列。因此总共占用了 1200 个单元格。在执行问题陈述中提到的代码时,清理函数将清理这 1200 个单元格,但不会将其删除。因此,Google API 认为这些单元格仍处于占用状态。现在,在使用 50 objects 的列表调用追加函数时,追加方法将通过将现有的 100 行移动到下行来添加 50 行新行。所以现在,执行我的代码后,占用的单元格总数将为 1800。

Total Number of Old cells = 100 ROWs * 12 Columns  = 1200
Total Number of New cells = 50 ROWs * 12 Columns   = 600
---------------------------------------------------------
Total Number of cell after job execution = 1800

因此,考虑到上述示例,频繁执行上述代码将导致我们的电子表格达到其最大允许单元格限制 50,00,000 错误。

  • 解决方案:

我们可以通过两种方式解决上面的问题

  1. 删除未使用的单元格,而不是使用 BatchUpdateDeleteDimensionRequest.

    [= 清除它们71=]
  2. 使用 Update 方法而不是 Append 方法,后者将使用现有单元格插入数据。但请注意,您需要在使用 Update 方法之前清除电子表格。

我建议使用解决方案#2 更新方法,因为 UpdateResponse 提供了更好的深度静态。它提供更新的行数、列数和单元格总数,可用于跟踪目的。

以下是上述两种解决方案的代码片段:

  • 解决方案#1. 使用 BatchUpdate 方法删除行和 删除维度请求 :

这里,我们需要注意一点。我们不能删除电子表格的所有行。所以我们需要在电子表格中保持静态 header。在使用 DeleteDimentionRequest 时,统计索引从零开始。

val dimensionRange : DimensionRange = new DimensionRange()
 dimensionRange.setDimension("ROWS")
 dimensionRange.setStartIndex(1)
 //dimensionRange.setEndIndex(1001)

 val deleteDimensionRequest : DeleteDimensionRequest = new DeleteDimensionRequest()
 deleteDimensionRequest.setRange(dimensionRange)

 val request : Request = new Request()
 request.setDeleteDimension(deleteDimensionRequest)

 val requests : util.List[Request] = new util.ArrayList[Request]()
 requests.add(request)

 val batchUpdateRequest : BatchUpdateSpreadsheetRequest =new  BatchUpdateSpreadsheetRequest()
 batchUpdateRequest.setRequests(requests)

 val batchUpdate: Sheets#Spreadsheets#BatchUpdate =  googleSpreadSheetService.getSheets()
    .spreadsheets()
    .batchUpdate(spreadSheetId, batchUpdateRequest)

 val batchUpdateResponse = batchUpdate.execute()
  • Solution#2.清理单元格并调用update方法插入 数据:

    //代码

    private def clearAndInsertRowsToSpreadSheet(itemsToAppend : util.List[util.List[AnyRef]], range : String, spreadSheetId : String) :  UpdateValuesResponse = {
    
            clearSpreadSheet(range, spreadSheetId)
            updateSpreadSheetData(itemsToAppend,range, spreadSheetId)
    
          }
    
          private def updateSpreadSheetData(itemsToAppend : util.List[util.List[AnyRef]], range : String, spreadSheetId : String) ={
    
            val data : ValueRange = new ValueRange()
            data.setValues(itemsToAppend)
    
            val updateRequest: Sheets#Spreadsheets#Values#Update = googleSpreadSheetService.getSheets()
              .spreadsheets()
              .values()
              .update(spreadSheetId, range, data)
    
            updateRequest.setValueInputOption("USER_ENTERED")
            val updateValuesResponse: UpdateValuesResponse = updateRequest.execute()
            updateValuesResponse
          }
          private def clearSpreadSheet(range : String, spreadSheetId : String) : String = {
    
            val clearDataRequest = googleSpreadSheetService.
              getSheets().spreadsheets().
              values().clear(spreadSheetId,range, new ClearValuesRequest())
    
            val clearResponse = clearDataRequest.execute()
    
            clearResponse.getClearedRange
          }
    

希望以上答案足以理解问题的性质及其可能的解决方案。