So sánh 3 cách lấy dữ liệu từ 1 file đang đóng

Liên hệ QC

Maika8008

Thành viên gạo cội
Tham gia
12/6/20
Bài viết
4,741
Được thích
5,669
Donate (Momo)
Donate
Giới tính
Nam
Với Excel chúng ta thường có nhu cầu lọc lựa, lấy dữ liệu từ 1 file đang trạng thái đóng đã biết trước đường dẫn và địa chỉ tham chiếu vùng dữ liệu, nhưng lại không muốn tự tay mở nó ra để chép 1 cách thủ công qua file đang làm. Với VBA, chúng ta có 3 cách để tự động hóa khâu lấy dữ liệu này.

Cách 1: Mở trực tiếp file lên, lấy dữ liệu xong đóng lại:
Rich (BB code):
Sub GetDataByOpenFile()
Dim Wb As Workbook, WbS As Workbook
Dim sFullName$, tmr#

tmr = Timer()
Application.ScreenUpdating = False
sFullName = "D:\GoogleDrive2\CaNhan\VBA\MapVN.xlsx"  'Duong dan file du lieu
Set Wb = ThisWorkbook
Set WbS = Workbooks.Open(sFullName)
WbS.Sheets("VNxy").Range("A1:D100").Copy Wb.Sheets("KQ").Range("A1")
WbS.Close False
Application.ScreenUpdating = True
Msgbox Timer() – tmr  'Thoi gian thuc hien
End Sub
- Ưu điểm của cách 1 là trực quan, ta có thể tạm ngừng lệnh, chạy từng bước để xem kết quả trung gian. Dữ liệu có thế nào chép sang thế ấy hoặc có thể tùy ý chép riêng định dạng, công thức, giá trị…

- Nhược điểm cách 1 là thời gian thực thi khá chậm, mất khoảng 1,4 giây cho việc mở file, chép dữ liệu, đóng file. (Thời gian ở đây là trong điều kiện thử nghiệm cụ thể của tác giả, chỉ để so sánh tốc độ thực hiện các cách với nhau. Thời gian đó sẽ khác đi khi dùng ở máy khác, dùng 1 file nguồn khác…)

Cách 2: Mở file bằng ADODB:
Rich (BB code):
Sub GetDataByADODB()
Dim Rec As Object, rs As Object
Dim sFullName$, iCol&, tmr#

tmr = Timer()
sFullName = "D:\GoogleDrive2\CaNhan\VBA\MapVN.xlsx"   'Duong dan file du lieu
Application.ScreenUpdating = False
Set Rec = CreateObject("ADODB.Connection")
With Rec
        .Provider = "Microsoft.ACE.OLEDB.12.0"
        .ConnectionString = "Data Source=" & sFullName & ";" & _
              "Extended Properties=""Excel 12.0 Xml;HDR=YES"";"
        .Open
End With
        Set rs = Rec.Execute("Select * From [VNxy$A1:D100]")
        Sheets("KQ").Range("A2").CopyFromRecordset rs
        For iCol = 0 To rs.Fields.Count - 1   'Chep tieu de cọt
                Sheets("KQ").Cells(1, iCol + 1).Value = rs.Fields(iCol).Name
        Next
        Set rs = Nothing
        Msgbox Timer() – tmr  'Thoi gian thuc hien
        Application.ScreenUpdating = True
End Sub
- Ưu điểm của cách 2 là thực thi nhanh, chỉ mất cỡ 0,2 giây, và người dùng không nhận biết được file đóng mở.

- Nhược điểm cách 2 là:

+ Trong đa số trường hợp phải mất công khắc phục việc chạy lệnh đối với tiêu đề có dấu tiếng Việt (Có cách khác không bị ảnh hưởng bởi tiêu đề tiếng Việt. Nhưng vấn đề này sẽ được trình bày ở 1 bài khác, có sự so sánh việc sử dụng của 2 cách).

