2023-08
6

VBA字典多条件单汇总

By xrspook @ 11:41:00 归类于: 烂日记

昨天说到用VBA做多条件单汇总的分组,要怎么个做法呢?

VBA这个东西其实我并不太熟悉,我没有系统学习过,或者说其实是系统过的,但只是皮毛,而且已经是多年以前了,之后用得不多,所以忘记得差不多了。所以现在我只是需要用什么的时候就去研究。在开始之前实现我的目标之前,我也先去搜索了,得出大概两个方案,一个是用字典,另外一个是用SQL。就思路来说,我感觉SQL比较直接,因为那调用的是列的信息,各种聚合会非常方便。我不知道VBA里面的SQL到底是怎么个执行法。因为2003或者以前的office,如果要做跨表查询就得用SQL。我已经不记得那些跨表查询到底只是一个工作簿里面不同表,还是直接可以跨不同的工作簿,但可以肯定的是,如果某Excel文件里运用了SQL,那么这个Excel文件一旦移动,比如发给了别人或者是U盘拷贝到另外一台电脑就会查询失败。之所以这样是因为实际上Excel在电脑的某个地方存储了与SQL有关的某些东西。虽然你把Excel文件挪走了,但是存储在你电脑里面的某个东西还在原来的地方,那是一个绝对地址。不管那个到底是绝对地址还是相对地址,简而言之就是实际上你只能挪走了一部分的东西,另外一部分还在那个地方,但即便你那么高难度,那个电脑深处的东西也挪走了。即便把它像放在另外一台电脑相对位置一样的地方,但还是识别不出来。所以最终你只能放弃那个查询,然后贴同样的代码,在另外一台电脑上重新建一个,这个操作很逆天,非常不方便。正是因为我知道这个,所以虽然在合并查询上SQL很方便,但是不到迫不得已我不会这么干。如果我真要这么干,我就不用Excel,我用了Access。以前的 Office 2003专业版都是自带Access。说起数据库的操作,在那个地方用SQL就很自然。

想到分组的时候,我当然有考虑过SQL,但是我知道以前的Excel对这个很不友好。所以别人做的某些方案,SQL版本干的事情很简单,但前面一大堆的参数引用会让你眼花缭乱。相对而言,字典是VBA的原生,所以说VBA一定能很好支持,因为VBA的字典是提升VBA性能的一个重要体现。我觉得不仅仅是在VBA,在python里,用字典和不用字典做同样的查询,效果差别也非常明显。我想要的多条件单汇总要怎么做字典呢?我首先想到的当然是把条件都合并起来作为键,然后把要汇总的那个作为键值。这就保证了前面那些条件一定是唯一的,后面的键值做累加就行了。当然,你也可以不把前面所有条件都合并成键,你只需要把一些特征条件合并就行。把多条件合并起来,做一个循环,用分隔符把它们连起来就可以了。耗费我比较长时间的是,我建立了这个字典以后,我就需要把键和键值都输出重新组合成一个数组,那就是我想要的多条件单汇总结果。但问题是怎么才能把合并起来的键重新分解输出呢?我研究了半天。理论上被我连接起来的那些条件放的键实际上是一个字符串,而且里面有规律的分隔符。我利用分隔符就能把它重新打开,然后我新建一个数组接收这些被打断的字符串。这个接收了打断字符串的数组外加字典的键值,合并起来再复制给新的数组就是我想要的东西。思路是这样的,但是当我要执行的时候,我却一直卡在那个地方。关键就我在定义这个接受打断字符串数组的时候加了括号。为什么其它数组在定义的时候就得在名称后面加括号,但是这种接收字符串的数组在定义的时候就不能加呢?这个是我搞不懂的。事实是不加我就可以实现功能,加了就会说类型错误。这种接收打断字符串的东西不应该称作数组?我已经不记得到底我是从哪里学回来的这一招接收打断字符串形成数组的这种做法。生成一个表格标题的时候,这样做非常简单高效。

先把多条件合并成一个字符串,然后再把这个字符串打断。这种方案我在网上也看到了,别人也是怎么干,但是别人之所以没有像我这样卡住,是因为把字符串打断的时候他们并不是赋值给数组,而是直接在单元格里输出了,于是他们也就没有定义数组名称时加了个括号就卡住的烦恼。

