Google 应用程序脚本 360 秒超出时间限制解决方法
Posted
技术标签:
【中文标题】Google 应用程序脚本 360 秒超出时间限制解决方法【英文标题】:Google apps script 360 seconds exceeded time limit work around 【发布时间】:2017-03-23 18:23:21 【问题描述】:将 google 表格数据导入 mysql 表并尝试解决 360 秒执行限制的编码。我正在使用批处理执行,即使这也无助于导入具有更大数据的工作表。谷歌应用程序脚本中有没有一种方法可以在 350 秒或记录数(以先到者为准)时中断,并让下一次执行从中断的地方继续执行。
var address = 'database_IP_address';
var rootPwd = 'root_password';
var user = 'user_name';
var userPwd = 'user_password';
var db = 'database_name';
var root = 'root';
var instanceUrl = 'jdbc:mysql://' + address;
var dbUrl = instanceUrl + '/' + db;
function googleSheetsToMySQL()
var RecId;
var Code;
var ProductDescription;
var Price;
var dbconnection = Jdbc.getConnection(dbUrl, root, rootPwd);
var statement = dbconnection.createStatement();
var googlesheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('product');
var data = googlesheet.getDataRange().getValues();
dbconnection.setAutoCommit(false)
for (var i = 1; i < data.length; i++)
RecId = data[i][0];
Code = data[i][1];
ProductDescription = data[i][2];
Price = data[i][3];
var sql = "call [dbo].[sp_googlesheetstotable](?,?,?,?)";
statement = dbconnection.prepareCall(sql);
statement.setString(1, RecId);
statement.setString(2, Code);
statement.setString(3, ProductDescription);
statement.setString(4, Price);
statement.addBatch()
statement.executeBatch()
dbconnection.commit()
【问题讨论】:
【参考方案1】:在这种情况下,我要做的是创建一个空工作表,允许我存储数据库的起始 ID,然后在脚本上设置一个触发器,使其每 10 分钟运行一次。为此,只需单击 GAS 编辑器中播放按钮左侧的小时钟图标。
例如,假设我有一个包含 1000 条记录的数据库,并且每次触发器运行脚本时我想提升 100 条记录。我会创建一个工作表 - 比如说“Sheet1” - 最初将 0
放在单元格 A1 中。
在 GAS 中,我会让脚本在每次运行时获取 A1 的值:
var ss = SpreadsheetApp.openById("XXXXX");
var sheet = ss.getSheetByName("Sheet1");
var a1 = sheet.getRange("A1").getValue();
var a1 = parseInt(a1);
var last_row = a1+100;
那么您的 SQL 语句应该类似于以下查询:
"SELECT * FROM `table` WHERE `id` >= '"+a1+"' AND `id` < '"+last_row+"'"
一旦查询完成并将其结果输出到电子表格,您就可以将A1
更新为last_row
:
sheet.getRange("A1").setValue(last_row);
当脚本再次运行时(通过触发器),它将采用新值100
,添加100
(使其变为200
),查询表中大于或等于100的结果和小于 200,并相应显示。
【讨论】:
【参考方案2】:是的,只要问题是由循环过程引起的,这很容易实现。如果没有循环,则有点棘手,如果是单个操作,则不可能。我们很幸运,你的似乎是第一种。
本质上我们首先需要知道脚本什么时候开始,所以我们做一个
var startTime = new Date();
获取开始时间。接下来我们需要检查每次迭代后的时间,因此在您的for
循环中,我们将添加(我个人会先声明变量并且此处没有 var,但这只是我的风格偏好)
var execTime = new Date();
execTime = execTime - startTime
告诉我们脚本执行了多长时间。如果你想变得花哨,你可以检查你的循环平均需要多长时间并检查是否还有足够的时间,但通常只给它一些时间来执行截止就足够了。然后你需要做一些事情。首先,您希望通过使用来存储您拥有的数据
PropertiesService.getScriptProperties().setProperty(key, value)
其中key
是属性名称,value
是您要存储的对象。如果它是一个数组,请确保使用JSON
对其进行字符串化。您还需要创建一个触发器以从您离开的地方触发。阅读更多关于 here 的信息。你当然可以先创建触发器,然后存储属性,没有问题。
这是在我的脚本中执行此段的函数示例:
function autoTrigger(passProperties, sysKeys)
var sysProperties = new systemProperties();
if (typeof sysKeys === 'undefined' || sysKeys === null)
sysKeys = new systemKeys();
var triggerID = ScriptApp.newTrigger('stateRebuild')
.timeBased()
.after(60000)
.create()
.getUniqueId();
Logger.log('~~~ RESTART TRIGGER CREATED ~~~');
//-------------------------------------------------------------------------------------------------
// In order to properly retrieve the time later, it is stored in milliseconds
passProperties.timeframe.start = passProperties.timeframe.start.getTime();
passProperties.timeframe.end = passProperties.timeframe.end.getTime();
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
// Properties are stored in User Properties using JSON
PropertiesService.getUserProperties()
.setProperty(sysKeys.startup.rebuildCache, JSON.stringify(passProperties));
//-------------------------------------------------------------------------------------------------
Logger.log('~~~ CURRENT PROPERTIES STORED ~~~');
return triggerID;
现在您必须停止脚本。您可以使用throw
来执行此操作。这是我的做法:
if (typeof execProperties.nextPageId !== 'undefined' && execProperties.nextPageId)
if (isTimeUp(startTime, Math.max(averageExec, execTime)) === true)
autoTrigger(execProperties);
throw new Error('Script reseting');
我留 1 分钟以防万一。
function isTimeUp(start, need)
var cutoff = 500000 // in miliseconds (5 minutes)
var now = new Date();
return cutoff - (now.getTime() - start.getTime()) < need;
请注意,在我的情况下,脚本不会调用相同的函数,因为有不同的启动过程。您需要做的是调整脚本以获取存储在属性中的值。您可以进一步阅读属性here。因此,如果您的脚本可以找到属性,您要做的就是删除触发器、检索变量并清除所有这些属性,然后一直跳到循环。这可以通过几个if
语句轻松完成。
希望这会有所帮助。
【讨论】:
以上是关于Google 应用程序脚本 360 秒超出时间限制解决方法的主要内容,如果未能解决你的问题,请参考以下文章
尝试在我的网络应用上使用 Google 和登录时,不断收到“超出未经身份验证使用的每日限制。继续使用需要注册”