+ Những ai chưa rành cú pháp SQL và lồng các biến vào câu lệnh SQL có thể gặp trở ngại khi điều kiện truy vấn phức tạp.

+ Gặp dữ liệu có dấu phân cách thập phân với các máy đã được dịnh dạng trong Control Panel là dấu phẩy thì sẽ có rắc rối với kết quả chép ra. Thay vì đúng là 144,12 thì kết quả chép ra ở đây là 14412,00.

Để khắc phục nhược điểm này thì phải thêm mã lệnh để chuyển định dạng dấu phẩy thập phân trong Control Panel sang dấu chấm trước khi chạy truy vấn rồi chuyển lại dấu phẩy ngay sau truy vấn.

Khai báo và đặt hàm SetLocalSetting trên đầu Module:
Rich (BB code):
#If VBA7 Then

 Private Declare PtrSafe Function SetLocaleInfo _

 Lib "kernel32" Alias "SetLocaleInfoA" ( _

 ByVal Locale As LongPtr, _

 ByVal LCType As LongPtr, _

 ByVal lpLCData As String) As Boolean

 

 Private Declare PtrSafe Function GetUserDefaultLCID% Lib "kernel32" ()

#Else

 Private Declare Function SetLocaleInfo _

 Lib "kernel32" Alias "SetLocaleInfoA" ( _

 ByVal Locale As Long, _

 ByVal LCType As Long, _

 ByVal lpLCData As String) As Boolean

 

 Private Declare Function GetUserDefaultLCID% Lib "kernel32" ()

#End If

 

 Private Const LOCALE_SDECIMAL = &HE

 

 Private Function SetLocalSetting(LC_CONST As Long, Setting As String) As Boolean

 Call SetLocaleInfo(GetUserDefaultLCID(), LC_CONST, Setting)

 End Function
Với Sub GetDataByADODB() ở trên, thêm lệnh này trước lệnh truy vấn SQL:

Call SetLocalSetting(LOCALE_SDECIMAL, ".")

Và thêm lệnh này ngay sau khi chạy xong truy vấn SQL:

Call SetLocalSetting(LOCALE_SDECIMAL, ",")



Cách 3: Dùng Macro 4. Cách này tôi dùng code từ nguồn:

https://www.giaiphapexcel.com/diendan/threads/dùng-macro-4-để-lấy-dữ-liệu-từ-1-file-đang-đóng.39312/
Rich (BB code):
Sub GetDataByMacro4()
Dim tmr#
tmr = Timer()
  Dim sFile As String, sSheet As String, sAddr As String
  sFile = "D:\GoogleDrive2\CaNhan\VBA\MapVN.xlsx"
  sSheet = "VNxy"
  sAddr = "A1:D100"  
  Sheets("KQ").Range("A1:D100") = GetData(sFile, sSheet, sAddr)
'Kích thuoc phai bang sAddr
  Msgbox Timer() – tmr  'Thoi gian thuc hien
End Sub

Function GetData(sFile As String, sSheet As String, sAddr As String)
      Dim pLink As String, iR As Long, iC As Long, Arr
      If Len(Dir(sFile)) Then
            Arr = Range(sAddr)
            pLink = "'" & Replace(sFile, Dir(sFile), "[" & Dir(sFile) & "]") & sSheet & "'!"
            For iR = 1 To Range(sAddr).Rows.Count
                  For iC = 1 To Range(sAddr).Columns.Count
                        Arr(iR, iC) = ExecuteExcel4Macro(pLink & Range(sAddr). _
Cells(iR, iC).Address(, , 2))
                  Next iC
            Next iR
            GetData = Arr
      End If
End Function
Với cách này, dù dữ liệu nguồn sAddr = "A1:D100" nhưng khi muốn hiển thị kết quả ít hơn thì bạn giảm kích thước vùng kết quả chỗ Sheets("KQ").Range("A1:D100"). Nếu tăng kích thước vùng kết quả thì sẽ bị #N/A ở các ô thừa.

