2023-08
31

着迷中

By xrspook @ 8:09:03 归类于: 烂日记

我已经不记得自己是如何走上这条写代码的不归路的了,确切地说我已经不记得具体我是哪一天开始研究这个跨表查询的问题。我大概知道顺序是怎么样的,但是具体是哪一天开始我实在已经说不清了。现在我天天都在写,天天都在写VBA、天天都在纠结VBA里面的SQL。我已经不记得这种状态已经持续了多长时间,反正即便是吃饭睡觉,我的脑子里依然想着那个东西。每天就是起床吃早餐然后完成必须做的工作,接着以最快的速度完成打卡,然后一整天都在那里写代码,不管上班还是下班。在这种状态之下上班,那8个小时突然变得好短。因为已经沉迷其中,所以到了下班时间经常停不下来,吃饭时间因此一再拖延。如果不是去饭堂吃,而是自己吃麦片,那就更加是一杯麦片,我根本不知道自己是如何勺到口里的,反正那杯东西我可能得吃一个小时以上。当你完全着迷于做某事的时候,你根本感觉不到其它东西的存在,比如身体的各种反应,比如某些疼痛又或者是饥饿等等。因为我知道我一旦着迷就完全没办法顾及其它事情,所以当我还没迷进去的时候,我就得先把其它必须做的做完了,否则是完全不能自拔的。

之所以出现有现在这个状况,是因为自从我实现了第一个跨表查询的任务以后,我发现一直以来我的很多东西都可以用VBA+ADO+SQL把它们联系起来。那些以前我有数据,但需要这样那样粘贴的玩意现在我可以把它们统一起来通过查询的方式直接获取结果。这样的好处是成功开发以后很省事,但是坏处是这样非常定制化,所以基本没有什么动态可调整的余地,即便设定了很多条件当你想加更多的时候无能为力。还有一个让人觉得挺累的,就是如果我做的是数据透视表,汇总那些事情不是我干的,是系统干的,我管好明细就好,我要什么明细我就搞到什么程度,但如果我得在一个查询结果里面把汇总考虑进去,无论是列还是行,我都得想办法把实现。相对于数据透视表来说,我觉得这种汇总是硬汇总,是设计的人必须得想好的,而且一旦变换某些条件,又得伤筋动骨调试一番。对用户来说,对只看到最终结果的那个人来说,可能会觉得很爽,如果他做过之前那些这样那样的复杂操作,他更加会觉得这实在是太爽了,但如果某个人之前没有经历过那些,他只是直接用这个捷径,从他的角度考虑可能他想知道更多,但是现在已经固定好的方式没办法满足他的要求。所以我还是觉得如果数据是规范的,用Power Pivot的方式把连动起来更靠谱。

PP非常的稳定,尤其是相对于PQ来说,但问题是无论是PP还是PQ,那个写代码的地方都非常不友好。正常的窗口操作基本不会让你挂掉,但是写代码的时候非常有可能直接让你卡死,必须得关掉重来,但是之前写的东西就全部废掉了。现在我之所以会在那里玩VBA,因为实际上我玩的不仅仅是VBA本身,而是VBA延伸出去的SQL。本质上,我在进行类似PQ或者PP的操作,虽然公式少了很多,隐藏技能也就是我至今无法猜透的更多,但就运行效率来说,尤其是对小数据的运行效率来说,VBA+ADO+SQL远远优于PQ和PP。我这里说的小数据是单表不超过1万条,最宽的表不超过30列。

可能,如果我不是用Excel去玩PP和PQ,而是用Power BI去玩,效果会很不一样。

2023-08
27

连续日期累计求和

By xrspook @ 11:32:10 归类于: 烂日记

昨天说到库存查询,最后的展示方式是透视表,透视这个东西只要前面做对了,就可以实现,那就只是最后一个步骤而已,但是如何得到透视需要的所有东西呢?在做这个库存查询之前,如果我需要得到某数据在连续日期中的汇总,我会先把那些数据按日期分组,然后选定日期表里面其中一段连续日期,接着用左外的方式连接日期表和数据。但问题是在库存查询中,我最后的结果是透视的,这就意味着被透视的那些字段在没有被透视之前,是各自对应一段一样的日期表,如果最后透视出来的字段有5个,就意味着我有5段一样的日期表,我该怎么表达这个东西呢?

