Khai thác và tùy biến thêm, sửa, xuất file và lấy dữ liệu từ Recordset

Liên hệ QC
có lẻ nên mở rộng chủ đề nghiên cứu thêm Rs.UpdateBatch ở nhiều cách tiếp cận ADODB khác nhau đi ... vì trên GPE này thấy còn quá ít cho nó
 
Trong CSDL Excel thì nó có giới hạn không cho xóa nhé em.
Một CSDL có data (dữ liệu) và metadata (hạ tầng có sở, tức dữ liệu về dữ liệu và cấu trúc của chúng).
CSV thì có metatdata đơn giản (điển hình: chỉ dấu phẩy chia cách các cột)
Nhưng Excel thì metatdata phức tạp hơn nhiều. Điển hình, nó có cả một cái vec tơ map để map các ô trong bảng tính.
ADO là công cụ COM để trích xuất dữ liệu. Để đọc dữ liệu trong file Excel, ADO phải đi tắt (shortcut) một số metadata. MS mắt buộc phải giới hạn tính chất sửa xoá để tránh trường hợp dữ liệu mới bị "lạc bầy" (mất liên hệ với metadata)

Quý vị nào muốn làm việc với CSDL thì nên tìm hiểu cho rõ về metadata.
 
Sub GetRs(dongtradulieu as string,ByVal dongdau As Long, ByVal dongcuoi As Long)
...
.Open ("Select * from [Sheet1$]"), "Provider=Microsoft.ACE.OLEDB.12.0;Extended Properties=Excel 12.0 Xml;Data Source=" & ThisWorkbook.FullName
...
Sheet2.Range(dongtradulieu).CopyFromRecordset .DataSource, dongcuoi

Thế này được không nhanh mạnh

Vẫn còn các tham số Sheet1, Sheet2 cố định trong hàm :) , không thể tuỳ biến.
Đối với tôi khi viết hàm/ thủ tục thì không phải chỉ chuyển các tham số thành các biến để truyền trong hàm mà còn phải xét tổng thể nghiệp vụ, qui trình xử lý, các bẫy lỗi, các trường hợp có thể phát sinh v.v.. để viết cái hàm phù hợp để có thể tái sử dụng. Một thủ tục trên có thể tách thành mấy hàm để xử lý linh hoạt.
Ví dụ:
- Nếu tôi muốn kết nối tới Excel 2003 (.xls) thì chuỗi kết nối trên có phù hợp không?
- Nếu tôi không muốn "Select *..." mà "Select F1, F4, F7..." thì như thế nào?
- ...
Trên đây chỉ là một số gợi ý tham khảo thêm để viết các hàm, thủ tục cho ứng dụng. Còn các ví dụ của bác HLMT trong chủ đề này thì cứ dùng cách viết đơn giản nhất để mọi người dễ hiểu, dễ tham khảo từng bước thôi.
 
Lần chỉnh sửa cuối:
Vẫn còn các tham số Sheet1, Sheet2 cố định trong hàm :) , không thể tuỳ biến.
Đối với tôi khi viết hàm/ thủ tục thì không phải chỉ chuyển các tham số thành các biến để truyền trong hàm mà còn phải xét tổng thể nghiệp vụ, qui trình xử lý, các bẫy lỗi, các trường hợp có thể phát sinh v.v.. để viết cái hàm phù hợp để có thể tái sử dụng. Một thủ tục trên có thể tách thành mấy hàm để xử lý linh hoạt.
Ví dụ:
- Nếu tôi muốn kết nối tới Excel 2003 (.xls) thì chuỗi kết nối trên có phù hợp không?
- Nếu tôi không muốn "Select *..." mà "Select F1, F4, F7..." thì như thế nào?
- ...
Trên đây chỉ là một số gợi ý tham khảo thêm để viết các hàm, thủ tục cho ứng dụng. Còn các ví dụ của bác HLMT trong chủ đề này thì cứ dùng cách viết đơn giản nhất để mọi người dễ hiểu, dễ tham khảo từng bước thôi.
Khoảng 10 năm trước thì việc viết hàm/thủ tục có tính cách tổng thể khá quan trọng.
Nhưng hiện giờ thì thế giới phần mềm đã thay đổi nhiều, và với tôcvs độ rất nhanh. Những gì hôm nay là tối ưu vẫn có thể trở thành lỗi thời ngày mai.
Vì vậy khuynh hướng mới bây giờ là chỉ tổng thể những gì rất vững chắc, ít thay đổi.

