Board logo

標題: [發問] 請益 是否有更快的查詢方式 [打印本頁]

作者: fantersy    時間: 2021-7-29 19:45     標題: 請益 是否有更快的查詢方式

小弟有一是想請問
附件請參考
做法
1.分頁 序號 內容有兩萬多筆資料。
2.序號內容符合 L 分頁 或者 R分頁的 起始Barcode  及 結束Barcode 區間 。
3.符合區間的序號 在旁邊的工單號碼 自動帶出 L 或者 R 區邊的 工單號碼資訊。
個人做法如下
1.小弟先用資料庫(Arry) 先去讀取了 L 跟 R的所有起始Barcode  及 結束Barcode 區間 ,還有工單號碼
2.再利用迴圈 及 IF  的方式 去判別 是否在區間內開啟跟結束的Barcode。
3.如果符合條件 則帶出 工單號碼。
問題
雖然 L分頁 跟 R分頁的資料不多,但是序號分頁 有兩萬多筆資料,用迴圈跑就會跑很久(差不多20分鐘)
每一筆序號 就要比對 L分頁跟 R分頁的資料。
想請問一下!!是否建議更快速的方式??
謝謝!![attach]33790[/attach]
作者: n7822123    時間: 2021-7-30 01:47

本帖最後由 n7822123 於 2021-7-30 01:52 編輯

回復 1# fantersy

1.小弟先用資料庫(Arry) 先去讀取了 L 跟 R的所有起始Barcode  及 結束Barcode 區間 ,還有工單號碼
2.再利用迴圈 及 IF  的方式 去判別 是否在區間內開啟跟結束的Barcode。
3.如果符合條件 則帶出 工單號碼。
問題
雖然 L分頁 跟 R分頁的資料不多,但是序號分頁 有兩萬多筆資料,用迴圈跑就會跑很久(差不多20分鐘)


邏輯沒問題,才2萬多筆資料,做簡單的判斷,理論上不會跑這麼久(20分鐘)

你應該沒有用純數值運算+陣列一次輸出,若是一個個儲存格跑,會很久(儲存格是物件)

我自己嘗試,跑幾秒鐘而已,我電腦也是N年前的普通機,不可能比你快這麼多

你的附檔沒有程式(.xlsx),無法知道是哪裡花較久時間

作者: singo1232001    時間: 2021-7-30 04:04

本帖最後由 singo1232001 於 2021-7-30 04:10 編輯

回復 1# fantersy


    還有一兩種方式也很快
調用match函數 或者vlookup函數
只不過要做4次

本檔案內有兩種方式
一般陣列處理 50秒
3維陣列處理 1秒
作者: singo1232001    時間: 2021-7-30 04:26

本帖最後由 singo1232001 於 2021-7-30 04:41 編輯

回復 3# singo1232001


另外補充一下
3維方式內
總共有3組陣列  
ar=放序號用 功能抽象為 桶表資料庫 (3維陣列)
LR=放L跟R的工單&起始結束用    功能抽象為  查詢資料 (2維陣列)
br=放比對後的資料收集表用 功能抽象為 集中整合比對後資料  (2維陣列)

此案與一般不同之處 在於
我是將  資料庫 與查詢資料 是反過來建置的
雖然明面上還是 用查詢資料 去查資料庫
但我一開始就把 資料庫 跟 查詢資料 顛倒過來 建置設計表

原因是
由於L,R本身資料量太少
所以用L,R 去查找比對序號

正常來說 應該要L,R資料特別多
是用序號 去查找比對 L,R資料庫

不過正反查找比對 都可以做
就目前來說 速度還可以

就原理來說 桶表主要放大量資料 速度會比較快
所以才把序號設為 桶表資料庫
否則正常情況下 LR資料量好幾萬筆
就應該要把L,R設為桶表資料庫

另外內附三張圖 便於學習與理解
三維陣列 可以想像成 開了99張的工作表 這樣會比較容易理解

如果資料量還更大
建議直接去學SQL 下語法 會比用excel來的快

另外補充
為什麼會挑第11位 與最後1位 的字元來當基準(這是為了此案而優化的部分)
因為這兩個字元變化較為平均
放在桶裡比較容易分散開來
上述的好處是 ar初始陣列設定大小時 就不用設太大了
只要設2000列 就可以放入了
作者: singo1232001    時間: 2021-7-30 04:46