在解决这个多段一模一样的连续日期之前,我首先攻克的是累计数。在SQL里面实现累计数有好几种方式,但问题是不是每一个都适合在Excel的VBA里实现,比如窗口函数over在Excel里面就是不支持的,虽然窗口函数的效率是最高的。对同一个表进行子查询可以实现累计,但问题是这样生成的累计无法用在下一步的透视里使用,接着我在另外一个方案里面看到了笛卡尔积的方法,笛卡尔积得出来的结果跟子查询完全一样,但笛卡尔积出来的结果可以用在下一步的透视里,而且同样的数据,笛卡尔积的运行效率比子查询高(可能是我测试的数据少?)。这个累计数的问题,我在Power Pivot里也研究过。我觉得PP的解决方案跟子查询有点类似。在计算累计数的时候,PP的效率很高。在用PP实现累计数之前,我曾经用Power Query实现累计数。我的PQ解决办法是先生成变化数,然后按日期把变化数累加起来。接下来用日期表跟这个数据左外连接,如果不是天天都有变化数,那么这个合并后的累计肯定会有空行,这个时候再用向下填充的方式补全。用这样的方法在PQ里的确是可以计算出每天的库存,但是效率非常低,所以在那以后,当我要做累计库存,我会直接放弃PQ选择PP。现在我已经掌握了在VBA里用SQL的方法实现。在筛选的日期不多,以及最后被透视出来的字段不多的情况下,效率挺高。比如最终我只需要展示一个月的数据。运行时间通常不会超过0.5秒。我个人觉得1秒是一个分水岭,如果1秒以上才出结果的话,我觉得这需要等待,尤其是运行时间超过2秒,我觉得那得优化了。低于0.5秒的运行时间对我来说几乎是无感的,我可以接受这种方案。

在累计数可以实现之后我要继续研究怎么按照条件需求把日期表捆绑上去。折腾了好长一段时间,都是没什么结果,于是我就去吃午饭了,在去吃午饭的路上,我突然想到,要把这个日期表扩充开来,实际上我不就是要做一个按条件笛卡尔积吗?所以我需要进行左外连接左边的部分,在这个情况下就不是一个普通的日期表,而是被透视项和日期表进行笛卡尔积的东西。笛卡尔积这个东西,如果数据很多会是一个噩梦,甚至会让电脑崩溃,所以所有教程都会告诉你,如果你要的不是这种东西,尽量不要做这种操作。但思前想后,我发现我正是需要这种东西。吃过午饭后,我赶紧去测试我的想法,果然,用笛卡尔积的结果再加后续的操作,我就能生成我需要的东西,并最终能以透视的方式展示出来。

我感觉现在当我把一些最基础的东西用熟练了以后,渐渐地我体会到了一些其实你明明知道,但是你却完全没有料到可以这么用的方法。

既然人可以通过某些逻辑得到某些结果,那么我应该可以按照这些逻辑生成一些自动化的方法,在这个时候,我最讨厌例外情况。

2023-08
26

一次一个小愿望

By xrspook @ 10:58:44 归类于: 烂日记

每次都定下一个小目标,然后去实现。结果发现一天多一点的时间居然就能搞定一个问题,这种进度有点出乎我意料,因为之前的那些问题让我挣扎了好长一段时间,起码有两三天,之所以后来进度加快了,大概是因为我明了了我要做什么,但是尽管是这样,还是会遇到很多奇奇怪怪的问题。

这周初我解决的是批量生成月度核对表。我把这个任务分成两个步骤,一个是查询到底要生成多少,接着就是把查询到的结果生成文件,但是那个结果跟文件又不一定是完全对应的,根据不同条件可能10条查询结果最终会生成9个表。因为实际上某些条件是需要合并才能得到我想要的文件。在怎么设定条件,如何进行循环方面,我纠结了好长时间,几乎可以这么说,上个周末我一直在做各种尝试,为的就是最终实现这个目标。

在完成了批量生成月报以后,接下来我要做的是生成某个仓的分仓台账。相对于之前的月度核对表,这个单仓台账相对而言条件是固定的,而且必定只生成一个文件。同样我首先做的也是做一个查询,查询一下这个仓到底有多少条记录是可以生成分仓台账的,我又要生成具体哪一条。有些是无法自动实现的,因为实际情况是某些仓某些筛选条件是不一样的,但实际上应该反映在同一个分仓台账里。这个时候就需要手动合并一下条件。这种例外的事件不确定会在什么时候发生,所以必须给手动留有余地。之所以分步骤,其实一个很重要的原因是其实有时并不是为了生成分仓台账,只是要查询一下这个仓的情况。批量生成月度核对表,我花了好几天的时间,但是生成分仓台账,我只花了一天不到。