- Thực tình thì không thấy ưu điểm nào của cách này ngoài việc nó lấy đúng số liệu nguồn như cách 1. Còn nhược điểm là quá chậm, phải đến tận 5 giây (gấp hơn 3 lần cách 1) thì mới lấy được dữ liệu cùng cỡ với các cách trên. Dòng tiêu đề sẽ hiển thị là 0 với tiêu đề nào chừa trống.

P/S: Tôi đính kèm file dữ liệu mà tôi đã dùng để test tốc độ thực thi code để các bạn có cùng 1 mẫu thử.
Trong bài có chỗ nào sai, sót thì các bác "gội" rồi mới "cạo" nhẹ nhàng góp ý giúp.
 

File đính kèm

  • MapVN.xlsx
    660.4 KB · Đọc: 55
Cách này hay đấy, sẽ bỏ được một vòng lặp, tốc độ vì thế sẽ cải thiện, dùng code sẽ phê hơn bình thường.
Nói về lọc dữ liệu trên ComboBox bằng array để tìm mục nào đó thì mình mới phát minh ra một kiểu lọc mà từ trước tới nay chưa một ai có thuật toán như mình, nó đảm bảo nhanh từ bằng đến gấp đôi gấp ba và nhiều hơn thế nữa nếu cứ tăng 1 ký tự! Nhưng mình đang hoàn thiện sẽ có dịp mình tặng cho mọi người cùng thưởng thức!
 
Upvote 0
Nói về lọc dữ liệu trên ComboBox bằng array để tìm mục nào đó thì mình mới phát minh ra một kiểu lọc mà từ trước tới nay chưa một ai có thuật toán như mình, nó đảm bảo nhanh từ bằng đến gấp đôi gấp ba và nhiều hơn thế nữa nếu cứ tăng 1 ký tự! Nhưng mình đang hoàn thiện sẽ có dịp mình tặng cho mọi người cùng thưởng thức!
Chia luôn để mọi người sẻ xem thế nào anh Nghĩa đẹp trai ơi.
 
Upvote 0
So sánh vẫn luôn là so sánh thôi và rất dễ bị tráo khái niệm
Nếu đã so sánh thì phải đồng (cùng ) nhiều thứ:
- Cùng File Dữ liệu
- Cùng File kết quả
- Cùng môi trường kết quả cuối cùng (file kết quả là đang mở hay là không mở ở Excel)
vv

Còn nếu chuyển qua môi trường khác như CSV, TextFile -- thì phải coi đó là bước trung gian, cần kể cả thời gian chuyển tự động qua bước trung gian đó.

Tóm lại, ai quen môi trường nào thì làm ở môi trường đó, và quy mô File cấu trúc File dữ liệu gốc , cũng như kết quả sẽ quyết định nên chọn cái nào: Chân phương, hay là tắt, lắt léo, hay sử dụng công cụ (Tool) khác ...
 
Upvote 0
Vậy thì khá rắc rối, thôi thì cứ bê nguyên cái dữ liệu theo điều kiện mình đặt ra, sau đó nếu cần gõ chữ nào thì lọc chữ đó cho nó lành. Phàm cái gì khó quá thì bỏ qua cho khỏe.

P/s: Mà cứ gõ ký tự lại kết nối với CSDL phải chăng là nó quá lâu hơn so với lọc qua mảng không?
Cách này tôi làm không phải dạng "tìm kiếm ngay khi gõ" (OnChange) mà tìm sau khi gõ xong (AfterUpdate).
Bên cạnh đó thì ADO Recordset cũng đã tải và nằm trong bộ nhớ rồi, chỉ cần Filter nó ra hoăc xoá Filter để trả lại Recordset nguyên vẹn, không cần kết nối, tải lại Recordset.
Cái vụ ADO recordset Filter có trong loạt bài của bạn HLMT.
 