Sau một thời gian tiếp xúc với GPE, tôi có hai nhận xét (mỗi nhận xét đi kèm với lời khuyên):

1. Hầu hết những nhu cầu ở GPE thuộc về loại cấp thời, thay đổi liên tục. Vì vậy chỉ thích hợp với loại code cần tới đâu viết tới đó. Thư viện thì chỉ thích hợp với loại code "mì ăn liền", tức là bạn tự lập cho mình một cái file (hoặc một folder với một đống files) text hay word với những đoạn code thường dùng, khi cần viết code thì cứ việc giở ra mà copy/paste.

2. Cách viết hàm/thủ tục tổng thể ở đây (GPE) là viết một cái hàm/thủ tục tổ bố với một đống options. Đọc muốn xỉu.
Trên thực tế, kỹ thuật viết hàm thư viện không phải vậy. Người ta tách từng công việc riêng biệt ra thành hàm riêng. Cái hàm tổng thể sẽ gọi các hàm nhỏ kia mà làm đúng công việc cần thiết. Nói cách khác, cái hàm/thủ tục tổng thể với nhiều options không phải là một hàm từ a đến z, mà là một nhóm hàm với một vài cái giao diện của hàm chính (*).

(*) đối với lập trình hướng đối tượng thì điều này là bắt buộc. Và đó là một trong những lợi điểm của LTHĐT.
 
Chỉnh sửa dữ liệu, ví dụ với file mẫu bài 1, tôi muốn chỉnh sửa dữ liệu trong cột Code = 'HAI LÚA' và cột Price =100 với điều kiện cột ID có giá trị là 'TP0005' thì code như sau:
Rich (BB code):
Sub ChinhSuaDL_HLMT()
    With CreateObject("ADODB.Recordset")
        .Open "Select * from [Sheet1$]", "Provider=Microsoft.ACE.OLEDB.12.0;Extended Properties=Excel 12.0 Xml;Data Source=" & ThisWorkbook.FullName, , 3
        .Filter = "[ID]='TP0005'"
        !Price = 100
        !Code = "HAI LÚA"
        .Update
    End With
End Sub

1605924791756.png
 
Cũng file mẫu bài số 1, tôi tiến hành xuất kết quả truy vấn với điều kiện cột [ID] khác 'TP0001' và cột [PRICE] <1.000.000 ([ID]<>'TP0001' and [PRICE] <1000000) ra file *.Csv
Code như sau:

Rich (BB code):
Sub LuuFileCsv_HLMT()
    Dim strRecord As String
    With CreateObject("ADODB.Recordset")
        .Open "Select * from [Sheet1$]", "Provider=Microsoft.ACE.OLEDB.12.0;Extended Properties=Excel 12.0 Xml;Data Source=" & ThisWorkbook.FullName, 1, 3
        .Filter = "[ID]<>'TP0001' and [PRICE] <1000000"
        strRecord = .GetString(, , ";", vbCrLf)
        .Close
    End With
    CreateObject("Scripting.FileSystemObject").CreateTextFile(ThisWorkbook.Path & "\Test.csv").Write strRecord
End Sub

Kết quả:

1606180780558.png
 
Cũng file mẫu bài số 1, tôi tiến hành xuất kết quả truy vấn với điều kiện cột [ID] khác 'TP0001' và cột [PRICE] <1.000.000 ([ID]<>'TP0001' and [PRICE] <1000000) ra file *.xml
Code như sau:

Mã:
Sub LuuFileXML_HLMT()
    With CreateObject("ADODB.Recordset")
        .Open "Select * from [Sheet1$]", "Provider=Microsoft.ACE.OLEDB.12.0;Extended Properties=Excel 12.0 Xml;Data Source=" & ThisWorkbook.FullName 
        .Filter = "[ID]<>'TP0001' and [PRICE] <1000000"
        .Save ThisWorkbook.Path & "\Test.xml", 1
    End With
End Sub

Kết quả:
XML:
<xml xmlns:s='uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882'
    xmlns:dt='uuid:C2F41010-65B3-11d1-A29F-00AA00C14882'
    xmlns:rs='urn:schemas-microsoft-com:rowset'
    xmlns:z='#RowsetSchema'>