最终,我实现了自己的目标用VBA接收源数据,然后进行加工处理,做一个多条件单汇总,最终在一个固定格式的表格里输出并另存为一个新文件。整个过程你只需要在VBA所在的文件里面按一下初始化按钮,然后把需要整理的数据贴进去,然后再按一个相应的生成文件按钮。文件生成的过程可以说是秒杀,几乎不需要时间。生成的文件会自动保存在VBA文件的相同目录。这个VBA文件你可以点保存,整理后的文件内容也会在VBA文件的相应页面输出。如果你需要查询,直接可以在那里看,如果你根本不想看,这个VBA文件你甚至不需要按保存。因为如果粘贴源数据前内容是空的,下一次你再进来的时候就不需要初始化,所以步骤就更为简单,下一次你只需要做粘贴以及点一下生成。明明转换这些数据很简单,VBA可以秒杀完成,为什么PQ就得转那么好几秒呢?之所以在做这个事情上我不做python方案,是因为你还得把源数据贴到Excel里面,保存关闭,再按python生成。这就意味着。虽然你只打开关闭了Excel文件一次,但是python在运行的时候又得把Excel文件打开关闭一次,这样就得耗双倍的开关时间。如果数据量很大,VBA很慢,又或者那得进行多个文件的拼接才能取得一个很大的数据,汇总的数据超过了VBA的处理范围,那么可能我会选择python方案,但现在,顶多每次就几百条数据,难度是非常低的,高效完成是我最看重的,因此VBA是最佳选择。

用过VSCode以后,当我在Excel的VBA里编写代码,真的非常痛苦,因为当你一句话没写完,就要在其它地方复制某些东西过去的时候,就会给你弹窗报错。为什么你非得给我弹窗呢?你给我下面波浪红线不好吗?又或者当我要运行的时候,你才告诉我那个不对不行吗?我写一半就不写了,并不是因为那是我写错了,而是因为我需要做其它复制粘贴,又或者是切换个窗口做一下别的事情。如果能在VSCode面直接编写VBA,然后能直接在Excel里面调试运行,不需要把代码贴回VBA那么折腾该多好。

我可以忍受VBA的各种规则,但是我真的不喜欢Excel自带的VBE。

VBA多条件单汇总字典方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
'crr(i, 1 To 7)为数组,多行7列,前6列为条件,最后1列为数据,需要汇总
Set dic = CreateObject("scripting.dictionary") '建立字典
    For i = 1 To UBound(crr)
        x = crr(i, 1)
        For j = 2 To 6
            x = x & "|" & crr(i, j) '合并分组依据
        Next
    dic(x) = dic(x) + crr(i, 7)
    Next
Dim err, frr() As Variant
ReDim frr(1 To dic.Count, 1 To 7) '新汇总数组
i = 1
For Each x In dic.Keys
    err = Split(x, "|") '打碎分组依据
    For j = 0 To 5
        frr(i, j + 1) = err(j)
    Next
    frr(i, 7) = dic(x)
    i = i + 1
Next
2020-10
23

我实现这些

By xrspook @ 10:16:14 归类于: 烂日记

渐渐地我越发有点明白自己想要的到底是什么。之所以让我纠结了半天,不知道该如何计算的某些数据,实际上是因为两种性质的东西混搭在一起了。我不知道别人是否真的搞得清,他们是有意糊弄我们的吗?是因为他们知道我们算不清这个,所以这么搞吗?这个问题很困扰我,是因为那个东西把正常的批次跟没有写明批次但要先进先出的混搭在一起。有明确批次的数量计算是不分仓号的,因为通常那意味着好几个仓进同一批货,开始计费的日期定在第1个仓刚开始进货的那天。所以,如果同批次还有其他仓,最后一个仓进货的时候可能已经是3天后,那个仓的免堆期等于要减去三天。如果某个客户所有东西都这么弄,不会有烦恼,因为无论进货还是出货,肯定会带入批次。