回復 4# singo1232001


    另外 有個小失誤
關於 module1內的 sub tt() 有一段 我少打了 一行代碼

  For j = 1 To ar(a, 0, 0) - 1
        If TX = ar(a, j, 0) Then
        rw = ar(a, j, 1)  '序號列位
        br(rw, 0) = LR(i, 3)   '工丹號碼
        br(rw, 1) = LR(i, 2)   '工作表名
        br(rw, 2) = rw         '列位
        rw = 0
        Exit For    '<--這行幫我加上去
        End If
    Next

這樣速度會在快一咪咪
作者: singo1232001    時間: 2021-7-30 05:04

本帖最後由 singo1232001 於 2021-7-30 05:16 編輯

回復 5# singo1232001


    因為檔案內有個小失誤 我也順便修正了一下檔案
    我把檔案重傳一次
作者: samwang    時間: 2021-7-30 09:14

回復 1# fantersy

請測試看看,謝謝

Sub test()
Dim Arr, xD, i&, Tm
Tm = Timer
Set xD = CreateObject("Scripting.Dictionary")
Arr = Range([L!a1], [L!l65536].End(3))
For i = 2 To UBound(Arr)
    xD(Arr(i, 10)) = Arr(i, 1)
    xD(Arr(i, 12)) = Arr(i, 1)
Next
Arr = Range([R!b1], [R!m65536].End(3))
For i = 2 To UBound(Arr)
    xD(Arr(i, 10)) = Arr(i, 1)
    xD(Arr(i, 12)) = Arr(i, 1)
Next
Arr = Range([序號!a2], [序號!a65536].End(3))
For i = 1 To UBound(Arr)
    Arr(i, 1) = xD(Arr(i, 1))
Next
Sheets("序號").[b2].Resize(UBound(Arr)) = Arr
MsgBox Timer - Tm
End Sub
作者: 准提部林    時間: 2021-7-30 11:58

區間比對, 沒有好方法, 迴迴圈吧!!!
若Barcode確定是由小而大排序的話, 可以減少判斷幾次~~
[attach]33802[/attach]
作者: samwang    時間: 2021-7-30 12:54

本帖最後由 samwang 於 2021-7-30 12:56 編輯

回復 1# fantersy


看了准大的答案,才知道誤會樓主的需求了,正確需求要區間比對,
所以請忽略7樓程式,謝謝
作者: fantersy    時間: 2021-7-30 14:31

回復 8# 准提部林


    感謝 准大 解惑
看來真的只有用迴圈了

我原本的程式判讀 只要符合區間 使用
Exit For  跳出內迴圈
看到 准大 的程式 也是個不錯的方式
再次感謝喔!!
作者: fantersy    時間: 2021-7-30 14:33

回復 3# singo1232001


    函數的部分 我有使用過
只不過這是我舉例範例 數量只有2萬多筆
若筆數越多 用函數  電腦會越吃力
後來就放棄用函數了 ><""
作者: 准提部林    時間: 2021-7-31 09:22

另一類方法, 字典中的字典:
[attach]33803[/attach]

以Barcode前七碼分別建立字典, 記憶陣列的行位置, 以減少不必要的迴圈,
注意:Barcode同行的開始碼及結束碼的前七碼必須相同(若不行, 可改為前六碼), 儘量可做到分類!!!
作者: singo1232001    時間: 2021-7-31 09:53

回復 12# 准提部林


准大這個字典 搭配 分割 的做法實在太好了
又學到一招
比我切那個99桶方便多了
記憶體分配不大
而且可以很隨機的切成想要的字元區間
看來上萬筆資料的篩選也多了一招更方便的方式
完全體會到鏈表的好用之處
而且也學到條碼文字比大小的特殊案例
一魚兩吃 非常感謝

另外想請教准大一個概念上的問題
目前我使用陣列去切 是不是只剩下
"資料庫有多筆相同資料的情況,並且都要找出來"才會用得上了
(不過想想),字典碰上重複 ,但key值應該也可以把資料 &","& 起來 或者 &" "&起來

目前秉持實驗與學習的精神
小弟也把修改後實驗結果呈上 速度慢了些 要0.6秒
作者: singo1232001    時間: 2021-7-31 10:09

回復 13# singo1232001


    剛剛發現 字典下面還可以再放入字典 不用擔心重複了 這招好用!
    又學到一招!