Upvote 0
Cách này tôi làm không phải dạng "tìm kiếm ngay khi gõ" (OnChange) mà tìm sau khi gõ xong (AfterUpdate).
Bên cạnh đó thì ADO Recordset cũng đã tải và nằm trong bộ nhớ rồi, chỉ cần Filter nó ra hoăc xoá Filter để trả lại Recordset nguyên vẹn, không cần kết nối, tải lại Recordset.
Cái vụ ADO recordset Filter có trong loạt bài của bạn HLMT.
Tìm kiếm sau khi gõ thì gõ mệt nghỉ, còn mình chỉ gõ vài key là nó show ra cho mình chọn, đỡ mất công gõ, giống search trên Google đó thôi.
 
Upvote 0
Nói về lọc dữ liệu trên ComboBox bằng array để tìm mục nào đó thì mình mới phát minh ra một kiểu lọc mà từ trước tới nay chưa một ai có thuật toán như mình, nó đảm bảo nhanh từ bằng đến gấp đôi gấp ba và nhiều hơn thế nữa nếu cứ tăng 1 ký tự! Nhưng mình đang hoàn thiện sẽ có dịp mình tặng cho mọi người cùng thưởng thức!
Trước giờ mình chỉ biết cách lọc trên mảng, không nghĩ là có những cách khác hay hơn, nếu làm được điều này thì sẽ tuyệt vời đối với những danh sách dài vài chục nghìn dòng mà tốc độ vẫn nhanh. ý tưởng code như vậy có nhanh không anh, tầm 1 ngày hoặc 2 ngày có xong được không anh, anh nghĩ code sẽ ngắn chứ?
 
Upvote 0
Trước giờ mình chỉ biết cách lọc trên mảng, không nghĩ là có những cách khác hay hơn, nếu làm được điều này thì sẽ tuyệt vời đối với những danh sách dài vài chục nghìn dòng mà tốc độ vẫn nhanh. ý tưởng code như vậy có nhanh không anh, tầm 1 ngày hoặc 2 ngày có xong được không anh, anh nghĩ code sẽ ngắn chứ?
Vẫn lọc duyệt trên mảng nhưng bằng phương thức khác, code thì dài dòng văn tự, nhưng nó rất nhanh đối với công cụ tìm kiếm bằng combobox, bởi cứ mỗi sự kiện change ta gõ 1 ký tự thì nó lọc một công đoạn, cho nên nó sẽ rất nhanh so với các kiểu duyệt lọc thông thường khác.
 
Upvote 0
Vẫn lọc duyệt trên mảng nhưng bằng phương thức khác, code thì dài dòng văn tự, nhưng nó rất nhanh đối với công cụ tìm kiếm bằng combobox, bởi cứ mỗi sự kiện change ta gõ 1 ký tự thì nó lọc một công đoạn, cho nên nó sẽ rất nhanh so với các kiểu duyệt lọc thông thường khác.
Kiểu như dùng nâng cao của mảng? Có cách nào để áp dụng luôn sáng tìm trên textbox và hiện ra trên listbox để có tốc độ cực nhanh?
 
Upvote 0
Kiểu như dùng nâng cao của mảng? Có cách nào để áp dụng luôn sáng tìm trên textbox và hiện ra trên listbox để có tốc độ cực nhanh?
Đừng tìm kiếm bằng TextBox và cho kết quả trên ListBox, tôi nhớ trước đây có tranh luận về vấn đề này rồi, đó là một vấn đề tạo control trên sheet nó phát sinh lỗi (để tìm lại không nhớ nó nằm ở đâu).
Tại sao tôi khuyến khích tìm kiếm trên ComboBox? Bởi vì nó như là một sản phẩm được kết cấu vừa là TextBox và ListBox kết hợp lại thành một cho nên sử dụng nó như sử dụng 2 control mà nó sẽ không bị lỗi. Nó chỉ khác ListBox một chỗ là không được chọn nhiều mục một lúc.
 