在Power Query的世界里,这就意味着要以批次和日期分组。各种仓号数据可以全部加在一起,无所谓。但同时,这个客户又有另外一种情况。如果他进货不是用船,而是用车,每天几千几千吨进货的话,我们跟他们签订的合同是按每天为一个批次。一个批次到达免堆期以后开始计费。现在我们的系统总的来说,根本没分清这种批次,计费完全是人肉计算的。但实际上,这种没有批次的批次,理论上也应该自动带入,入库的时候以日期为批次。所以有可能发生同一个批次有几个仓出库的时候,要以先进先出进行批次划分。进货的时候,一个批次可能有几个仓,但是出货的时候肯定是指定的。所以在这里要以仓好为分类,然后在对入库批次分割数量。比如某一个仓里第1个日期批次用完了再开始用第2个,第2个没用完,就留到以后继续分割。据说用SQL和Power Pivot能解决这个先进先出的问题。但貌似,我看到的Power Pivot的例子没有看到我想要的最终结果,也就是把那个出库数量自动分割成入库批次。先进先出是非常经典的会计问题,只不过我们的人从来都是我觉得我要指定这个,没有用过这些规则,当然也就没有思考该怎么自己动手去计算。一开始没有批次,用先进先出的规则,入库的时候自动添加批次,出库的时候按照入库的批次风格,最终得出来的数据,跟之前一开始就已经有批次的东西可以用同样的规则继续演算。

对我来说,有两个点我没想明白。首先,要怎么在PQ里以先进先出的规则,对出库数量进行批次分割呢?第二,因为实际上我们得到的信息是一个入库日期和出库日期。而这些日期通常都是不连续的,在不连续的日子里也要计算每天库存,到达一定时间之后,要每天计算堆存费。所以,该怎么在不连续的日子里插入一些日期,然后向下填充信息呢?之前我想到的办法是先建立一个日期索引。然后把有数据的东西合并到索引里,这就意味着有些日期可能是没有数据的。接着,把这些东西透视展开,把数据为空的那一列删掉,余下的东西向下填充,然后再把零数据替换为空,接下来再用一个逆透视恢复。这个方法比较笨。但可以一次性实现N个批次的插入和填充,至于速度如何,估计跟电脑的性能有关。我总觉得高手一定不需要用到这种先透视再逆透视的方法。他们是怎么插入日期,然后向下填充数据的呢?

想清楚了这些,离我想要的最终结果就会又近了一些。

2020-10
20

我要优化提速

By xrspook @ 8:36:19 归类于: 烂日记

当我终于把功能做出来以后,我却嫌弃出结果太慢了,居然要好几分钟。明明最终我想要的是一个表的合并,为了更快,我不得不拆分为两个查询。第2个查询以第1个查询的结果为基础。其实这么操作,无非我是想利用第1个查询已经得到的缓存结果。那个结果已经被我用表格输出。之前我试过从零开始弄第2个查询,结果发现实在太慢了。如果没有那么多的分组,速度还会那么慢吗?如果只是一个求和,根本无需分组,但问题是,每个批次的东西必须分开计算,然后才可以出现分段的结果。说白了,让我纠结的是一个累计求和。

累计求和这种东西的思路在PQ里通常都意味着新增一列,参数设定匹配某行的某些东西,符合条件就把某列的数据求和。所以实际上这是一个筛选的过程。如果数据很多,筛选肯定会很慢,但除了这样,还能有什么方法吗?据说可以用索引的方法。据说索引的方法比筛选的方法快非常多。如果用python的思路去考虑,我觉得筛选是一个列表的操作,而另外一个是字典的操作。如果不用二分法。历遍列表是非常慢的,但如果要立片字典,历遍是轻而易举的事,而且字典的效率比二分法还要高。所以我应该如何建立索引呢?如果筛选的是多条件,索引大法还能继续管用吗?我觉得现在我遇到的问题那些经常接触数据库的人估计已经纠结过了。这不仅仅是Power Query的问题,这是如何运用数据进行弯曲折叠的问题。只要是数据库,无论是SQL还是其他形式,都会有这种烦恼。