<s:Schema id='RowsetSchema'>
    <s:ElementType name='row' content='eltOnly'>
        <s:AttributeType name='ID' rs:number='1' rs:nullable='true' rs:maydefer='true' rs:writeunknown='true'>
            <s:datatype dt:type='string' dt:maxLength='255'/>
        </s:AttributeType>
        <s:AttributeType name='Code' rs:number='2' rs:nullable='true' rs:maydefer='true' rs:writeunknown='true'>
            <s:datatype dt:type='string' dt:maxLength='255'/>
        </s:AttributeType>
        <s:AttributeType name='Price' rs:number='3' rs:nullable='true' rs:maydefer='true' rs:writeunknown='true'>
            <s:datatype dt:type='float' dt:maxLength='8' rs:precision='15' rs:fixedlength='true'/>
        </s:AttributeType>
        <s:extends type='rs:rowbase'/>
    </s:ElementType>
</s:Schema>
<rs:data>
    <z:row ID='TP0002' Code='MBY001' Price='250000'/>
    <z:row ID='TP0003' Code='MBY002' Price='300000'/>
    <z:row ID='TP0004' Code='MBY002' Price='350000'/>
    <z:row ID='TP0005' Code='MBY002' Price='400000'/>
    <z:row ID='TP0006' Code='MBY003' Price='450000'/>
    <z:row ID='TP0007' Code='MBY004' Price='500000'/>
    <z:row ID='TP0008' Code='MBY005' Price='550000'/>
    <z:row ID='TP0009' Code='MBY006' Price='600000'/>
    <z:row ID='TP0010' Code='MBY007' Price='650000'/>
    <z:row ID='TP0011' Code='MBY008' Price='700000'/>
    <z:row ID='TP0012' Code='MBY009' Price='750000'/>
    <z:row ID='TP0013' Code='MBY010' Price='800000'/>
    <z:row ID='TP0014' Code='MBY011' Price='850000'/>
    <z:row ID='TP0015' Code='MBY012' Price='900000'/>
    <z:row ID='TP0016' Code='MBY013' Price='950000'/>
</rs:data>
</xml>
 
Cũng file mẫu bài số 1, tôi tiến hành xuất kết quả truy vấn với điều kiện cột [ID] khác 'TP0001' và cột [PRICE] <1.000.000 ([ID]<>'TP0001' and [PRICE] <1000000) ra file *.xml
Code như sau:

Mã:
Sub LuuFileXML_HLMT()
    With CreateObject("ADODB.Recordset")
        .Open "Select * from [Sheet1$]", "Provider=Microsoft.ACE.OLEDB.12.0;Extended Properties=Excel 12.0 Xml;Data Source=" & ThisWorkbook.FullName
        .Filter = "[ID]<>'TP0001' and [PRICE] <1000000"
        .Save ThisWorkbook.Path & "\Test.xml", 1
    End With
End Sub

Kết quả:
XML:
<xml xmlns:s='uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882'
    xmlns:dt='uuid:C2F41010-65B3-11d1-A29F-00AA00C14882'
    xmlns:rs='urn:schemas-microsoft-com:rowset'
    xmlns:z='#RowsetSchema'>
<s:Schema id='RowsetSchema'>
    <s:ElementType name='row' content='eltOnly'>
        <s:AttributeType name='ID' rs:number='1' rs:nullable='true' rs:maydefer='true' rs:writeunknown='true'>
            <s:datatype dt:type='string' dt:maxLength='255'/>
        </s:AttributeType>
        <s:AttributeType name='Code' rs:number='2' rs:nullable='true' rs:maydefer='true' rs:writeunknown='true'>
            <s:datatype dt:type='string' dt:maxLength='255'/>
        </s:AttributeType>
        <s:AttributeType name='Price' rs:number='3' rs:nullable='true' rs:maydefer='true' rs:writeunknown='true'>
            <s:datatype dt:type='float' dt:maxLength='8' rs:precision='15' rs:fixedlength='true'/>
        </s:AttributeType>
        <s:extends type='rs:rowbase'/>
    </s:ElementType>