Upvote 0
Đừng tìm kiếm bằng TextBox và cho kết quả trên ListBox, tôi nhớ trước đây có tranh luận về vấn đề này rồi, đó là một vấn đề tạo control trên sheet nó phát sinh lỗi (để tìm lại không nhớ nó nằm ở đâu).
Tại sao tôi khuyến khích tìm kiếm trên ComboBox? Bởi vì nó như là một sản phẩm được kết cấu vừa là TextBox và ListBox kết hợp lại thành một cho nên sử dụng nó như sử dụng 2 control mà nó sẽ không bị lỗi. Nó chỉ khác ListBox một chỗ là không được chọn nhiều mục một lúc.
A Nghĩa mở thớt mới thôi anh.
Một topic hay, có lẽ cắt hết các bài liên quan sang, để không ảnh hưởng đến bài của tác giả.
 
Upvote 0
A Nghĩa mở thớt mới thôi anh.
Một topic hay, có lẽ cắt hết các bài liên quan sang, để không ảnh hưởng đến bài của tác giả.
Do bạn đó hỏi thì mình trả lời, nhưng vấn đề lọc do mình phát minh chưa hoàn thiện nên không mở topic mới được. Mà thôi, không nói về nó ở đây nữa vậy.
 
Upvote 0
Nói về lọc dữ liệu trên ComboBox bằng array để tìm mục nào đó thì mình mới phát minh ra một kiểu lọc mà từ trước tới nay chưa một ai có thuật toán như mình, nó đảm bảo nhanh từ bằng đến gấp đôi gấp ba và nhiều hơn thế nữa nếu cứ tăng 1 ký tự! Nhưng mình đang hoàn thiện sẽ có dịp mình tặng cho mọi người cùng thưởng thức!

Lúc trước tôi cũng có viết mấy kiểu lọc dữ liệu "của comboBox". Ngoài mục tiêu giúp người dùng tìm nhanh còn có mục tiêu giảm tải lượng dữ liệu truyền tải nếu áp dụng cho các ứng dụng lấy dữ liệu qua internet (SQL Server).




 
Lần chỉnh sửa cuối:
Upvote 0
Lúc trước tôi cũng có viết mấy kiểu lọc dữ liệu "của comboBox". Ngoài mục tiêu giúp người dùng tìm nhanh còn có mục tiêu giảm tải lượng dữ liệu truyền tải nếu áp dụng cho các ứng dụng lấy dữ liệu qua internet (SQL Server).
Thì mình cũng lấy dữ liệu từ Access lên Excel rồi lọc dữ liệu qua ComboBox:

 
Upvote 0
Lúc nào đó phải học mấy bác chiêu này, thấy hay quá
 
Upvote 0
Đừng tìm kiếm bằng TextBox và cho kết quả trên ListBox, tôi nhớ trước đây có tranh luận về vấn đề này rồi, đó là một vấn đề tạo control trên sheet nó phát sinh lỗi (để tìm lại không nhớ nó nằm ở đâu).
Tại sao tôi khuyến khích tìm kiếm trên ComboBox? Bởi vì nó như là một sản phẩm được kết cấu vừa là TextBox và ListBox kết hợp lại thành một cho nên sử dụng nó như sử dụng 2 control mà nó sẽ không bị lỗi. Nó chỉ khác ListBox một chỗ là không được chọn nhiều mục một lúc.
Anh xem thêm về cách lọc bằng textbox và kết quả là listbox Bài này nhé
 
Upvote 0
đoán thôi chứ chưa chắc lắm ... hình như Rs.Filter thuần ADODB nó nhanh hơn cho vào 1 Array xong lọc nó

Nếu lọc = 1 Array data trên Sheet thì may ra nhanh còn Data Ở Files khác chắc thua quá
 
