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,670
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
Tôi đang nói về code trong GetData bạn à. Nó lấy ra mảng bằng cách:
1/ Lấy từ Recordset ra mảng tạm bằng .GetRows.
2/ Dùng 2 vòng lặp để biến cái mảng tạm đấy thành mảng có cấu trúc giống như data nguồn.
uhm, tôi cũng dân ngoại đạo, thực sự thì ngôn ngữ ADO tôi cũng không hiểu sâu, tôi cứ nạp bằng hàm Getdata là lấy data từ File khác (đang đóng) về.
Snag_149024d9.png
 
Upvote 0
Lại dở lý thuyết, dở hoài thành dở lái mất :p:
Vì với đa số ngôn ngữ lập trình, kể cả VBA, Array là loại mảng trọng cột (column major: xếp cột trước, dòng sau) cho nên hàm GetRows của ADO được viết để lấy dữ liệu theo cột. Tức là mỗi dòng mà GetRows đọc trên recordset sẽ được ghi lại vào một cột trong array.
Nếu mảng nhỏ, và dữ liệu không dài lắm thì có thể dùng hàm Transpose của worksheet để xoay mảng. Hình như giới hạn của Transpose là khoảng 400 dòng và mỗi string dưới 256 ký tự.

Nếu lấy dữ liệu trong Recordset nhiều lần thì nên tìm hiểu về cách đặt con trỏ.

Chú: ADO là một đối tượng. Recordset cũng là một đối tượng. GetRows là một phương thức của đối tượng Recordset. Mặt khác, CopyFromRecordSet là một phương thức của Range (Excel). Theo nguyên tắc lập trình hướng đối tượng thì đối tượng có thể biến hình tuỳ theo mọt vài bản chất nào đó. Muốn biết cách hoạt động của đối tượng ra sao thì phải tìm hiểu về thuộc tính và phương thức của chúng.

Chú 2 (thêm vào sau 12 phút): ở bài trước tôi quên nói thẳng rằng <quote>nhu cầu chọn lựa<endquote> theo nguyên tắc lập trình thì sẽ đặt trọng điểm trên điều kiện môi trường. Tuy quên giải thích, nhưng ở bài ấy tôi có đưa thẳng ra phương pháp chọn lựa theo ý cá nhân. Trái với đa số trên GPE, có lẽ gồm cả thớt, theo quan điểm tốc độ. Đó là một quan điểm chọn lựa "thực tế". Thực tế ở đây là sẽ đúng với hầu hết các trường hợp mà quý vị gặp. Chỉ có điều là quý vị cùng có suy nghĩ giống nhau cho nên các điều kiện môi trường và nhu cầu sẽ xếp giống nhau, các mẫu/loại dữ liệu mà quý vị test cũng giống nhau.
Khi gặp một môi trường khác, quý vị sẽ chưng hửng. Nhưng đó là chuyện của mai sau. Mai sau là lý thuyết, hiện tại mới là thực tế.
 
Lần chỉnh sửa cuối:
Upvote 0
Lại dở lý thuyết, dở hoài thành dở lái mất :p:
Vì với đa số ngôn ngữ lập trình, kể cả VBA, Array là loại mảng trọng cột (column major: xếp cột trước, dòng sau) cho nên hàm GetRows của ADO được viết để lấy dữ liệu theo cột. Tức là mỗi dòng mà GetRows đọc trên recordset sẽ được ghi lại vào một cột trong array.
Nếu mảng nhỏ, và dữ liệu không dài lắm thì có thể dùng hàm Transpose của worksheet để xoay mảng. Hình như giới hạn của Transpose là khoảng 400 dòng và mỗi string dưới 256 ký tự.

Nếu lấy dữ liệu trong Recordset nhiều lần thì nên tìm hiểu về cách đặt con trỏ.

Chú: ADO là một đối tượng. Recordset cũng là một đối tượng. GetRows là một phương thức của đối tượng Recordset. Mặt khác, CopyFromRecordSet là một phương thức của Range (Excel). Theo nguyên tắc lập trình hướng đối tượng thì đối tượng có thể biến hình tuỳ theo mọt vài bản chất nào đó. Muốn biết cách hoạt động của đối tượng ra sao thì phải tìm hiểu về thuộc tính và phương thức của chúng.

