用過SAP的憑證批量錄入模板(Excel文件)的都知道,一個憑證由【抬頭】和多個【行項目】組成,這是一個關於excel兩表信息關聯的典型場景。
這里頭蘊藏着一個麻煩:當我們需要一次性錄入多個憑證時,如何將每個憑證的抬頭與行項目關聯起來呢?
假定這是一個SAP憑證的excel錄入模板,包含多個待錄入的憑證:
【抬頭】表:
號碼 | 憑證日期 | 憑證類型 | 公司代碼 | 記賬日期 | 貨幣 | 匯率 | 參考憑證號 | 抬頭文本 |
1 | 20220113 | RE | CN10 | 20200113 | CNY | 這是一個僅用於測試的憑證1 | ||
2 | 20220113 | RE | CN10 | 20200113 | CNY | 這是一個僅用於測試的憑證2 |
【行項目】表:
號碼 | 記賬碼 | 科目賬戶 | 特別總賬標識 | 憑證貨幣金額 | 稅碼 | 成本中心 | 訂單 | 文本 | 基准日 | 分配 | 原因代碼 | 貿易伙伴 |
1 | 40 | 1001010000 | 123.25 | 這是一個用於測試的憑證1 | 20210106 | A11 | ||||||
1 | 19 | 1066322 | A | 123.25 | 這是一個用於測試的憑證1 | 20210106 | ||||||
2 | 40 | 1001010000 | 427.86 | 這是一個用於測試的憑證2 | 20210108 | A11 | ||||||
2 | 19 | 1066323 | A | 427.86 | 這是一個用於測試的憑證2 | 20210108 | ||||||
看了表結構,如果實際業務中【抬頭】文本跟【行項目】文本要求有一定差異,那么能將兩張表關聯起來的就剩【號碼】字段了。此處【抬頭】號碼1跟【行項目】號碼1說的都是第1個待錄入憑證。
當我們第一層循環是遍歷【抬頭】表的每一行時,第二層內循環便是遍歷該憑證行項目對應的每一行。進一步分解需求,比如號碼1,我們需要動態計算號碼1在【行項目】的起始和終止行號。小爬能想到的思路有以下三個。
①利用excel公式來動態計算;
②利用兩個字典分別存儲某個號碼在【行項目】中的首尾行號;
③建立個臨時表temp,利用sql或Excel自帶篩選功能快速篩選出號碼等於特定數字(比如1)的數據,寫入臨時表。
先說方法一:使用match結合countif分別得到首尾行號。
如下圖所示:
接着說說方法二:使用字典來得到某個憑證行項目的首尾行號
我們從上至下遍歷【行項目】每一行,用字典(key,value)分別存儲號碼和對應的行號,由於字典【key】的唯一性和可覆蓋性,我們永遠key對應【號碼】,則value永遠對應的該key最后一次出現的位置(行號),因為該key第一次存入字典的value(起始行號),隨着逐行往下遍歷,被不停用新的value(行號)覆蓋。
此時我們只需要掌握一個trick,先定義個字典,從上至下遍歷,最終存儲key(號碼)和value(結束行號);緊接着再定義個字典,從下至上遍歷,便可存儲key(號碼)和value(起始行號),你get到這一點了嗎?
完整實現的代碼示例(VBA)如下:
1 Sub voucherEntry() 2 Dim headerSht As Worksheet, itemSht As Worksheet, numberStr As String, startNum As Integer, endNum As Integer, headerMaxRow As Long, itemMaxRow As Long, i As Integer, j As Integer 3 Dim startDic As Object, endDic As Object 4 Set startDic = CreateObject("scripting.dictionary") 5 Set endDic = CreateObject("scripting.dictionary") 6 7 Set headerSht = ThisWorkbook.Sheets("抬頭") 8 Set itemSht = ThisWorkbook.Sheets("行項目") 9 headerMaxRow = headerSht.Cells(Rows.Count, 1).End(xlUp) '[抬頭]表的最后一行行號 10 itemMaxRow = itemSht.Cells(Rows.Count, 1).End(xlUp) '[行項目]表的最后一行行號 11 For i = 2 To itemMaxRow 12 endDic.Add CStr(itemSht.Range("A" & i).Value), i '將號碼與對應行號的關系存入字典,思考下,為啥要轉為字符串格式 13 Next 14 15 For i = itemMaxRow To 2 Step -1 16 startDic.Add CStr(itemSht.Range("A" & i).Value), i '將號碼與對應行號的關系存入字典,思考下,為啥要轉為字符串格式 17 Next 18 19 For i = 2 To headerMaxRow '遍歷【抬頭】表每一個抬頭行 20 numberStr = CStr(headerSht.Range("A" & i).Value) '【號碼】,思考下,為啥要轉為字符串格式 21 startNum = startDic(numberStr) '憑證對應的起始行號 22 endNum = endDic(numberStr) '憑證對應的結束行號 23 For j = startNum To endNum '遍歷該憑證行項目的每一行 24 此處維護核心【代碼塊】 25 Next 26 27 Next 28 29 End Sub
至於方法三,小爬就暫時不對此進行具體實現了,感興趣的同學不妨自己動手嘗試一波~
快來掃碼關注我的公眾號 獲取更多爬蟲、數據分析的知識!