</s:Schema>
<rs:data>
    <z:row ID='TP0002' Code='MBY001' Price='250000'/>
    <z:row ID='TP0003' Code='MBY002' Price='300000'/>
    <z:row ID='TP0004' Code='MBY002' Price='350000'/>
    <z:row ID='TP0005' Code='MBY002' Price='400000'/>
    <z:row ID='TP0006' Code='MBY003' Price='450000'/>
    <z:row ID='TP0007' Code='MBY004' Price='500000'/>
    <z:row ID='TP0008' Code='MBY005' Price='550000'/>
    <z:row ID='TP0009' Code='MBY006' Price='600000'/>
    <z:row ID='TP0010' Code='MBY007' Price='650000'/>
    <z:row ID='TP0011' Code='MBY008' Price='700000'/>
    <z:row ID='TP0012' Code='MBY009' Price='750000'/>
    <z:row ID='TP0013' Code='MBY010' Price='800000'/>
    <z:row ID='TP0014' Code='MBY011' Price='850000'/>
    <z:row ID='TP0015' Code='MBY012' Price='900000'/>
    <z:row ID='TP0016' Code='MBY013' Price='950000'/>
</rs:data>
</xml>
Có vẻ độc thoại có một mình buồn nhỉ ... Mạnh mà vào là lại nổi sóng ngay và luôn he ... có điều kỳ này làm biếng lắm

1/ Viết xuất xml rồi => xong
2/ Viết tiếp các kiểu ActiveWorkbook.XmlImport nữa đi chứ cho nó chọn bộ ?!
 
Có vẻ độc thoại có một mình buồn nhỉ ... Mạnh mà vào là lại nổi sóng ngay và luôn he ... có điều kỳ này làm biếng lắm

1/ Viết xuất xml rồi => xong
2/ Viết tiếp các kiểu ActiveWorkbook.XmlImport nữa đi chứ cho nó chọn bộ ?!
Đang khai thác Recordset mà anh. Vậy nên mình phải lấy từ Recordset.
 
Có vẻ độc thoại có một mình buồn nhỉ ... Mạnh mà vào là lại nổi sóng ngay và luôn he ... có điều kỳ này làm biếng lắm
Hôm bữa có thấy bạn có đưa mấy đề tài liên quan mà chưa thấy demo, trả lời để mọi người học hỏi thì nó xôm tụ chứ.
 
Cũng file mẫu bài số 1, tôi tiến hành xuất kết quả truy vấn với điều kiện cột [ID] khác 'TP0001' và cột [PRICE] <1.000.000 ([ID]<>'TP0001' and [PRICE] <1000000) ra file *.xml
Code như sau:

Mã:
Sub LuuFileXML_HLMT()
    With CreateObject("ADODB.Recordset")
        .Open "Select * from [Sheet1$]", "Provider=Microsoft.ACE.OLEDB.12.0;Extended Properties=Excel 12.0 Xml;Data Source=" & ThisWorkbook.FullName
        .Filter = "[ID]<>'TP0001' and [PRICE] <1000000"
        .Save ThisWorkbook.Path & "\Test.xml", 1
    End With
End Sub

Kết quả:
XML:
<xml xmlns:s='uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882'
    xmlns:dt='uuid:C2F41010-65B3-11d1-A29F-00AA00C14882'
    xmlns:rs='urn:schemas-microsoft-com:rowset'
    xmlns:z='#RowsetSchema'>
<s:Schema id='RowsetSchema'>
    <s:ElementType name='row' content='eltOnly'>
        <s:AttributeType name='ID' rs:number='1' rs:nullable='true' rs:maydefer='true' rs:writeunknown='true'>
            <s:datatype dt:type='string' dt:maxLength='255'/>
        </s:AttributeType>
        <s:AttributeType name='Code' rs:number='2' rs:nullable='true' rs:maydefer='true' rs:writeunknown='true'>
            <s:datatype dt:type='string' dt:maxLength='255'/>
        </s:AttributeType>
        <s:AttributeType name='Price' rs:number='3' rs:nullable='true' rs:maydefer='true' rs:writeunknown='true'>
            <s:datatype dt:type='float' dt:maxLength='8' rs:precision='15' rs:fixedlength='true'/>
        </s:AttributeType>
        <s:extends type='rs:rowbase'/>
    </s:ElementType>