昨天我终于经历了一个Excel要跑好几分钟甚至十几分钟才能出结果的东西,我感觉那没多少数据。我曾经试过把那些东西输出,结果发现输出速度非常慢,每秒钟只处理了不到100个。那些数据粗略计算了一下,可能有超过2万条。为什么加载2万条数据会这么慢呢?这是一个令我纠结的结果,如果把最后的分组都做了,输出的数据只有365条,但如果不做最后的分组,有超过2万条。不做分组的话,那个结果可以在软件里直接展示出来,顶多只需要几秒的运算时间,但是不做分组,把数据输出却有超过2万条,即便我不输出表格只输出数据透视表,依然在输出的时候速度非常慢。为什么对2万条数据进行分组会这么慢呢?除了分组,还有其他快速的方式可以对某条件进行求和吗?整个操作之所以这么慢,除了因为分组,还有排序,还有一些,null转化为0,或者把0转化为null的操作,最后,还有一条我自己都觉得应该会很作死的向下填充。那个结果我花了好几分钟才计算出来,如果让高手去解答,估计运行时间会会是毫秒级的,顶多不会超过三秒钟。

一方面,我很想知道如何提升运行速度,直接拿去问人显然是最显而易见的办法,但在这之前,我想自己先思考一下,毕竟走到这一步已经很不容易,我不想在最后一步认输。这让我想起了高中数学老师的某句经典语录,学习数学几个境界里的最后一句——全而不好(前几句是“不懂不会,会而不对,对而不全”)。

2020-10
13

Excel的高端玩法

By xrspook @ 8:43:14 归类于: 烂日记

数据本身没有问题,如果我们不能让它们确立某种关系,只是因为我们对那个东西还不够了解而已。在Excel里做一件事,你可以通过很多方法,比如说函数,比如说VBA,比如说SQL查询,又或者Power Query或Power Pivot。当然,我这里所说的,主要是针对查询,或者说数据清洗类的东西。如果纯粹是针对单元格的格式化,函数以及Power BI系列以及SQL是没办法做到的。

同样一个数据,用不同的方法都可以得出目标答案,但是哪个会更简便快捷一些呢?函数我觉得挺被动的,尤其是在处理大量数据的时候,效率非常低。因为在处理一些复杂东西的时候通常要用到数组函数,即便不需要用到数据函数本身,其实也在运用着数组函数的变体。而且函数这种东西受Excel本身版本的限制,越是低版本的Excel越是没办法轻而易举地实现某些逼格的功能。于是就出现了你不得不为了某个功能升级Excel,又或者因为你的伙伴升级了Excel,用了一些高端的函数,但是你却看不到,工作就没办法继续下去了。SQL和VBA是两个大杀器,很早以前Excel就已经支持。与其说他们是Office软件的一部分,不如说这两个东西更接近于编程语言。我对Excel里面的SQL不是十分熟悉,因为至今为止,虽然已经折腾了不少网站,但是我从未试过操作数据库。SQL在Excel可以用,但我觉得可能在Access里SQL会用得更顺手一些。比如说如果改变数据源,比如移动文件之后,SQL需要重新连接。若没有VBA的帮助,这是无解的。我不喜欢用SQL的其中一个原因是它会在硬盘的某个位置生成某个数据库。

VBA这个东西强大到任何你想到想不到的东西都可以控制,无论是数据本身还是说单元格的格式,一律通杀,它甚至可以让Excel自杀,又或者让你的系统自杀。VBA用得好不好直接决定了某个脚本的运行效率。是对初级用户来说,VBA的学习成本实在是高,除非你从来不打算要建立自己的规则而纯粹只是用别人的东西。

至于Power BI系列的Power Query和Power Pivot现在我仍然处在甚至还不能说入门的阶段,我只是稍微了解了一点这两个东西。在数据清洗和建立关系的时候,它们实在太强大了。但是要使用这两个东西,Excel的版本就必须有要求。所以这也导致了不少免费用户直接绕过这两个强大的东西。我也不知道为什么自己在使用Excel高级函数几乎还没入门的情况下,我就去折腾M语言。我觉得那个东西一定程度上颠覆了我对数据的理解。Power Query对数据的处理方式就像通过各种蹂躏就能得出你想要的东西,其间你没有修改原数据,所以实际上在写M语言的时候就像是手工编写一个宏,而那个宏要比一般的VBA简洁很多。之所以简洁,一定程度是因为那是在高级套用的前提下。Power Query里玩的数据转换实际上是在折叠、删除以及扩充,一定程度上就像是在用类似于递归或者迭代的方式。

