正如我们在AnyLogic中数据库应用的小陷阱(Part 1)——数据刷新中看到的那样, Anylogic 的内部数据库是有一些小陷阱的......
在这篇文章中,我们想让您了解在使用Excel作为外部数据源时数据库的一些其他值得注意的地方,并指出一种更可靠的数据导入方法。
通常,在为客户创建模型时,仿真模型所需的重要输入数据通常会以Excel文件的形式提供。可能是轮班计划、生产计划或客户订单等等。确实,Excel不是处理数据的最佳选择,但大多数公司都在使用这种简单易用的软件来准备和收集数据。因此,仿真项目的客户大多要求以Excel作为管理仿真模型输入数据的首选工具。
所以,对于仿真建模人员来说,能够熟练地将基于Excel的数据输入到模型中,不必费事和绕道而行,是一项必备技能。
但是要如何做到这一点呢?
使用内置数据库访问Excel文件
您可能已经猜到了:使用Anylogic的内置数据库!只需从Anylogic建立到Excel文件的连接,所有数据都将被提取并加载到数据库中,从而加载到您的模型中。听起来太简单太好了以至于感觉不像是真的?但这绝对是真的!
在大多数情况下,标准连接可以充分发挥作用,尤其是当您熟悉使用AnyLogic时。但如果您是一名顾问,客户的具体需求往往会有所不同。。。大多数客户希望在无需AnyLogic软件的情况下使用该模型,因此交付成果会是导出的独立版本或云版本。在这两种情况下,只能通过仿真前端或正在使用的excel文件调整输入数据。很多时候,第二种选择更受青睐,因为大多数客户已经熟悉Excel。因此,构建尽可能稳健的数据导入非常重要。
我们设置了一个示例Excel工作表来重现和解决一些常见问题。在下图中,左侧为Excel文件,右侧为AnyLogic中导入的数据。
我们可以调整每一列中的条目,当启用自动更新时,AnyLogic通常会在仿真启动时更新这些条目。但这里存在第一个陷阱:如果输入文件的结构或数据类型发生重大更改,AnyLogic DB将不再刷新数据库,甚至更糟的是,这个过程不会向用户发出任何错误消息或通知,因此可能会导致具有误导性的仿真结果。
我们总结了一些可能导致此问题发生的案例:
向表中添加带有标题的新列:通常,在excel文件中的其他列中执行某些计算时不会出现问题,但如果向这些列添加新标题,则刷新此文件中的数据将不再有效。
更改列的数据类型:例如,如果我们将小数添加到预定义的整数列中,表也不会刷新。因此,如果将来此列中可能有小数,则必须将任何仅包含整数值的列定义为双精度。
使用VLOOKUP:就其本身而言,在表中没有“#N/A”值的情况下,使用VLOOKUP是没有问题的(“#N/A”值也表示会导致数据库停止更新的不同数据类型)。例如,可以通过在返回正确数据类型的VLOOKUP周围封装IFERROR语句来避免这种情况。
那现在怎么办呢?
方法1:
删除其他标题,并且整数列再次仅包含整数值时,此表才正常刷新。基本上,就是直至恢复原始格式。之后,表就会再次按预期工作和更新。
因此,在使用外部数据源时,如果仅依赖自动更新功能,则需要小心。更新失败很可能会被忽视,并最终危及模型的质量。因此,我们更推荐使用excel数据输入的第2种方法,可以在数据库更新失败时,让用户可以接收到反馈。
方法2:
如果要使用内部更新功能并想要注意可能的错误,可以使用以下代码。实际上,它本身并不能解决问题,但至少在表更新失败时,您会收到相应的错误消息提醒。
首先,定义ImportableDataonStartup函数,需要将下面这段代码添加到相应智能体的附加类代码:
1.public void importTableDataOnStartup(java.sql.Connection internalDatabaseConnection) throws Exception {
2.try (DatabaseDescriptorRegistry r = new DatabaseDescriptorRegistry()) {
3.java.sql.Connection cachedSourceConnection;
4.cachedSourceConnection = r.getConnection(DatabaseDescriptorFactory.createFileDescriptor(Utilities.findExistingFile("excelfile.xlsx"), null, ""));
5. UtilitiesDatabase.copyDatabaseTable(cachedSourceConnection, internalDatabaseConnection, "\"EXCEL-TABLE-NAME\"",
6."INTERNAL AL DB TABLE NAME");
之后,用下面的代码调用importableDataonStartup函数,以下代码必须在实验属性的Java行动“仿真运行前”中用于初始的实验更新,或在智能体属性的智能体行动“启动时”中用于与智能体相关的更新。如果在初始化例程中需要使用数据,则在初始化main类之前执行更新是很重要的。否则,初始化将基于过时的数据进行。
1.try {
2.// this will call the additional class code
3. importTableDataOnStartup( getDatabaseConnection() );
4.} catch ( Exception e ) {
5.// this will generate an error message to let the user know that there are some issues
6. e.printStackTrace();
7.}
为了确保即使客户错误处理导入文件,您的模型也会更新并因此正常工作,您可以使用AnyLogic连接面板中的数据库模块。这样,您就可以在任何Excel文件上使用类似sql的SELECT FROM语句。但是,这同样有一些非常重要的缺点需要注意:
l 您可以添加WHERE语句,但在没有任何通知的情况下不会考虑这些语句。这意味着“SELECT material FROM mat_db WHERE density > 5”的将检索列表中的所有材质,而不仅仅是密度大于5的材质。
l 数据中的任何公式都不会得到结果。您将收到一个包含公式本身的字符串
因此,这种变通方法可能只在一些比较少见的用例中使用,其中
l excel文件中的列位置可能会频繁更改,
l 列不包含任何公式
l 如果在检索数据之前不需要过滤数据(显然,可以在从文件中检索数据集之后过滤数据,但这可能会导致大量数据的性能问题。因此,我们一般不推荐这种方法)
最后介绍的方法使用AnyLogic连接面板的Excel文件模块。通过此操作,您可以使用行号和列号的组合来访问Excel文件的任何填充单元格。要将其用于类似数据库的数据调用,必须开发相对复杂的访问函数。如果您以适当的方式执行此操作,您就可以真正根据自己的需求创建一个强大而灵活的界面。
作为客座作者,我们将使用Excel文件模块方法用于非常复杂的客户项目,以确保模型的生命周期能够持续很长时间,而无需客户提出任何额外的支持请求。我们明确建议仅对高级用户和复杂客户请求使用此方法。最佳实践是将内部数据库的更新方法与我们的扩展代码一起使用,如果AnyLogic在更新过程中出现问题,都会得到通知。
原文作者:
Maximillian Selmair 和 Patrick Wöhe相关文章