</s:Schema>
<rs:data>
    <z:row ID='TP0002' Code='MBY001' Price='250000'/>
    <z:row ID='TP0003' Code='MBY002' Price='300000'/>
    <z:row ID='TP0004' Code='MBY002' Price='350000'/>
    <z:row ID='TP0005' Code='MBY002' Price='400000'/>
    <z:row ID='TP0006' Code='MBY003' Price='450000'/>
    <z:row ID='TP0007' Code='MBY004' Price='500000'/>
    <z:row ID='TP0008' Code='MBY005' Price='550000'/>
    <z:row ID='TP0009' Code='MBY006' Price='600000'/>
    <z:row ID='TP0010' Code='MBY007' Price='650000'/>
    <z:row ID='TP0011' Code='MBY008' Price='700000'/>
    <z:row ID='TP0012' Code='MBY009' Price='750000'/>
    <z:row ID='TP0013' Code='MBY010' Price='800000'/>
    <z:row ID='TP0014' Code='MBY011' Price='850000'/>
    <z:row ID='TP0015' Code='MBY012' Price='900000'/>
    <z:row ID='TP0016' Code='MBY013' Price='950000'/>
</rs:data>
</xml>
Sau khi chạy code trên ta được file có tên là Test.xml. Tiến hành lấy dữ liệu từ file được tạo ra đó bằng code sau:

Rich (BB code):
Sub LayDL_XML_HLMT()
    With CreateObject("ADODB.Recordset")
        .Open ThisWorkbook.Path & "\Test.xml", "Provider=MSPersist"
        Sheet2.Range("A2").CopyFromRecordset .DataSource
    End With
End Sub
 
Như trên có bài viết nói về việc đưa mảng vào Listbox để thay cho hàm TRANSPOSE hoặc xoay mảng bằng hàm tự tạo. Nay tôi xin tạm viết cái gợi ý đó thành cái hàm như sau:

Mã:
Public Function GetRsValues(strSQL As String, rng As Range, Optional strPath As String) As Variant
    On Error GoTo ErrorHandler
    With CreateObject("ADODB.Recordset")
        .Open (strSQL), ("Provider=Microsoft.ACE.OLEDB.12.0;Extended Properties=Excel 12.0 Xml;Data Source=" & IIf(Len(strPath) = 0, ThisWorkbook.FullName, strPath))
        GetRsValues = .GetRows
    End With
    With CreateObject("New:{8BD21D20-EC42-11CE-9E0D-00AA006002F3}")
        .Column = GetRsValues
        GetRsValues = .List
    End With
    rng.Resize(UBound(GetRsValues) + 1, UBound(GetRsValues, 2) + 1) = GetRsValues
    Exit Function
ErrorHandler:
    MsgBox Err.Description
End Function

Như dữ liệu bài 1 tôi thử hàm trên như sau:

Mã:
Sub Test_GetRsValues()
    Call GetRsValues("Select * from [Sheet1$]", Sheet2.Range("A2"))
End Sub

Dữ liệu sẽ được đổ vào cell A2 của sheet2. Hàm trên còn 1 tham số cuối chưa truyền vào đó là đường dẫn đến file (strPath) bởi vì nó được lấy dữ liệu ở chính file đó. Nếu các bạn muốn lấy file khác với file chạy code thì phải thêm 1 tham số còn lại là đường dẫn đến file nguồn nhé.
 
Như trên có bài viết nói về việc đưa mảng vào Listbox để thay cho hàm TRANSPOSE hoặc xoay mảng bằng hàm tự tạo. Nay tôi xin tạm viết cái gợi ý đó thành cái hàm như sau:

Mã:
Public Function GetRsValues(strSQL As String, rng As Range, Optional strPath As String) As Variant
    On Error GoTo ErrorHandler
    With CreateObject("ADODB.Recordset")
        .Open (strSQL), ("Provider=Microsoft.ACE.OLEDB.12.0;Extended Properties=Excel 12.0 Xml;Data Source=" & IIf(Len(strPath) = 0, ThisWorkbook.FullName, strPath))
        GetRsValues = .GetRows
    End With
    With CreateObject("New:{8BD21D20-EC42-11CE-9E0D-00AA006002F3}")
        .Column = GetRsValues
        GetRsValues = .List
    End With
    rng.Resize(UBound(GetRsValues) + 1, UBound(GetRsValues, 2) + 1) = GetRsValues
    Exit Function
ErrorHandler:
    MsgBox Err.Description
End Function

Như dữ liệu bài 1 tôi thử hàm trên như sau:

Mã:
Sub Test_GetRsValues()
    Call GetRsValues("Select * from [Sheet1$]", Sheet2.Range("A2"))
End Sub