别人把时间耗在应付考试上,我把时间耗在折腾自己上。

2020-09
19

我喜欢Excel

By xrspook @ 20:53:41 归类于: 烂日记

Excel的一般公式,我比较熟练,一些高级公式的叠加,我需要找教程套用,但起码我知道那是可以做到的。一般的数据透视表,是我一直以来用得相对来说最顺溜的东西,至于高级的数据透视表,也就是超级数据透视表我几乎不了解它的高级用法。在数据的筛选查询方面,之前我用的是公式,而近期,我知道了有Power Query这种神器。在这之前,我已经知道可以SQL语言查询。去年我开始系统学习了Excel VBA。这让我大大提升了某些工作的效率。当然这是非常有针对性的。对我来说,要开发一个VBA脚本需要好些时间,并不是一写就能用的那种类型,期间要经过不少修改。所以其实总的来说,对Excel的了解我还是比较全面的。

也正是因为有这样的经历,所以当我遇到某些综合性的问题的时候,当别人把目光主要集中在某个他们很熟悉的版块的时候,我会凭借我的直觉找问题,而不局限于他们觉得出问题的那个地方。比如在把SQL查询跟VBA结合的时候,别人会把精力放在SQL查询有没有写错上面。SQL有没有写错,其实我根本没看,对我来说那些东西太长了,看不懂,而且那个人写的VBA脚本缩进很有问题,看得我很郁闷,所以我就更加没有心情在那里琢磨。那既然能计算出一个正确答案,说明那个查询语句应该没什么问题。也正是因为写脚本的人的那堆东西格式比较混乱,所以我有理由怀疑那是拼凑起来的脚本,因为居然在脚本的开头连变量的定义都没有。为什么VBA里没有进行规范的变量定义,后面也居然可以照样使用呢?这让我有点惊讶,毕竟这是个VBA,不是python。C语言里,如果不先进性变量定义,后面根本用不了。在我记忆之中,VBA的变量在使用之前是需要先定义的。最终我发现是那个人的脚本之所以出错,是因为某些语句的套用搞错了,为什么他会把那个东西放在里?我觉得大概是因为他没有明白他一开始做的那个with是什么意思。但如果你问我为什么他把那堆东西套在里面会出错,而且是某些地方出错,不是全部出错,我回答不出来。理论上这种错误能在恰当的调试中体现出来,但实际上,VBA的调试句子我还用得不算很熟练。或者你会说,这是因为我的VBA学习还不够系统化,但我觉得我已经用了学习VBA最靠谱的那本书了。可以肯定的是,一些很基础的调试方式我还没掌握,如果我学会了那些东西,我可以大大提升我的调试效率,把错误定位得更精准。VBA脚本这种东西,我觉得最根本的是必须得理解。如果纯粹是各种套用,基础功能的确可以快速实现,但是当遇到的问题比较综合的时候,就会出现一些他们完全料想不到的状况。那种状况有可能与脚本本身的内容无关,与脚本的结构有关。

相对来说,Excel里我用得最弱的是高级公式的套用。如何用一个非常复杂的公式解决一些高端的问题是我一直以来都不大上心,或者说记得不够好的部分。非常复杂的公式,尤其是数组公式,虽然能解决一些神一般的问题,但问题是,其实那些公式需要耗费大量资源,所以在处理大数据的时候,非常有可能出状况。我是一个实用主义者,能做到某个功能,但是做起来的效率不高不好,我为什么要选择那种只是看上去很炫酷的方式呢?情况就像用VBA解决同一问题的时候,如果只是在工作表层面处理和先用内存数组处理再在工作表层面表达,效率千差万别。

Excel对我来说,除了要最终结果,过程也得追求高效和方便。

© 2004 - 2024 我的天 | Theme by xrspook | Power by WordPress