作为我正在研究的项目的一部分,我必须建立各种报告。为了构建这些报告,我从非常大的数据集中收集数据。因此,我试图优化我的循环结构,以最大限度地减少内部循环内的检查。
我经常使用的一个全局变量(键值类型的数据集)大致具有以下结构:
dataStatus->inputDate->state->region->street->data
我必须遍历前面的每个部分才能获得我所追求的数据。问题是,虽然我总是有状态(它是输入屏幕中的必填字段),但区域和 street 可能会被省略。如果 street 留空,我必须遍历该地区的所有街道。同样,如果区域留空,我必须遍历州内的所有区域,并再次遍历每个区域的所有街道。
简单的解决方案是这样的:
loop through dataStatuses {
loop through dates {
if street is set {
get data
} else if region is set {
loop through streets {
get data
}
} else {
loop through regions {
loop through streets {
get data
}
}
}
}
}
但是,我真的想要一种跳过内部检查的方法。到目前为止,我能想出的最佳解决方案是这样的:
if street is set {
loop through dataStatuses {
loop through dates {
get data
}
}
} else if region is set {
loop through dataStatuses {
loop through dates {
loop through streets {
get data
}
}
}
} else {
loop through dataStatuses {
loop through dates {
loop through regions {
loop through streets {
get data
}
}
}
}
}
有没有比这更优雅的解决方案,可能会在达到数据之前满足n个级别?
非常感谢任何帮助。
答案 0 :(得分:1)
嗯。嗯,首先,这个代码可能是如此重要的I / O限制,优化除磁盘访问之外的任何东西可能是无关紧要的。你可以尝试在每次if-test之前将无意义的循环抛到1000,只是为了看看它有多大差异。
索引似乎不会对您有所帮助,因为您必须在未指定值时访问所有数据,但如果您正在计算聚合,则缓存这些可能有所帮助。 Intersystem's DeepSee产品专为此而设计。
但听起来好像你只是在运行一个大报告打印所有数据,并希望优化你能做到的。在这种情况下,我认为你的解决方案就是这样。就优雅而言,它通常不会与重度优化相结合。推广解决方案的问题在于它通常比定制解决方案慢。
我认为可能使用标签和goto语句,虽然更难阅读,但运行速度可能比解决方案快一些 - 如果您认为值得的话。 Intersystems在编译的SQL例程中执行标记和getos这一事实让我觉得它可能是最快的选择。我没有测量出差异,但我认为Intersystems可能有。如果你真的需要速度,那么你应该尝试不同的方法并测量速度。一定要使用冷热常规和全局缓存进行测试。
就一般解决方案而言,如果您真的想要,可以编写生成代码的内容,类似于您手工编码的代码,但可以为一般的n级解决方案生成代码。你告诉它关于水平,它会产生解决方案。这将是一般的和快速的 - 但我非常怀疑,对于这个例子,你可以很好地利用你的时间编写这个通用的解决方案,因为手工编码非常简单,而且很可能并不常见。
答案 1 :(得分:0)
我想出了一个适合我的解决方案。这可能不是我最初寻找的完美,但由于以下原因,它适用于我:
我相信我的解决方案非常容易阅读和维护而不会牺牲太多的性能:
GatherData
set command="do LoopThroughRegions(status,date,state,.dataCollection)"
if ($length(street)>0){
set command="do LoopThroughData(status,date,state,region,street,.dataCollection)"
} elseif ($length(region)>0){
set command="do LoopThroughStreets(status,date,state,region,.dataCollection)"
}
set date=fromDate-1,status=""
for{
set status=$order(^GLOBAL(status)) quit:status=""
for{
set date=$order(^GLOBAL(status,date)) quit:((date>toDate)||(date=""))
xecute (command)
}
}
quit
LoopThroughRegions(status,date,state,dataCollection)
set currentRegion=""
for{
set currentRegion=$order(^GLOBAL(status,date,region,currentRegion)) quit:currentRegion=""
do LoopThroughStreets(status,date,state,currentRegion,.dataCollection)
}
quit
LoopThroughStreets(status,date,state,region,dataCollection)
set currentStreet=""
for{
set currentStreet=$order(^GLOBAL(status,date,state,region,currentStreet)) quit:currentStreet=""
do LoopThroughData(status,date,state,region,currentStreet,.dataCollection)
}
quit
LoopThroughData(status,date,state,region,street,dataCollection)
set dataItem=""
for{
set dataItem=$order(^GLOBAL(status,date,state,region,street,dataItem)) quit:dataItem=""
// Do stuff
}
quit
除非提供更好的解决方案,否则我会选择自己的答案以供将来参考。希望它甚至可以帮助其他人。
答案 2 :(得分:0)
你正在按照你提出的答案向正确的方向发展,但我会略微改变它以避免使用xecute(xecute和性能很少发挥得很好)并使整体架构更好一些。
不是决定在主例程中做什么,只传入所需的参数,而是传入所有参数并决定在子例程中需要做什么。
所以你的代码将成为:
GatherData(...)
set date=fromDate-1,status=""
for {
set status=$order(^GLOBAL(status)) quit:status=""
for {
set date=$order(^GLOBAL(status,date)) quit:((date>toDate)||(date=""))
do LoopThroughRegions(status,date,state,region,street,.dataCollection)
}
}
quit
LoopThroughRegions(status,date,state,region,street,dataCollection)
if region'="" {
do LoopThroughStreets(status,date,state,region,street,.dataCollection)
quit
}
set currentRegion=""
for {
set currentRegion=$order(^GLOBAL(status,date,region,currentRegion)) quit:currentRegion=""
do LoopThroughStreets(status,date,state,currentRegion,street,.dataCollection)
}
quit
LoopThroughStreets(status,date,state,region,street,dataCollection)
if street'="" {
do LoopThroughData(status,date,state,region,street,dataCollection)
quit
}
set currentStreet=""
for{
set currentStreet=$order(^GLOBAL(status,date,state,region,currentStreet)) quit:currentStreet=""
do LoopThroughData(status,date,state,region,currentStreet,.dataCollection)
}
quit
LoopThroughData(status,date,state,region,street,dataCollection)
set dataItem=""
for{
set dataItem=$order(^GLOBAL(status,date,state,region,street,dataItem)) quit:dataItem=""
// Do stuff
}
quit
我还建议将这些小子程序更改为私有程序。