Dữ liệu sẽ được đổ vào cell A2 của sheet2. Hàm trên còn 1 tham số cuối chưa truyền vào đó là đường dẫn đến file (strPath) bởi vì nó được lấy dữ liệu ở chính file đó. Nếu các bạn muốn lấy file khác với file chạy code thì phải thêm 1 tham số còn lại là đường dẫn đến file nguồn nhé.
Thì hôm lâu rồi ở thớt khác Mạnh có nói rồi nay mới thấy Úp he
Mạnh cảm giác thấy mình như con sâu code trên GPE này ý ... khả năng của ai sao thì gần như có thể đoán ra he
 
Thì hôm lâu rồi ở thớt khác Mạnh có nói rồi nay mới thấy Úp he
Mạnh cảm giác thấy mình như con sâu code trên GPE này ý ... khả năng của ai sao thì gần như có thể đoán ra he
Hàm trên chỉ là căn bản, muốn sử dụng ta cần phải thêm thắt rất nhiều.
 
Đưa một vùng dữ liệu trên bảng tính vào Recordset có nhiều cách. Tuy nhiên hôm nay tôi xin giới thiệu cách đưa vào từ XML
Cũng ví dụ file mẫu bài 1, tôi đưa vùng dữ liệu từ A1 : C109 với điều kiện cột Price <= 900.000 sau đó sắp xếp cột này theo thứ tự giảm dần.
Code sẽ như sau:
Mã:
Sub Rng2Rst()
    Dim objXML As Object
    Set objXML = CreateObject("MSXML2.DOMDocument")
    objXML.LoadXML Sheet1.Range("A1:C109").Value(12)
    With CreateObject("ADODB.Recordset")
        .Open objXML
        .Filter = "[Price] <= 900000"
        .Sort = "[Price] DESC"
        Sheet2.Range("A2").CopyFromRecordset .DataSource
    End With
End Sub
Kết quả như hình bên dưới:
1606964038036.png
 
Lần chỉnh sửa cuối:
Lần chỉnh sửa cuối:
khi nào rảnh các bạn đổi hướng sang thử xài API của Bill đi xem sao ???!!!

Google 1 hồi ra đầy code mẫu cho mà test
govert/SQLiteForExcel: A lightweight wrapper to give access to the SQLite3 library from VBA. (github.com)

Connect to SQLite from VBA using winsqlite3.dll (renenyffenegger.ch)

Bạn có đọc hiểu cái thư viện này làm gì chưa mà nói của Bill? Của tay Govert viết ra nhé.
SQLite thì có liên quan như thế nào đến chủ đề này bạn? Hay bạn muốn ứng dụng Excel dùng SQLite làm CSDL back end hay muốn đọc ghi dữ liệu từ các thiết bị điện thoại, máy chơi game, remote ...
 
Lần chỉnh sửa cuối:
Bạn có đọc hiểu cái thư viện này làm gì chưa mà nói của Bill? Của tay Govert viết ra nhé.
SQLite thì có liên quan như thế nào đến chủ đề này bạn? Hay bạn muốn ứng dụng Excel dùng SQLite làm CSDL back end hay muốn đọc ghi dữ liệu từ các thiết bị điện thoại, máy chơi game, remote ...
Hình như có nhầm lẫn gì đó anh.

winsqlite3.dll chắc chắn là của Microsoft.

1607141430590.png

Còn cái SQLiteForExcel là cái khác.

1607141497766.png
 
Hình như có nhầm lẫn gì đó anh.

winsqlite3.dll chắc chắn là của Microsoft.

View attachment 250630

Còn cái SQLiteForExcel là cái khác.

View attachment 250631

Thì cũng là link bạn Mạnh đưa lên thôi và cái winsqlite3.dll cũng chỉ dùng để tương tác với SQLite database thôi.
Vậy ứng dụng nó ra sao trong Excel và nếu dùng nó có gì hay hơn so với vụ dùng ADODB để kết nối nguồn dữ liệu Excel mà phải đổi hướng xài nó vậy? Tôi chưa xài tới nó bao giờ vì chưa thấy có nhu cầu trong hiện tại của tôi, nếu bạn Mạnh thấy được tính ứng dụng của nó thì khai sáng cho mọi người nhé hoặc bạn Mạnh tạo cái chủ đề mới giới thiệu về nó và ứng dụng cho mọi người học hỏi.
 
Lần chỉnh sửa cuối:
Web KT
Back
Top Bottom