Chú 2: ở bài trước tôi quên nói thẳng rằng <quote>nhu cầu chọn lựa<endquote> theo nguyên tắc lập trình thì sẽ đặt trọng điểm trên điều kiện môi trường. Tuye uqeen giải thích, nhưng ở bài ấy tôi có đưa thẳng ra phương pháp chọn lựa theo ý cá nhân. Trái với đa số trên GPE, có lẽ gồm cả thớt, theo quan điểm tốc độ. Đó là một quan điểm chọn lựa "thực tế". Thực tế ở đây là sẽ đúng với hầu hết các trường hợp mà quý vị gặp. Chỉ có điều là quý vị cùng có suy nghĩ giống nhau cho nên các điều kiện môi trường và nhu cầu sẽ xếp giống nhau, các mẫu/loại dữ liệu mà quý vị test cũng giống nhau.
Khi gặp một môi trường khác, quý vị sẽ chưng hửng. Nhưng đó là chuyện của mai sau. Mai sau là lý thuyết, hiện tại mới là thực tế.
Hà hà. Tôi có rành lý thuyết đâu bác. Do đó tôi mới đặt vấn đề để có ai đó như bác gợi mở.
---------
"Nếu lấy dữ liệu trong Recordset nhiều lần thì nên tìm hiểu về cách đặt con trỏ.". Ồ! Hóa ra là vậy. Giữa GetRows và CopyFromRecordset tôi chen giữa bằng .MoveFirst thì ngon lành. Cám ơn bác!
 
Upvote 0
Code nạp csv như hình đấy bạn, python nạp excel chậm lắm bạn nếu dữ liệu càng lớn vì file excel nó dạng dữ liệu nén, dữ liệu khoảng vài chục ngàn thì nạp excel được còn lớn hơn thì tốt nhất chuyển qua csv , đọc file thì nó cũng phụ thuộc vào cpu và tốc độ ssd bạn ạ nên máy bạn yếu nạp chậm hơn là bình thường bạn ạ
Nếu python nạp excel chậm thì cũng giống power query a nhỉ
Việc import đơn giản chỉ là lấy dữ liệu về thì ADO nhanh và tiện lợi.
Tuy nhiên nếu cần thêm việc tổng hợp, tính toán, group theo vài tiêu chí thì phải dùng Power Query.
Em cũng chưa biết python là gì, ví dụ việc tổng hợp khoảng 30 file excel (mỗi file tầm 3M) thì python có lợi thế hơn Power Query không anh?
 
Upvote 0
Tùy theo nhu cầu mà ta có thể chọn cách nào đó cho phù hợp. Chủ thớt chỉ xét 1 phần nhỏ khi dùng ADO để so sánh.
 
Upvote 0
Cái hay của Range().CopyFromRecordSet là nó đảo chiều từ hàng thành cột của một mảng từ dữ liệu được xuất ra bằng ADO. Nhưng mình rất thích xử lý trên mảng "đảo ngược" được xuất ra bằng ADO. Các anh (chị) có biết tại sao không?
 
Upvote 0
Cái hay của Range().CopyFromRecordSet là nó đảo chiều từ hàng thành cột của một mảng từ dữ liệu được xuất ra bằng ADO. Nhưng mình rất thích xử lý trên mảng "đảo ngược" được xuất ra bằng ADO. Các anh (chị) có biết tại sao không?
Tôi chỉ biết là nhanh hơn, không qua khâu trung gian.
 
Upvote 0
Còn đoạn: "Thêm nữa (cũng lạ?) đã Sheet3.Range("A2").CopyFromRecordset Rec.Datasource thì sau đó không Getrows được nữa và ngược lại đã Getrows thì CopyFromRecordset không lỗi nhưng không ra kết quả gì cả." là tôi thử nghiệm vào code ADO của tôi ở bài #1: Đã .GetRows thì không CopyFromRecordset được nữa và ngược lại
Khi bạn muốn lấy lại thì bạn phải di chuyển con trỏ về dòng đầu tiên.
Ví dụ nếu bạn đã đổ recordset xuống sheet, nhưng vì lý do nào đó mà bạn muốn đổ nữa hoặc thực hiện điều gì đó thì xem ví dụ như sau:

Rich (BB code):
Sub test_ADO()
    With CreateObject("ADODB.Recordset")
        .Open "Select * from [Sheet1$]", "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & ThisWorkbook.FullName & ";Extended Properties=Excel 12.0"
        Sheet2.Range("A2").CopyFromRecordset .DataSource
        .MoveFirst
        Sheet3.Range("A2").CopyFromRecordset .DataSource
    End With
End Sub
 
Upvote 0
Khi bạn muốn lấy lại thì bạn phải di chuyển con trỏ về dòng đầu tiên.
Ví dụ nếu bạn đã đổ recordset xuống sheet, nhưng vì lý do nào đó mà bạn muốn đổ nữa hoặc thực hiện điều gì đó thì xem ví dụ như sau:

Rich (BB code):
Sub test_ADO()
    With CreateObject("ADODB.Recordset")
        .Open "Select * from [Sheet1$]", "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & ThisWorkbook.FullName & ";Extended Properties=Excel 12.0"
        Sheet2.Range("A2").CopyFromRecordset .DataSource
        .MoveFirst
        Sheet3.Range("A2").CopyFromRecordset .DataSource
    End With
End Sub
Tôi cũng vừa biết điều này và có nói ở bài #43.
 