接着,我研究的是库存查询。在不同的条件之下进行库存查询,最后的结果是以一个透视表的方式展现出来,根据不同的查询条件透视的项目不一样。虽然我想到透视的项目是不一样的,但实际上在我研究的过程中,我先在单一的条件上做尝试,当单一的条件生成的数据没有问题以后再把它扩充到动态条件。因为有了之前月度核对表的锻炼,所以动态条件该怎么做我是有点底的。

SQL最基本的查询语句基本上我已经比较熟悉,这一次库存查询最后一步需要做一个透视。透视这个东西是我之前没有尝试过的,虽然我是Excel数据透视表的超级粉丝,但是在SQL里面控制这个东西,我还是很不在行的。所以到底什么条件可以控制,可以控制到什么程度我是不知道的。教程通常都只是最简单的那些,用上面的数据你重复100遍都不会出什么幺蛾子,但是在实际情况下你会有更多需求。比如当我要控制被透视列的排序的时候发现好像在Excel的SQL里无法做到。即便我在透视之前那一步已经排好序了,但是透视的时候依然是我行我素。让我比较挣扎的是,在透视之前我已经通过分组合并计算出被透视的列的合计数了,但是透视之后合计混在了那一堆被透视的字段中间。最后我已经想到不计算合计,在SQL里面生成透视以后输出到数组,我在数组里面做合计。就在我几乎要放弃的时候,原来合计可以通过在透视的select里用一个聚合函数实现,这样的话透视之后的表格就是先是条件列,然后是合计,接着是那些被透视的列。虽然合计不是放在我想要的最后面,但起码放在了最前面。

库存查询研究过程中让我纠结的问题是什么,明天继续。

2023-08
23

追求更好

By xrspook @ 8:32:56 归类于: 烂日记

实现功能,我觉得只是最基本的要求。接下来我会考虑到底有什么是可以优化的,比如如果我做了很多个判断,我能不能少做一些,把那些判断合并起来或者里面我做了不少循环,循环通常是为了赋值,赋值完以后我有没有立即就把那个数组或者字典之类的释放。我不知道那些东西释放跟不释放效率会不会相差很多,但可以肯定的是,如果我在一个脚本里同一个数组或者字典的名字被重复用多次,而在使用之前我又没有重新定义,在上一个数组或者字典使用完毕之后,把它清除干净会让我安心很多。

一开始要实现某个功能的时候,我都是写完一大堆,然后发现另外一个功能可能就是在那一大堆里面改一点点,然后就可以实现了,我就会把那一大堆复制粘贴,然后改一点点,发现的确是可以的,但是当这个脚本真的要放出来的时候,我就得合并同类项,能不能把这两个东西通过以某个判断把它们合并在一起呢?如果那两段话的差别就只是某个条件判断不一样,我能不能把那个条件判断放在前面,然后把判断结果赋值给一个变量接着后面就可以在同一段话里面引用那个变量了。这样的话从脚本的体积来说可以缩短很多,起码可以减肥一半,甚至更多。

一直以来我都想不通,如果我要从一个工作簿里复制某个工作表到另外一个新的工作簿。那么我是应该把整个工作表复制过去呢,还是说在新的那个工作簿里面新建一个工作表,然后把原来那个的内容复制到那边。在生成这篇blog的时候,其实我依然没有做过这种尝试,到底哪一个的效率会高一点呢?我选择的是直接把工作表复制过去。当我全部复制完毕以后,因为每个工作簿新建的时候都会有一个默认的工作表,我最后的步骤就是把默认的那个工作表删掉。但是让我想不明白的是,如果我不是在VBA里操作,而是直接操作Excel,我在某个已经打开的工作簿的工作表的表名那里点右键之后选择在新的工作簿里面建立这个工作表的副本,那么新的工作簿里是不会有默认空白工作表的。这是不是证明,如果我在新建工作簿里面设定某些参数可以不让那个默认空白的工作表生成呢?因为如果这样的话,我就不需要在后面再做一个删除的操作了。从数据上说,删不删除都无所谓,因为那个是空白的,但是对完美主义者来说,总觉得有那么一个空表实在很碍眼。

