Kết nối giữa các file Excel bằng ADODC.
Mình đã cùng Thu Nghi và Challenge98 thảo luận về vấn đề kết nối và trích rút dữ liệu giữa các File Excel. Có rất nhiều cách, nhưng ADODC cũng là 1 cách khá hay. Nhưng rồi kết quả cũng quên đi.
Trong thực tế có rất nhiều người hỏi về vấn đề này, vậy mình Post lên đây file ví dụ để các bạn tham khảo và mở rộng cho chương trình khi cần thiết. Đây là ví dụ đơn giản nhất có thể nên cũng là dễ hiểu với các bạn mới vọc. Hy vọng các bạn tìm được điều gì đó ở ví dụ này.
(Lưu ý: Khởi bằng ZMain)
www.giaiphapexcel.com/diendan/threads/k%E1%BA%BFt-n%E1%BB%91i-gi%E1%BB%AFa-c%C3%A1c-file-excel-b%E1%BA%B1ng-adodc.24648/
Khóa học SprinGO phù hợp
Học Nhân sự Tổng hợp – Trở thành chiến binh nhân sự vững nghiệp vụ
Con người là một trong những yếu tố quan trọng của công ty, là tài sản quý giá của doanh nghiệp. Chính vì thế,...
Xem khóa học
Bạn vào tools–Reference–Chọn Microsoft ActiveX Data Ojects 2.0…và Visual Basic for App…
Để khắc phục lỗi này chúng ta nên dùng khai báo trễ (late binding). Ví dụ:
Đối với ví dụ này ta có thể chuyển thao tác kiểm tra việc kết nối thành công hay không vào một hàm như sau:
Hàm
sẽ trả về:
Các giá trị này thì tùy bạn, bạn cũng có thể viết chỉ 2 giá trị thôi là 0 (không kết nối được) và 1 (kết nối thành công).
Trong hàm này tôi sử dụng khai báo trễ (late biding), như vậy nó sẽ không phục thuộc vào phiên bản của ADO.
Sau đó là việc chúng ta viết một hàm để lấy về một Recordset dựa trên câu lệnh SQL ta truyền cho hàm này. Như vậy công việc của chúng ta sẽ đơn giản hơn với việc viết các hàm, thủ tục để thực hiện các chức năng chúng ta thường sử dụng trong quá trình kết nối.
Lê Văn Duyệt
Như tôi đã nói, khi bạn tham chiếu đến đối tượng ADO theo kiểu khai báo sớm (early binding) thì nếu phiên bản của máy người dùng khác với phiên bản của máy người viết, thì thông báo lỗi cũng xãy ra. Còn về việc khai báo sớm, khai báo trễ, lợi ích hay khó khăn của chúng thì chúng ta đã bàn nhiều trên GPE.
Bạn cũng có thể tham khảo các bài viết, so sánh sau:
https://word.mvps.org/fAQs/InterDev/EarlyvsLateBinding.htm
https://www.dicks-clicks.com/excel/olBinding.htm
https://blogs.msdn.com/davidklinems/archive/2006/11/27/what-is-late-binding.aspx
Lê Văn Duyệt
1. Anh sealand xem thử code bên dưới nhé:
Và anh hãy bỏ đoạn "On error Resume Next" đi, để còn biết nó lỗi ở đâu mà sửa!
==> nghĩa là query trong Excel không dùng "*" mà phải dùng "%" mới được!%#^#$
2. Thêm vào đó, muốn truy xuất dữ liệu từ 1 file excel theo code bên dưới thì file excel nguồn phải đặt NAME cho vùng dữ liệu nguồn đó! ==> Với cách này, 1 sheet nguồn ta có thể có nhiều bảng tương ứng với nhiều NAME đặt cho nhiều vùng khác nhau trên sheet nguồn đó; và có thể truy xuất từ nhiều bảng này ra file khác theo code bên dưới.
(như trong file đính kèm, ta vào Insert/Name/Define sẽ thấy 1 Name có tên "DATA" với địa chỉ là vùng dữ liệu của sheet("DATA"); Ta xóa thử Name này đi thì code sẽ báo lỗi vì không tìm thấy bảng DATA nào cả!)
– Tuy nhiên không phải vì thể mà không giải quyết được, chúng ta có thể thêm "$" vào sau tên sheet của file –> excel sẽ tự coi sheet này là bảng luôn (giống như cách anh sealand phát biểu lệnh SQL ban đầu) ==> Tuy nhiên, với cách này, 1 sheet sẽ chỉ là 1 bảng dữ liệu mà thôi:
3. Em có thêm phần ghi chú trong code để người mới tiếp cận ADO dễ hình dung các bước tạo connection và truy xuất dữ liệu từ cơ sở dữ liệu nguồn ban đầu!
Đánh thêm dữ liệu xong bạn có lưu file lại hay chưa!? Mà nếu có lưu hay không lưu thì đâu có ảnh hưởng gì đến việc lấy dữ liệu đâu ta!? Bạn nói rõ "nó không cập nhật" thì "nó" là cái gì không cập nhật!? Có ví dụ cụ thể càng tốt!?
=========================
Vẫn cập nhật được trừ khi bạn thêm vào các dòng có mã TK không phải bắt đầu bằng "111".
Chr(13) là ký tự xuống dòng đó bạn.
[Where tk like '111%'] có thể phát biểu là: "khi tk bắt đầu bằng '111' "
Ví dụ 2 vê ADODC.
Bài này, mình thêm 1 ví dụ thô sử dụng ADODC để trích rút dữ liệu từ chính file Excel này để thiết lập báo cáo, chưa thêm phần nội lực của Excel. Điều mình muốn đề cập ở đây không phải giới thiệu kỹ thuật ADODC (Vì mình còn kém về vấn đề này lắm, nhưng không lo với sự hỗ trợ của GPE mình cho là không phải không làm được) mà mình muốn bàn là giải pháp nâng cao khả năng và có thể sử dụng Exc phục vụ công tác Kế toán thống kê mà thôi.
Trước đây, qua sách vở quảng bá và thực tế mình đã viết file Kế toán trên Exc khá kỳ công. Khi ở dạng Demo vài chục dòng thì chương trình trôi chảy, sổ sách báo báo cáo nhanh chóng, đẹp đẽ. Nhưng đau nhất lại là cuối năm, khi thời gian gấp rút không làm lại được với vài ngàn chứng từ (Tương ứng hàng chục ngàn dòng data) thì file sinh sự ỳ ra không chạy nổi, nhiều khi đơ luôn. Mỗi khi nhập 1 ô phải đợi mãi mới cập nhật được. Lý do, từ việc liên kết công thức từ quá nhiều sổ sách báo cáo đến sheet data. Biết bao nhiêu phép tính thường trực mỗi khi file hoạt động, nó làm cạn kiệt tài nguyên tưởng là khổng lồ của máy tính hiện nay.
Nhưng với ADODC trích dữ liệu xong thì cắt quan hệ với dữ liệu nguồn, các báo cáo gần như độc lập thì việc tạo thêm bao nhiêu báo cáo có hạn chế gì mấy, trong khi dùng công thức thì phải đắn đo mỗi báo cáo phải hy sinh bao nhiêu tốc độ. Mình mạn phép ví Excel -ADODC với khả năng kết hợp SQL thì như các cung thủ thời Tam Quốc được trang bị thêm kính La ze.
Mình xúi vậy thôi, nhưng các bạn cứ thử xem.
(Dữ liệu tháng 4/2009 các ô từ ngày, đến ngày thì bạn gõ tuỳ ý trong tháng 4)
Thu Nghi à, không phải ADO chiếm nhiều bộ nhớ đến nỗi treo máy đâu vì trong các phần mềm đôi khi đồng thời tồn tại vài RecordSet cùng lúc để sử lý mà có sao đâu (Hơn nữa, dữ liệu ví dụ đâu có nhiều). Mình nghĩ cái chính là code ví dụ nên độ tùy biến chưa cao khi sang máy khác hay bị trục trặc hoặc không hoạt động (Ở máy của mình thì những cái sổ lớn như 111,511 thì chỉ là 1 cái chớp màn hình). Nếu cẩn thận thì dùng code của anh Duyệt để kết nối thì an toàn hơn. Nhưng khổ nỗi ví dụ gửi lên gửi xuống mà nó lớn quá cũng không ổn.
Cái Group by trong ví dụ nó tương quan với Sumproduct cơ (3 điều kiện cơ đấy: Tài khoản, từ ngày, đến ngày) . Sumif sao làm nổi nếu không chuyển sang công thức mảng (Mà đã nói đến mảng thì mình lại ghê vì ít thì được chứ nhiều thì ngồi đếm % calculate là thường)
Phần thêm của Thu Nghi là đúng quá chứ. Trong ví dụ để ngắn gọn nên mình áp roẹt 1 cái là Sheet3.Rows("5:100").ClearContents chứ thực tế phải xác định dòng cuối cùng cụ thể rồi xóa mới chính xác chứ. Rồi còn bẫy và sử lý lỗi nữa.
To [URL='https://www.giaiphapexcel.com/forum/member.php?u=184553'%5Dchallenge98:
Mình sửa lại 1 số chỗ theo yêu cầu của bạn, bạn xem lại nhé.
Nhưng làm sao lại phải thêm 1 cái form trung gian để làm gì nữa mình tham gia càng bớt càng tốt.
If recex.RecordCount > 0 Then 'Nếu recex có DL thì làm , không thì thôi
recex.MoveFirst 'Chuyển con trỏ về record đầu tiên
k = 0 'Đặt biến k=0
Do While Not recex.EOF 'Bắt đầu vòng lặp nếu recex ở cuối thì thôi
ListBox1.AddItem recex.Fields(0) 'Thêm 1 mục vào List bằng Field thứ nhất
ListBox1.List(k, 1) = recex.Fields(1) 'Thêm cột thứ 2 cho mục thứ k của List bằng Field thứ hai
k = k + 1 'Đặt biến k tăng them 1
recex.MoveNext 'Chuyển con trỏ về record tiếp theo
Loop 'Lặp lại
2/ Câu lệnh SQL thì cũng y chang ý bạn đấy thôi
recex.Open "select * from [Sheet1$] ", cnEx, adOpenKeyset, adLockOptimistic
1/Theo mình nạp trực tiếp gọn hơn đỡ phải qua khâu nạp mảng, đỡ phải dọn dẹp vì với dữ liệu lớn thì cái biến mảng ấy khổng lồ, nếu quên reset biến thì nó chiếm bộ nhớ không nhỏ.
2/Nó không chạy vì phần mở rộng mà thôi, bạn đã khai đuôi trong chuỗi connect rồi. Bạn đổi phần mở rộng thành .xls xem.
Nếu muốn truy vấn dữ liệu trên một Range thì làm thế này
SELECT * FROM [sheet1$A4:H100]…
Thông thường nên đặt tên/Name NKC= sheet1!$A$4:$H$100
Khi đó ta có thể viết câu lệnh SQL
SELECT * FROM NKC…
Sao vận dụng đặt name vào bài SO KE TOAN của anh Sealand trong topic này mà nó không chạy. Các bác xem hộ sai chỗ nào.
Sub chepdl2()
ng1 =
ng2 =
Dim endR As Long, myRng As Range
On Error Resume Next
FName = ThisWorkbook.Path & "" & ThisWorkbook.Name
Set cnEx = New ADODB.Connection
With Sheets("Data")
endR = .Cells(65000, 1).End(xlUp).Row
Set myRng = .Range("A1:H" & endR)
End With
cnEx.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & _
FName & ";Persist Security Info=False; Extended Properties=Excel 8.0;"
cnEx.Open
Set recex = New ADODB.Recordset
recex.Open "select tk, sum(psno), sum(psco)from myrng where Ngay >=#" _
& ng1 & "# and Ngay<=#" & ng2 & "# group by tk ", cnEx, adOpenKeyset, adLockOptimistic
Sheet3.Rows("5:100").ClearContents
Sheet3..CopyFromRecordset recex
recex.Close
Set recex = Nothing
cnEx.Close
Set cnEx = Nothing
End Sub
Mới nhờ PTM0412 hướng dẫn và rút ra 1 nhận xét.
Phải đặt name chớ không phải
Dim MyRng as range
Set MyRng=…
Mà phải là
With Sheets("Data")
endR = .Cells(65000, 1).End(xlUp).Row
.Range("A1:H" & endR).Name = "myRng"
End With
Lúc này ADO mới xem dòng 1 là tên field.
Và phải thêm "" thành thì nó mới chạy.
Đây là một ví dụ ứng dụng ADODB với câu lệnh SQL lồng (dùng Union All). Các anh chị và các bạn tham khảo code bên dưới và xem file đính kèm. Đã test thử với cấu trúc DATA như file đính kèm, khoản 10.000 record lọc chưa tới 15 giây.
File PDF đính kèm hướng dẫn sơ lược cách sử dụng ADODB một cách đơn giản nhất! Mời mọi người tham khảo!
Bài này, mình đề xuất 1 phương án trích tạo sổ sách báo cáo sao cho thật linh hoạt và code ngăn nhất. Đông thời cũng là code tạo danh sách duy nhất khá gọn ( 3 trong 1:Các ngày có PS, Các TK có PS, Các Đối tương có PS). Đây chỉ là phương án thôi nên còn hạn chế và lỗi. Chỉ yêu cầu người dùng biết chút về viết SQL là chủ động tạo sổ sách, báo cáo rồi. Xin lỗi mình chưa trang trí tạo tiêu đề cho sổ.
Riêng phần kết nối mình đưa vào biến công cộng, khi cần dùng cứ việc gọi. Ta dùng loạt bài đầu để rút dữ liệu từ các file khác nhau là có thể tạo file sosach dùng chung. Như vậy, ta loại được sổ sách báo cáo ra khỏi file nhập dữ liệu làm cho file dữ liệu hoàn toàn tự do và nhẹ nhàng phục vụ mục đính chính là nhập dữ liệu.
Các bạn xem và góp ý cho mình với.
Mình không có Excel2007 nên không test được, Thu Nghi tham khảo [URL='https://www.connectionstrings.com/excel-2007'%5Dở đây xem
HDR = 1 để xác định dòng đầu là tên field
HDR = 0 để lấy luôn dòng đâu như là 1 record, khi copy kết quả xuống sheet.
Nếu dùng HDR = 0 thì với dữ liệu kiểu số, mà record đầu là tên filed, là text, nguyên field sẽ bị coi là text.
Do đó phải dùng IMEX = 1, mục đích để Excel nhận dạng lại type của dữ liệu trên cả field, không chỉ căn cứ trên dòng đầu.
Nói thêm, khuyến cáo nên dùng IMEX = 1 dù cho HDR = Yes hay No, để ADO không nhận dạng type dữ liệu bị sai, nhất là khi bảng tính định dạng General, và/ hoặc field dữ liệu bỏ trống record đầu.
Lý do là ADO khi đọc dòng dầu thấy ô trống, nó sẽ đoán (guess) 1 trong các kiểu là:
– Numeric và giá trị zero
– Text và giá trị blank
– DateTime và giá trị blank
Mà không phải lúc nào cũng đoán đúng.
NGAY_VS là ngày gì?
SO_CT là số CT của mua hay trả tiền?
NGAY_CT là ngày CT mua hay ngày trả tiền?