作者: 准提部林    時間: 2021-7-31 10:27

回復 13# singo1232001


我這大約要2秒左右(老PC-XP+2000),
這是分級包裝的概念...將零件組合成品, 再分級排列,
很好的運用, 讚~~~
作者: singo1232001    時間: 2021-7-31 11:13

回復 15# 准提部林


    准大的檔案 在我的電腦跑0.3秒   比我的檔案快一倍以上
作者: n7822123    時間: 2021-7-31 13:13

本帖最後由 n7822123 於 2021-7-31 13:27 編輯

回復 15# 准提部林

同樣的概念改寫準大程式,但是速度比較慢 (不過還是比純跑迴圈快)

推測需要額外做字串串接拆分 等運算,所以拖慢速度!

純粹用字典崁套,速度真的沒法比,真是厲害!

這概念還蠻容易理解的,像是先建立一個目錄,不用讓電腦每個去找

此做法,我第一個想到的是 ,可以應用在 多層下拉式選單


Sub TEST_AT()
Dim Arr, Brr, Crr(2), xD(2), T$, i&, j%, k, Km%, TM
TM = Timer
Crr(1) = Range([L!m1], [L!a1].Cells(Rows.Count, 1).End(xlUp))
Crr(2) = Range([R!m1], [R!a1].Cells(Rows.Count, 1).End(xlUp))
For j = 1 To 2
    Set xD(j) = CreateObject("Scripting.Dictionary")
    For i = 2 To UBound(Crr(j))
        T = Left(Crr(j)(i, j + 9), 7)
        xD(j)(T) = xD(j)(T) & " " & i
    Next i
Next j
Arr = Range([序號!a1], [序號!a1].Cells(Rows.Count, 1).End(xlUp))
ReDim Brr(1 To UBound(Arr), 1 To 2)
Brr(1, 1) = "工單號碼": Brr(1, 2) = "位置"
For i = 2 To UBound(Arr)
    T$ = Left(Arr(i, 1), 7): Km = 0
    For j = 1 To 2
        For Each k In Split(Trim(xD(j)(T)))
            k = Val(k)
            If Crr(j)(k, 9 + j) <= Arr(i, 1) And Crr(j)(k, 11 + j) >= Arr(i, 1) Then
               Brr(i, 1) = Crr(j)(k, j): Brr(i, 2) = Mid("LR", j, 1) & k
               Km = 1: Exit For
            End If
        Next
        If Km = 1 Then Exit For
j01: Next j
i01: Next i
[序號!b1].Resize(UBound(Brr), 2) = Brr
MsgBox Timer - TM
End Sub

作者: n7822123    時間: 2021-7-31 13:24

本帖最後由 n7822123 於 2021-7-31 13:27 編輯

回復 14# singo1232001

物件理論上是可以無限崁套的~~~

可以玩玩 "物件類別模組",自己創造物件,並且崁套看看~

作者: 准提部林    時間: 2021-7-31 13:57

回復 17# n7822123


這是我原來的方法,
但,,,'
1) 若行數太多, 連接字串可能超過長度, 尤其超過十萬行時, 每個數字都是六個字元
2) 連接字串本來就比較慢, 何況後面還要拆解一次

不過, 字典篏字典, 也不知可以容納多少個, 記憶體會不會掛???
沒有實測過~~~~
作者: singo1232001    時間: 2021-7-31 20:37

本帖最後由 singo1232001 於 2021-7-31 20:52 編輯

回復 19# 准提部林


之前做過測試
vba的字典沒有上限 純粹靠的是記憶體大小
只不過vba有個bug 在新增監看式的位置 只會顯示最多256個item值
但實際上 257以後沒顯示的 一樣確實有存在!

當初我也被這個bug 騙過 還在猶豫是否要繼續學字典 後來還好虛驚一場

這樣又讓我想起一條新出路
如果沒錯 資料量在更加龐大的情況 甚至字典 跟 陣列 做排序資料 還可以交互運用找到平衡點
字典嵌字典這招真的非常強大 應該可以玩很久!
想來測試一下 陣列裡面可以不可以嵌字典 如果可以的話 可玩性又高出許多
(看來是可以的,不過前面要加上set )
Sub ttt()
Dim ar
ReDim ar(5)
Set xD = CreateObject("Scripting.Dictionary")
Set ar(1) = xD
ar(2) = 123
End Sub