Upvote 0
Upvote 0
Tôi không nói vấn đề đã được xử lý bằng CopyFromRecordSet, cái tôi muốn nói là cái mảng xuất ra từ GetRows đó bạn.
Tôi cũng muốn nói đến mảng đó đó nhưng tôi không biết bạn thích xử lý nó vì sao. Tôi chỉ biết là xử lý từ đó nhanh hơn thôi. Cứ coi hàng là cột thì xử lý bình thường như data nguồn thôi.
 
Upvote 0
Khi làm việc với Listbox và Combobox thì nó sẽ không cần chuyển mảng mà có thể đưa dữ liệu trực tiếp vào.
Đúng rồi em, đây cũng là một lợi thế và cái hay mà anh nói cũng liên quan đến 2 loại control này, nhưng đó là chuyện sau này, còn mình thích nó ở đây là ... gợi ý: mảng dạng này có số hàng là cố định (nó lấy field làm hàng) và số cột là biến động (nó lấy record làm cột mới ghê). Đây là mấu chốt để mình dùng thuật toán để Filter nhanh hơn mảng 2 chiều từ sheet.
 
Upvote 0
Đúng rồi em, đây cũng là một lợi thế và cái hay mà anh nói cũng liên quan đến 2 loại control này, nhưng đó là chuyện sau này, còn mình thích nó ở đây là ... gợi ý: mảng dạng này có số hàng là cố định (nó lấy field làm hàng) và số cột là biến động (nó lấy record làm cột mới ghê). Đây là mấu chốt để mình dùng thuật toán để Filter nhanh hơn mảng 2 chiều từ sheet.
Nói đến đây chợt nhớ ArrayList.
 
Upvote 0
Đúng rồi em, đây cũng là một lợi thế và cái hay mà anh nói cũng liên quan đến 2 loại control này, nhưng đó là chuyện sau này, còn mình thích nó ở đây là ... gợi ý: mảng dạng này có số hàng là cố định (nó lấy field làm hàng) và số cột là biến động (nó lấy record làm cột mới ghê). Đây là mấu chốt để mình dùng thuật toán để Filter nhanh hơn mảng 2 chiều từ sheet.
Cụ thể là anh sẽ thực hiện điều đó như thế nào?
 
Upvote 0
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.
Nếu dữ liệu vượt quá số dòng chứa ở sheet đích thì code cho cách 1 và 3 sẽ như thế nào vậy bạn?
 
Upvote 0
Nếu dữ liệu vượt quá số dòng chứa ở sheet đích thì code cho cách 1 và 3 sẽ như thế nào vậy bạn?
Thực chất mọi người nên ngầm hiểu dữ liệu nguồn tôi muốn lấy là Excel và chỉ chừng chục ngàn dòng chứ tôi không bao giờ biết "dữ liệu khủng" là thế nào. Tôi nghĩ lấy từ Excel qua Excel (đừng từ 2003 lấy 2007 trở lên) thì đâu có xảy ra tình huống đó.
 
Upvote 0
Cụ thể là anh sẽ thực hiện điều đó như thế nào?
Trong một danh sách có nhiều mục, bạn cần lọc người có tên Tuấn chẳng hạn, nhưng đồng thời phải lọc theo đó là ngày sinh, nơi ở, số điện thoại v.v... hoặc đơn giản chỉ lọc lấy 2 cột tên và mã số thôi:

1630562260713.png

Bình thường thì mình dùng 2 vòng lặp là một vòng duyệt danh sách và trong khi duyệt mình lại làm 1 mảng để lưu lại số hàng đã được chọn, sau đó từ mảng ghi số hàng đó mình duyệt 1 vòng lặp nữa để tạo một mảng kết quả.

Thì với dạng mảng từ GetRows này mình chỉ duyệt 1 vòng lặp đã có mảng kết quả ngay trong đó luôn rồi.
 
Upvote 0
Khi làm việc với Listbox và Combobox thì nó sẽ không cần chuyển mảng mà có thể đưa dữ liệu trực tiếp vào.
Trong một danh sách có nhiều mục, bạn cần lọc người có tên Tuấn chẳng hạn, nhưng đồng thời phải lọc theo đó là ngày sinh, nơi ở, số điện thoại v.v... hoặc đơn giản chỉ lọc lấy 2 cột tên và mã số thôi:

View attachment 265276

Bình thường thì mình dùng 2 vòng lặp là một vòng duyệt danh sách và trong khi duyệt mình lại làm 1 mảng để lưu lại số hàng đã được chọn, sau đó từ mảng ghi số hàng đó mình duyệt 1 vòng lặp nữa để tạo một mảng kết quả.

Thì với dạng mảng từ GetRows này mình chỉ duyệt 1 vòng lặp đã có mảng kết quả ngay trong đó luôn rồi.
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.
 
Upvote 0
Web KT
Back
Top Bottom