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,763
Được thích
5,719
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: 57
Có gì mà ngấm.
Tạo tập tin mới có UserForm với 2 ListBox (có 2 cột). Dữ liệu như trên hình, code cũng như trong hình.

Tại sao tại (A) dùng LIST mà ở (B) lại dùng COLUMN?

Mảng Arr tại (A) có dòng cột y như cần phải có trong ListBox1 nên để load vào ListBox1 thì phải dùng LIST.

Mảng Arr tại (B) có dòng cột "xoay 90 độ" so với cần phải có trong ListBox2 nên để load vào ListBox2 thì phải dùng COLUMN.

Thường mảng Arr "xoay 90 độ" so với nhu cầu cần phải có được tạo bởi code và từng "ô" của nó được điền bằng code từ những giá trị nào đó. Khi đó những người không biết dùng COLUMN thì họ dùng
Mã:
ListBox2.List = Application.WorksheetFunction.Transpose(Arr)

Nhưng do nhiều khi Transpose có lỗi nên để dùng LIST họ phải tạo mảng mới ketqua từ Arr rồi dùng LIST

View attachment 265446
Hình như sau một hồi đọc lại nhiều lần thì cháu đã hiểu một chút về LIST và COLUMN rồi thì phải.
Túm lại khi lấy dữ liệu từ hình (A) và (B) nếu sử dụng chung một thuộc tính LIST hoặc COLUMN, thì một trong 2 sẽ phải dùng Transpose, nếu không muốn dùng Transpose thì phải sử dụng cả 2 thuộc tính này ứng với mỗi bảng, cháu hiểu là vậy cảm ơn chú.
Chú hướng dẫn rất có tâm, cháu chẳng biết gì mà còn hiểu được như thế này nếu với những người biết hoặc hơi biết thì có lẽ quá rõ rồi.
 
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.
Ra là như vậy, không tưởng là sài listbox trên sheets trong việc tìm kiếm lại phát sinh nhiều vấn đề lớn vậy. Nếu hợp nhất được thì tuyệt vời
 
Upvote 0
Cách 2: Mở file bằng ADODB:
Anh cho em hỏi là dùng ADO này thì không lấy dữ liệu từ file excel đang Protect workbook for structure thì phải. Có cách nào khắc phục được không anh?
Một vấn đề nữa là hay bị trường hợp không mở ngầm, mà lại mở lên dưới dạng ReadOnly luôn anh ạ, Code vẫn chạy bình thường nhưng gây khó chịu với mất thời gian tắt đi thôi anh. Anh giúp em với!
 
Upvote 0
Anh cho em hỏi là dùng ADO này thì không lấy dữ liệu từ file excel đang Protect workbook for structure thì phải. Có cách nào khắc phục được không anh?
Một vấn đề nữa là hay bị trường hợp không mở ngầm, mà lại mở lên dưới dạng ReadOnly anh ạ, Code vẫn chạy bình thường nhưng gây khó chịu với mất thời gian tắt đi thôi anh. Anh giúp em với!
1. Tôi chưa thử với trường hợp protect workbook. Để tôi thử xem thế nào rồi thông tin lại.
2. Tôi dùng ADODB khá nhiều nhưng chưa bao giờ gặp nó mở file lên mà thấy được cả. Còn chưa hiểu cái Read Only của bạn có phải thấy được ở trạng thái ReadOnly không? Nếu đúng vậy bạn gửi tôi file nguồn và code bạn dùng để tôi xem thử mới biết được.
 
Upvote 0
1. Tôi chưa thử với trường hợp protect workbook. Để tôi thử xem thế nào rồi thông tin lại.
2. Tôi dùng ADODB khá nhiều nhưng chưa bao giờ gặp nó mở file lên mà thấy được cả. Còn chưa hiểu cái Read Only của bạn có phải thấy được ở trạng thái ReadOnly không? Nếu đúng vậy bạn gửi tôi file nguồn và code bạn dùng để tôi xem thử mới biết được.
Ở bài #105 hình như nói rằng "mình tự mở file".
ADO ngày xưa có cái bug là truy vấn file đang mở có thể bị kẹt file và nếu bộ nhớ yếu thì có thể bị treo máy (chỉ "có thể" thôi, không phải lúc nào cũng xảy ra). Bug này tôi đã đề cập trước đây khá lâu.
Hình như qua Office 1013 thì Microsoft đã khắc phục vấn đề này.
 