目前正在學習 二叉樹之類的遞迴方式 希望未來能實際應用上@@
這是在網路找到的資料 他展示了 兩種方式  遞迴 與 非遞迴的 排序方式 與前.中.後序遍歷
(簡體文 不排斥再看)
https://mp.weixin.qq.com/s?__biz=MzA3NTMzMjMyOA==&mid=2650919340&idx=1&sn=e5cf778df2f26e340fd3469d7a60785f&chksm=84875e2bb3f0d73d45172e70a3f202e404cbd21cbd4d26ad657c6dc9969fdf2eb6ab94cb9e65&scene=21#wechat_redirect
上面是第22篇
他還有第21篇跟 23篇
走投無路才找到的資料 哈哈(當初不知道如何下關鍵字)
作者: singo1232001    時間: 2021-8-2 08:56

本帖最後由 singo1232001 於 2021-8-2 09:01 編輯

回復 19# 准提部林


保持實驗學習精神
跟准大學到好多字典寶貴的技巧

最後回頭繼續測試
原來先轉陣列還可以在加速

目前也把時間壓縮成功!
順便奉上檔案參考

另外也測試了 陣列與字典的適用區間 真的好好玩!
作者: ML089    時間: 2021-8-2 23:50

我測試了一下,大部分L跟R都有符合的BarCode啟始及結束條件的工單號碼,
所以L先找或R先找會有不同的答案?

例如
   BarCode              符合位置
A2 CYR128401TG0WLX2H        R42,L38
A3 CYR128401600WLX2U        R42,L3,L38
作者: singo1232001    時間: 2021-8-3 18:14

本帖最後由 singo1232001 於 2021-8-3 18:17 編輯

回復 22# ML089


如果是指我的這份檔案的話  "3維陣列工單查詢 v4 v1"
執行後的結果有異常 有漏缺資料
是我的vba裡面有bug

Sub tttt()
t = Timer
Dim L As Worksheet
Dim R As Worksheet
Dim S As Worksheet

Dim i%, j%, w%, rw%, a%
Dim x As String
Dim TX As String


Set L = Sheets("L")
Set R = Sheets("R")
Set S = Sheets("OP")
f = S.Columns("A").Find("*", , -4163, , 1, 2).Row
f0 = L.Columns("A").Find("*", , -4163, , 1, 2).Row
f1 = R.Columns("A").Find("*", , -4163, , 1, 2).Row

ss = S.Range("A2").Resize(f, 1)
SL = L.Range("A2").Resize(f0, 12)
SR = R.Range("B2").Resize(f1, 12)
...

紅色的地方我打錯了 應該是R
狀況是 會有許多資料沒抓到
昨天晚上才發現的
附上修改好的檔案
作者: fantersy    時間: 2021-8-3 20:02

字典這一塊  我真的很不熟
最近才開始接觸
准大真的太強了!!每次發問都學到不少
感謝幫忙
作者: ML089    時間: 2021-8-3 20:22

回復 23# singo1232001

誤會了! 不是說你程式的問題

我作全比對L R兩個資料表,大部分這兩組資料表裡都有符合的位置,
因為大家的程式都是先比查詢L表在查詢R表,
我在練習時將查詢順序改為先比查詢R表在查詢L表,答案跟大家不同
所以發現資料去比對 起始BarCode 跟 結束BarCode,在L R表都有答案,滿奇怪的。