当我终于实现了批量生成文件以后,我发现批量生成文件的速度很慢。慢到大概每个表都需要一秒钟,如果要批量生成9个表就得9秒钟甚至更多。在考虑如何缩短时间的时候,我马上想到我把那些批量生成的表全部都放在一个工作簿里,事情就变成了批量生成一个工作簿里面的N个工作表,而不是生成N个工作簿。接着我发现这样的确能提高一点速度,如果要生成9个表的话,时间会从大概9秒钟下降到6秒钟,但是我觉得还是不行。于是我就开始研究我这个生成的过程到底有没有什么地方可以改进。因为之前我的策略是生成9个工作簿,所以我肯定是从原始的文件里先把自带格式的工作表复制到新的工作簿,然后再把新的数据粘贴到新的工作簿,然后保存。同样的思路,用在一个工作簿的N个工作表里,效率很低。所以接下来我做的就是新建一个工作簿,然后把带有格式的工作表复制过去。接着在新的工作簿里面复制里面带有格式的工作表,改名,接着在新的工作表里赋值。这样最大的区别就在于在多次复制工作表的时候,我不再需要跨工作簿了。这么一个不跨文件的操作,直接让批量生成的时间从6秒下降到2秒,效果非常明显。我觉得两秒也几乎基本上是个极限了,因为即便我用最简单的循环,基本上不在工作表里面录入什么数据也需要接近2秒的时间,现在我粘贴了那么多数据,还有就是生成这些数据之前还进行了N步操作,整个过程下来不到三秒,我觉得完全可以接受。

追求更快更好是从来没有尽头的。

2023-08
22

继续沉迷

By xrspook @ 9:12:09 归类于: 烂日记

星期天的晚上,我觉得自己睡了个寂寞。本来就很晚才去睡觉,再加上睡觉之前我正在想某些很烧脑的事情,所以现在躺在床上,我觉得整个晚上我的脑子里都是那些烧脑的玩意。在迷迷糊糊之中睡觉,我也说不准自己到底是清醒的还是睡着的。在睡梦之中我还在为那个睡觉之前折腾的问题烦恼,但醒了以后,我实在不记得睡梦之中我做了什么方案,所以说在其实在睡觉之前真的不应该努力思考,那样的话不仅仅会很久都睡不着,睡着了以后也会很挣扎。但我读书的时候不会这样,话说回来,读书的时候我就从来没有为某些问题主动这么努力过。如果是老师出的某些习题,通常都会有标准的答案。那个时候我们都不会自找麻烦,故意给自己制造难题,又或者说即便在那个时候我们找到了什么问题,你自己解决不了的,我也会主动去找同学或者直接去找老师。退一步说,我们找的那些答案只要到那是某些习题本上或者卷子上的题目,那些东西绝大多数情况之下都有标准答案,所以找到那个标准答案就好了,有可能是配套的答案也有可能是某些教辅的书籍里面有相关类似的解题说明。

现在我做的那些事情,我感觉没有一个标准答案。不同的人会用不同的方式去实现。不仅仅是实现的工具不一样,即便是一致的工具,也会有不同的实现思路。就我自己的实际情况而言,即便只是我一个人,可能今天跟明天的想法也会有区别,即便我把某个VBA脚本写得我自己满意,但说不准过上一段时间我又会有新的想法,觉得某些地方可以做某些改进。之所以这样,是因为首先我还一直在思考。其次,之所以一开始想得不周全,是因为我根本不知道周全应该是怎么样的,很多方法是参照过来的,可能在其它的编程里面是那么个用法,但什么才是最适合在VBA里实现的,又是另外一回事。比如其实VBA通过api是可以调用Excel自己前端函数,但那一定不是VBA最高效的方式。在VBA里,只要你玩好了判断和循环,对高手来说,就能解决几乎所有的问题。数组才是VBA的核心,但是数组不仅仅是一回事。可能是我一开始学VBA的时候并没有意识到原来数组是那么的复杂。因为在我印象之中,C语言的数组不是这样的。

VBA里的数组千变万化,虽然都用数组去命名,但实际定义赋值使用等等完全不一样,又或者是说它们的确有共性,但是它们的特性会让你觉得它们大概都是一回事的人无数次掉坑里,我就是这样的人。当我掉坑里很多次以后,我才发现原来它们是有很多特性的。

要清楚了解我的工具是做什么的。可以怎么用,然后我才能在用的时候得心应手。什么场合应该用什么样的数组,得到了数组以后,我又可以对那进行什么样的改造都是我应该烂熟于心的。

大概一周之前,我跟网友谈起我在VBA里遇到的烦恼的时候,我的网友说了一句,可能你连怎么VBA里调试都不知道。其实调试我是会的,因为如果我完全不懂调试的话,搞循环就是在数组瞎掰,显然除了碰壁就是碰壁。但是我真的没有系统的学习过在VBA里怎么高效调试。既然知道自己弱在哪里,我就得把这个补回来。现在我的调试要比之前高效那么一点点了。

在AI流行的现在,我还靠我自己写VBA,写那个超级八股又非常奇怪的编程语言,在别人看来非常反人类反潮流,但是当你从不大认识到比较熟悉一种编程语言,当你能得心应手轻而易举实现之前你想做到,但是却无法做到的事情的时候,那种成就感是无法言语的。

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