Upvote 0
Chào hai sếp, hai sếp làm ơn có thể cho em xin file kèm này tham khảo được không?
Các sếp nói chuyện với nhau em thấy hay quá nhưng em không hiểu gì hết.
Nếu ý bạn là hỏi về đoạn mà bạn trích của Hai Lúa Miền Tây thì tôi đã viết nhiều lần rồi.

Giả sử bạn có mảng 2 chiều Arr "bình thường" với nghĩa là dòng và cột của nó y hệt như dòng và cột mà bạn cần nhập vào ListBox, ComboBox. Lúc đó bạn nhập mảng đó vào ListBox, ComboBox bằng thuộc tính LIST
Mã:
ListBox1.List = Arr
ComboBox1.List = Arr
Nếu Arr là mảng "đảo ngược", "xoay 90 độ" so với mảng cần nhập vào ListBox, ComboBox thì bạn dùng thuộc tính COLUMN
Mã:
ListBox1.Column = Arr
ComboBox1.Column = Arr
Nhiều người không biết dùng Column nên khi có mảng Arr "xoay 90 độ" thì họ dùng vòng For để từ mảng Arr tạo ra mảng mới vd. ketqua là mảng "bình thường", rồi dùng thuộc tính List để nhập vào ListBox, ComboBox
Mã:
ListBox1.List = ketqua
ComboBox1.List = ketqua
 
Upvote 0
Nếu ý bạn là hỏi về đoạn mà bạn trích của Hai Lúa Miền Tây thì tôi đã viết nhiều lần rồi.

Giả sử bạn có mảng 2 chiều Arr "bình thường" với nghĩa là dòng và cột của nó y hệt như dòng và cột mà bạn cần nhập vào ListBox, ComboBox. Lúc đó bạn nhập mảng đó vào ListBox, ComboBox bằng thuộc tính LIST
Mã:
ListBox1.List = Arr
ComboBox1.List = Arr
Nếu Arr là mảng "đảo ngược", "xoay 90 độ" so với mảng cần nhập vào ListBox, ComboBox thì bạn dùng thuộc tính COLUMN
Mã:
ListBox1.Column = Arr
ComboBox1.Column = Arr
Nhiều người không biết dùng Column nên khi có mảng Arr "xoay 90 độ" thì họ dùng vòng For để từ mảng Arr tạo ra mảng mới vd. ketqua là mảng "bình thường", rồi dùng thuộc tính List để nhập vào ListBox, ComboBox
Mã:
ListBox1.List = ketqua
ComboBox1.List = ketqua
Sếp Nghĩa đã phớt lờ cháu rồi, giờ chỉ chú lả để ý đến cháu, cảm ơn chú rất nhiều.
Cháu sẽ đọc lại thêm mấy lần nữa xem có ngấm thêm được ít nào không.
 
Upvote 0
Sếp Nghĩa đã phớt lờ cháu rồi, giờ chỉ chú lả để ý đến cháu, cảm ơn chú rất nhiều.
Cháu sẽ đọc lại thêm mấy lần nữa xem có ngấm thêm được ít nào không.
Những gì bạn hỏi đều rất cao hơn trình độ của bạn, nếu có đưa bạn xem bạn chỉ ngó rồi để đó. Còn những bài của batman1 còn cao hơn, tôi đọc còn chỉ biết ngó luôn chứ bạn sao đọc nổi!
 
Upvote 0
Những gì bạn hỏi đều rất cao hơn trình độ của bạn, nếu có đưa bạn xem bạn chỉ ngó rồi để đó. Còn những bài của batman1 còn cao hơn, tôi đọc còn chỉ biết ngó luôn chứ bạn sao đọc nổi!
Cảm ơn sếp đã cho em lời khuyên, em tưởng sêp tiếp em xong sợ em bám càng đòi 2 bà Trưng và bà Tân Vlốc.
Em không hiểu nên không hỏi, chỉ xin file kèm xem mục đích khi chạy nó ra làm sao, nếu nó hữu ích cho em thì lúc đó em mới hỏi từng bước.
 
Upvote 0
Web KT
Back
Top Bottom