Sub LR全部比對()
    Dim LR(1 To 2), SN
    TM = Timer
    Set SN = Sheets("序號")
    LR(1) = [L!A1:L1].Resize([L!J65536].End(xlUp).Row)
    LR(2) = [R!B1:M1].Resize([R!J65536].End(xlUp).Row)
    Arr = SN.[A1].Resize(SN.[A65536].End(xlUp).Row, 3)
    ReDim Brr(1 To UBound(Arr), 1 To 4)
    Brr(1, 1) = "工單號碼": Brr(1, 2) = "LR位置": Brr(1, 3) = "起始BarCode": Brr(1, 4) = "結束BarCode"
    For i = 2 To UBound(Arr)
        Key = "": Wno = ""
        For j = 1 To 2
            For k = 2 To UBound(LR(j))
                If LR(j)(k, 10) <= Arr(i, 1) And Arr(i, 1) <= LR(j)(k, 12) Then
                    'Brr(i, 1) = LR(j)(k, 1)
                    'Brr(i, 2) = LR(j)(k, 3) & k
                    'Km = 1: Exit For
                    
                    Brr(i, 1) = Brr(i, 1) & "," & LR(j)(k, 1)      'L R 工單號碼
                    Brr(i, 2) = Brr(i, 2) & "," & LR(j)(k, 3) & k  'L R 位置
                    Brr(i, 3) = Brr(i, 3) & "," & LR(j)(k, 10) & k 'L R 起始Barcode
                    Brr(i, 4) = Brr(i, 4) & "," & LR(j)(k, 12) & k 'L R 結束Barcode
                End If
            Next
            'If Km = 1 Then Km = 0: Exit For
        Next
    Next
    SN.[D1].Resize(UBound(Arr), 4) = Brr
    SN.Range("D2,E3,F2,G2").Columns.AutoFit
    Debug.Print Timer - TM
End Sub
作者: singo1232001    時間: 2021-8-3 21:03

回復 25# ML089


    確實跟你講的一樣 有重複
作者: Andy2483    時間: 2022-7-19 15:19

回復 12# 准提部林


    請教前輩 範例中的判斷式 以字串比大小! 是以什麼規則比大小或等於?
If Crr(j)(k, 9 + j) <= Arr(i, 1) And Crr(j)(k, 11 + j) >= Arr(i, 1) Then

自己有測試,但是無法了解規則!顛覆後學的認知! 以為只有數字才能比較!
Sub TEST_20220719()
If [A2] > [A3] Then
   MsgBox "[A2] > [A3]"
   Else
      MsgBox "[A2] <= [A3]"
End If
End Sub


您的範例完整內容如下:
Sub TEST_A2()
Dim Arr, Brr, Crr(2), xD(2), T$, i&, j%, k, Km%, TM
TM = Timer
Crr(1) = Range([L!m1], [L!a1].Cells(Rows.Count, 1).End(xlUp))
Crr(2) = Range([R!m1], [R!a1].Cells(Rows.Count, 1).End(xlUp))
For j = 1 To 2
    Set xD(j) = CreateObject("Scripting.Dictionary")
    For i = 2 To UBound(Crr(j))
        T = Left(Crr(j)(i, j + 9), 7)
        If Not IsObject(xD(j)(T)) Then
           Set xD(j)(T) = CreateObject("Scripting.Dictionary")
        End If
        xD(j)(T)(i) = ""
    Next i
Next j
Arr = Range([序號!a1], [序號!a1].Cells(Rows.Count, 1).End(xlUp))
ReDim Brr(1 To UBound(Arr), 1 To 2)
Brr(1, 1) = "工單號碼": Brr(1, 2) = "位置"
For i = 2 To UBound(Arr)
   T$ = Left(Arr(i, 1), 7)
   Km = 0
   For j = 1 To 2
      If Not IsObject(xD(j)(T)) Then
         GoTo 666
      End If
      For Each k In xD(j)(T).keys
         If Crr(j)(k, 9 + j) <= Arr(i, 1) And Crr(j)(k, 11 + j) >= Arr(i, 1) Then
            Brr(i, 1) = Crr(j)(k, j)
            Brr(i, 2) = Mid("LR", j, 1) & k
            Km = 1
            Exit For
         End If
      Next
      If Km = 1 Then
         Exit For
      End If
     
666
   Next j
Next i
[序號!b1].Resize(UBound(Brr), 2) = Brr
MsgBox Timer - TM
End Sub

謝謝 前輩指導!
作者: Andy2483    時間: 2022-7-19 15:30

回復 12# 准提部林


    前輩不好意思!
您原本的程式碼太精簡了!後學整理成看得懂的!
所以樓上說是您原本的程式碼!其實是後學有編輯過的!
對不起!
作者: 准提部林    時間: 2022-7-24 16:57

回復 27# Andy2483

好久沒上來了!!!

文字比對....如同直接排序, 是可以的,
但若有數字為開頭, 必須"字元長度"要相同才會準確,
如: 100A35, 2A46 必須是 100A35 002A46, 2才會排在上面
作者: Andy2483    時間: 2022-7-25 14:07

回復 29# 准提部林


    謝謝前輩指導!




歡迎光臨 麻辣家族討論版版 (http://forum.twbts.com/)