Upvote 0
1. Tôi chưa thử với trường hợp protect workbook. Để tôi thử xem thế nào rồi thông tin lại.
2. Tôi dùng ADODB khá nhiều nhưng chưa bao giờ gặp nó mở file lên mà thấy được cả. Còn chưa hiểu cái Read Only của bạn có phải thấy được ở trạng thái ReadOnly không? Nếu đúng vậy bạn gửi tôi file nguồn và code bạn dùng để tôi xem thử mới biết được.
Vậy nhờ anh thử giúp em với ạ. Còn cái code ADO đó em tùy biến từ file của thầy Ndu ở bài viết hồi 2017 hay sao đó ạ. Sau đó em chạy lại file của thầy luôn, nhưng vẫn bị anh.
Theo em thấy thì thường bị khi có file excel khác bất kỳ đang mở, hoặc đang có 1 file được preview ở folder nào đó ạ. sau đó em tắt hết excel và mở lại thì nó lại bình thường anh.
Hình như qua Office 1013 thì Microsoft đã khắc phục vấn đề này.
Em sử dụng excel 2016 ạ, không biết có cách nào không ạ?
Bài đã được tự động gộp:

Nếu đúng vậy bạn gửi tôi file nguồn và code bạn dùng để tôi xem thử mới biết được.
Em dùng thử file của trên GPE luôn thì vẫn có lúc bị ạ
 
Upvote 0
Vậy nhờ anh thử giúp em với ạ. Còn cái code ADO đó em tùy biến từ file của thầy Ndu ở bài viết hồi 2017 hay sao đó ạ. Sau đó em chạy lại file của thầy luôn, nhưng vẫn bị anh.
File Protect Workbook for structure có mật khẩu thì câu: "Extended Properties=""Excel 12.0 Xml;HDR=YES"";"
sửa thành: "Extended Properties=""Excel 12.0 Xml;HDR=YES;PWD=123"";"

(123 là mật khẩu protect)
 
Upvote 0
Anh cho em hỏi là dùng ADO này thì không lấy dữ liệu từ file excel đang Protect workbook for structure thì phải. Có cách nào khắc phục được không anh?
Một vấn đề nữa là hay bị trường hợp không mở ngầm, mà lại mở lên dưới dạng ReadOnly luôn anh ạ, Code vẫn chạy bình thường nhưng gây khó chịu với mất thời gian tắt đi thôi anh. Anh giúp em với!
1. Tôi vừa có thử Protect workbook for structure và import thì vẫn bình thường bạn. không có lỗi gì.
2. Hay trường hợp không mở ngầm? tôi chưa hiểu ý này?
 
Upvote 0
2. Hay trường hợp không mở ngầm? tôi chưa hiểu ý này?
Người hỏi có giải thích trường hợp ở bài #107: có file Excel đang mở.
Theo tôi nhớ thì do lỗi ADO bị chạm resource connection. Bug này ai chơi Resource Consumption qua mấy cái OLE đều biết. Bug này nếu Microsoft chưa khắc phục thì chịu thôi.
 
Upvote 0
1. Tôi vừa có thử Protect workbook for structure và import thì vẫn bình thường bạn. không có lỗi gì.
2. Hay trường hợp không mở ngầm? tôi chưa hiểu ý này?
Với code ở bài #1 thì buộc phải thêm PWD=123 mới chạy được. Còn code kiểu này thì không cần (tôi thực hành thôi chứ không biết lý do)
Rich (BB code):
    Set Rec = CreateObject("ADODB.Connection")
    With Rec
        .Open "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & sFullName & ";Extended Properties=""Excel 12.0;HDR=YES"""
        Sheet3.Range("A2").CopyFromRecordset .Execute("Select TEN, SOLG From [Sheet1$]")
    End With

P/S: sau khi đã cung cấp mật khẩu 1 lần không đóng Excel thì bỏ PWD=123 đi nó chạy được (lý do: không biết), chứ không phải do cú pháp tôi nói ở trên.
 
Upvote 0
Người hỏi có giải thích trường hợp ở bài #107: có file Excel đang mở.
Theo tôi nhớ thì do lỗi ADO bị chạm resource connection. Bug này ai chơi Resource Consumption qua mấy cái OLE đều biết. Bug này nếu Microsoft chưa khắc phục thì chịu thôi.
Hic, vậy là bó tay anh nhỉ? vì vấn đề này mà em cũng đang suy nghĩ chuyển qua Power Query. Sẵn cho em hỏi tốc độ như nào so với ADO nhỉ?.
Em gồm 12 file, mỗi file 1 sheet gồm khoảng 3000 dòng và 9 cột ạ
 
Lần chỉnh sửa cuối:
Upvote 0
Hic, vậy là bó tay anh nhỉ? vì vấn đề này mà em cũng đang suy nghĩ chuyển qua Power Query. Sẵn cho em hỏi tốc độ như nào so với ADO nhỉ?.
Em gồm 12 file, mỗi file 1 sheet gồm khoảng 3000 dòng và 9 dòng ạ
Bạn thử power query xem sao, chắc là sẽ không thất vọng đâu.
Có thể chậm nhưng chắc.
Ngoài ra PQ tổng hợp tính toán ngon lành.
 
Upvote 0
Bạn thử power query xem sao, chắc là sẽ không thất vọng đâu.
Có thể chậm nhưng chắc.
Ngoài ra PQ tổng hợp tính toán ngon lành.
Tôi mà làm việc với dữ liệu lớn, cần PQry, PBi thì tôi học ngay. Bạn @Haffaz Aladeen có lẽ nên học là vừa.

Tính tôi là học mà không thấy trước mắt được gì thì tôi không học được (chứ không phải là không thèm học)
..................................................................................................... $$$$@
 
Upvote 0
Tôi mà làm việc với dữ liệu lớn, cần PQry, PBi thì tôi học ngay. Bạn @Haffaz Aladeen có lẽ nên học là vừa.

Tính tôi là học mà không thấy trước mắt được gì thì tôi không học được (chứ không phải là không thèm học)
..................................................................................................... $$$$@
Trước mắt là bạn có thể bổ sung thêm một cách import dữ liệu ngay tại topic này đó thôi.
PQ là 1 tool của excel, thử đọc tài liệu của sư phụ @ptm0412 phần cơ bản là giải quyết được nhiều thứ lắm rồi.
 
Upvote 0
...

Tính tôi là học mà không thấy trước mắt được gì thì tôi không học được (chứ không phải là không thèm học)
..................................................................................................... $$$$@
Nếu bạn thích code VBA thì cứ giữ vậy.
Nếu bạn muốn bước qua lĩnh vực Query thì nên học cái nhóm Power BI.
 
Upvote 0
Trước mắt là bạn có thể bổ sung thêm một cách import dữ liệu ngay tại topic này đó thôi.
PQ là 1 tool của excel, thử đọc tài liệu của sư phụ @ptm0412 phần cơ bản là giải quyết được nhiều thứ lắm rồi.
Tôi nói là đời thực bạn ơi. Tôi học được VBA là do nó liên quan đến công việc, cơm áo gạo tiền của tôi. Việc đến đâu học đến đó thôi chứ không nâng tầm được. Nên chừ thì có gì xài nấy thôi (may mà còn có thứ để xài --=0 )
 
Upvote 0
Tôi nói là đời thực bạn ơi. Tôi học được VBA là do nó liên quan đến công việc, cơm áo gạo tiền của tôi. Việc đến đâu học đến đó thôi chứ không nâng tầm được. Nên chừ thì có gì xài nấy thôi (may mà còn có thứ để xài --=0 )
GPE này rất thực mà bạn.
PQ không đơn thuần là lấy dữ liệu về. PQ có thể tính toán, tổng hợp, group by, insert thêm trường… nếu dùng ADO chắc sẽ mệt lắm.
 
Upvote 0
GPE này rất thực mà bạn.
PQ không đơn thuần là lấy dữ liệu về. PQ có thể tính toán, tổng hợp, group by, insert thêm trường… nếu dùng ADO chắc sẽ mệt lắm.
Đúng như bạn nói, GPE rất thực. Tôi học được rất nhiều từ từ GPE và nó làm tư duy lập trình của tôi chuyển biến rất nhiều. Khi bập phải một yêu cầu gì đó tôi lại phải tự học, uốn nắn tư duy để tìm giải thuật. Biết đâu 1 ngày nào đó tôi lại hứng thú với P.BI thì sao? Chưa biết được.
 
Lần chỉnh sửa cuối:
Upvote 0
Đúng như bạn nói, GPE rất thực. ....
Ưu điẻm của 'thực' là nó ... thực.
Khuyết điểm của 'thực', trong môi trường làm việc ở VN, là nó quá cứng nhắc theo guồng máy của VN.

Tôi đã từng nhiều lần nhắc nhỏ, như cái đĩa hát cũ, là làm việc kiểu này là chạy theo dữ liệu chứ không làm chủ nó. (*1)

BI là Business Intelligence, nó ngầm chứa điều kiện trích xuất và phân tích dữ liệu theo dạng cube (nhiều mặt, đa chiều). Dữ liệu chứa trong bảng tính Excel là một mặt, hai chiều, những cái vặn vẹo qua VBA chỉ tối đa giúp cho lắp thêm vây cánh cho Excel một cách cứng nhắc. Code VBA có thể thêm mặt nhìn, thêm chiều, nhưng muốn nó uyển chuyển thì cái project VBA phải lớn tổ bố. Viết cái project tổ bố thì thà học Power Query khoẻ hơn.

(*1) nếu bạn chỉ vì cơm gạo thì thoi. Nhưng nếu muốn có chút thoải mái (tôi tránh dùng từ 'đam mê') thì làm chủ được dữ liệu sẽ thấy cái đẹp, cái hay của chúng, và sẽ thấy thoải mái với công việc thiết kế những dạng bảng tính 'xịn'.
 
Upvote 0
Web KT

Bài viết mới nhất

Back
Top Bottom