AI muốn lập trình DLL cho Excel và các loại bằng Delphi thì xem video này nhé!

Liên hệ QC

Nguyễn Duy Tuân

Nghị Hách
Thành viên danh dự
Tham gia
13/6/06
Bài viết
4,649
Được thích
10,138
Giới tính
Nam
Nghề nghiệp
Giáo viên, CEO tại Bluesofts
Lần chỉnh sửa cuối:
Bạn làm theo bài #1258 là chính xác
mình làm anh Tuân hướng dẫn (của mình Win 64bit + Office 64bit ) chép vào system32 thì khi chạy nó lại báo lỗi "can't find dll entry point...". Mình có thử đăng ký DLL cho máy tính cũng bị báo lỗi. Mong bạn chỉ giúp.
Cảm ơn bạn !
 
Upvote 0
mình làm anh Tuân hướng dẫn (của mình Win 64bit + Office 64bit ) chép vào system32 thì khi chạy nó lại báo lỗi "can't find dll entry point...". Mình có thử đăng ký DLL cho máy tính cũng bị báo lỗi. Mong bạn chỉ giúp.
Cảm ơn bạn !

Có lẽ lỗi của DLL A gọi DLL B. Tác giả phải chỉnh lại mã nguồn tring DLL mới ổn.
Bài đã được tự động gộp:

mình làm anh Tuân hướng dẫn (của mình Win 64bit + Office 64bit ) chép vào system32 thì khi chạy nó lại báo lỗi "can't find dll entry point...". Mình có thử đăng ký DLL cho máy tính cũng bị báo lỗi. Mong bạn chỉ giúp.
Cảm ơn bạn !

Có lẽ lỗi của DLL A gọi DLL B. Tác giả phải chỉnh lại mã nguồn tring DLL mới ổn.
 
Upvote 0
mình làm anh Tuân hướng dẫn (của mình Win 64bit + Office 64bit ) chép vào system32 thì khi chạy nó lại báo lỗi "can't find dll entry point...". Mình có thử đăng ký DLL cho máy tính cũng bị báo lỗi. Mong bạn chỉ giúp.
Cảm ơn bạn !
Hi bạn cứ hỏi trong này anh tuân và các anh khác sẽ hướng dẫn cho bạn. còn mình delphi cũng chỉ biết chút ích ah.
Bạn nên xem lại source library của bạn xem có sai chỗ nào không. Mới đầu tập viết thì viết cái đơn giản thôi, gọi form gọi hàm..., từ DLL khác không khó đâu.
 
Upvote 0
Chị Thủy yêu chỉ em học Đeo Phai với
 
Upvote 0
Nhờ ACE giúp mình tối ưu đoạn code tính tổng 1 vùng trong excel, mình thử test tốc độ với mảng 11 cột và 500 ngàn dòng thì VBA chạy 0,8s còn delphi thì 1,1s, mình nghĩ do chưa tối ưu được mảng truyền vào, đọc chủ đề này thấy anh Nguyễn Duy Tuân có nói phải dùng PSafeArray.
- Code duyệt mảng và tính tổng trong VBA mình viết như sau:
Mã:
Sub TestTinhTong2()
    Dim lr As Long, arr As Variant, tong As Double, i As Long, j As Long, t As Single
    t = Timer
    With Sheet1
        lr = .Range("A1000000").End(3).Row
        arr = .Range("A1:K" & lr).value
    End With
    For i = 1 To UBound(arr)
        For j = 1 To UBound(arr, 2)
            tong = tong + arr(i, j)
        Next j
    Next i
    MsgBox tong, , Timer - t
End Sub
- Code delphi
Mã:
function GetSumRange(arr:Variant):Double;stdcall;
 var
  i,j:uint32;
  iSum:Double;
 begin
  iSum:=0;
  for i := VarArrayLowBound(arr,1) to VarArrayhighBound(arr,1) do
    begin
      for j := varArrayLowBound(arr,2) to varArrayHighBound(arr,2) do
         iSum:=iSum + arr[i,j];
    end;
  Result:=iSum;
 end;
 exports
  GetSumRange;
- Code gọi hàm trong VBA
Mã:
Declare Function GetSumRange Lib "DLLSum.dll" (ByVal arr As Variant) As Double
Sub TestTinhTong()
    Dim t As Single, lr As Long, arr As Variant
    t = Timer
    With Sheet1
        lr = .Range("A1000000").End(3).Row
        arr = .Range("A1:K" & lr).value
    End With
    MsgBox GetSumRange(arr), , Timer - t
End Sub
 
Upvote 0
Nhờ ACE giúp mình tối ưu đoạn code tính tổng 1 vùng trong excel, mình thử test tốc độ với mảng 11 cột và 500 ngàn dòng thì VBA chạy 0,8s còn delphi thì 1,1s, mình nghĩ do chưa tối ưu được mảng truyền vào, đọc chủ đề này thấy anh Nguyễn Duy Tuân có nói phải dùng PSafeArray.

- Code delphi
Mã:
function GetSumRange(arr:Variant):Double;stdcall;
 var
  i,j:uint32;
  iSum:Double;
 begin
  iSum:=0;
  for i := VarArrayLowBound(arr,1) to VarArrayhighBound(arr,1) do
    begin
      for j := varArrayLowBound(arr,2) to varArrayHighBound(arr,2) do
         iSum:=iSum + arr[i,j];
    end;
  Result:=iSum;
 end;

Em thử sửa lại như thế này xem nhanh hơn không? Anh chưa test nhé.
Mã:
function GetSumRange(var arr: Variant): Double; stdcall;
 var
  i,j: uint32;
  iSum: Double;
 begin
  iSum := 0;
  for i := VarArrayLowBound(arr,1) to VarArrayhighBound(arr,1) do
    begin
      for j := varArrayLowBound(arr,2) to varArrayHighBound(arr,2) do
         iSum := iSum + TVarData(arr[i,j]).VDouble;
    end;
  Result := iSum;
 end;

Kiểu khai báo tham số của hàm anh thêm "var", trong VBA em phải để ByRef như sau.
Mã:
Declare Function GetSumRange Lib "DLLSum.dll" (ByRef arr As Variant) As Double
 
Upvote 0
Kiểu khai báo tham số của hàm anh thêm "var", trong VBA em phải để ByRef như sau.
Mã:
Declare Function GetSumRange Lib "DLLSum.dll" (ByRef arr As Variant) As Double
tốc độ giờ là 0,7s anh, đã nhanh hơn code vba 0,1s, anh cho em hỏi TVarData là 1 record và Vdouble là 1 tham số hình thức, Khi TVarData gọi phương thức thì phải .varDouble chứ a, và tại sao trong vba mình phải dùng Byref ạ ( e thử để byval thì văng app)
 
Upvote 0
tốc độ giờ là 0,7s anh, đã nhanh hơn code vba 0,1s, anh cho em hỏi TVarData là 1 record và Vdouble là 1 tham số hình thức, Khi TVarData gọi phương thức thì phải .varDouble chứ a, và tại sao trong vba mình phải dùng Byref ạ

Hỏi toàn câu khôn thế :).

Variant là một kiểu hỗn hợp, nó ghi được nhiều kiểu giá trị, mỗi kiểu giá trị nó lưu vào một field của variant record.
Ví dụ biến v kiểu Variant, nó được gán

v = 120.40
Thì cấu trúc của v được lưu v.VType = varDouble; v.VDouble = 120.40

v = "Tôi là Delphi"
Thì cấu trúc của v được lưu v.VType = varOleStr; v.VOleStr= "Tôi là Delphi"

....

+ Muốn nhận thẳng giá trị mà v lưu trữ thì gọi thông qua cấu trúc TVarData(v).Field (field là trường lưu giá trị nhứ VDouble, VOleStr,...). Nếu không gọi thẳng TVarData(v).Field mà gọi kiểu "dễ hiểu" là iSum = iSum + v thì trình biên dịch phải làm thêm cơ số việc đi so sánh kiểu rồi lấy giá trị nên tốc độ sẽ chậm hơn.
+ Muốn kiểm tra kiểu của biến v đang là varDouble hay varOleStr,.... thì dùng VarType(v).

Từ khóa khai báo tham số dạng reference (địa chỉ vùng nhớ) trong hàm và thủ tục:
Trong Delphi var hoặc out, VB/VBA là ByRef là yêu cầu trỏ vào địa chỉ vùng nhớ của dữ liệu. Với cách này thì tham số chuyển tiếp qua các hàm và thủ tục sẽ nhanh, trình biên dịch không phải đi sao chép dữ liệu, đặc biệt với Array lớn sẽ mất nhiều thời gian vì nó phải sao chép. Khi làm với tham số kiểu tham chiếu phải cẩn thận trong code, không được thay đổi giá trị của nó nếu không có yêu cầu.
 
Upvote 0
Hỏi toàn câu khôn thế :).

Variant là một kiểu hỗn hợp, nó ghi được nhiều kiểu giá trị, mỗi kiểu giá trị nó lưu vào một field của variant record.
Ví dụ biến v kiểu Variant, nó được gán

v = 120.40
Thì cấu trúc của v được lưu v.VType = varDouble; v.VDouble = 120.40

v = "Tôi là Delphi"
Thì cấu trúc của v được lưu v.VType = varOleStr; v.VOleStr= "Tôi là Delphi"

....

+ Muốn nhận thẳng giá trị mà v lưu trữ thì gọi thông qua cấu trúc TVarData(v).Field (field là trường lưu giá trị nhứ VDouble, VOleStr,...). Nếu không gọi thẳng TVarData(v).Field mà gọi kiểu "dễ hiểu" là iSum = iSum + v thì trình biên dịch phải làm thêm cơ số việc đi so sánh kiểu rồi lấy giá trị nên tốc độ sẽ chậm hơn.
+ Muốn kiểm tra kiểu của biến v đang là varDouble hay varOleStr,.... thì dùng VarType(v).

Từ khóa khai báo tham số dạng reference (địa chỉ vùng nhớ) trong hàm và thủ tục:
Trong Delphi var hoặc out, VB/VBA là ByRef là yêu cầu trỏ vào địa chỉ vùng nhớ của dữ liệu. Với cách này thì tham số chuyển tiếp qua các hàm và thủ tục sẽ nhanh, trình biên dịch không phải đi sao chép dữ liệu, đặc biệt với Array lớn sẽ mất nhiều thời gian vì nó phải sao chép. Khi làm với tham số kiểu tham chiếu phải cẩn thận trong code, không được thay đổi giá trị của nó nếu không có yêu cầu.
cám ơn anh nhiều lắm, quả kiến thức quá uyên thâm về delphi, với dạng bài như trên thì còn có thể tối ưu tốc độ nữa được ko anh, vì tốc độ nó cũng chưa vượt trội mấy so với VBA.
 
Upvote 0
cám ơn anh nhiều lắm, quả kiến thức quá uyên thâm về delphi, với dạng bài như trên thì còn có thể tối ưu tốc độ nữa được ko anh, vì tốc độ nó cũng chưa vượt trội mấy so với VBA.
Em thử làm thử yêu cầu tỉnh tổng có điều kiện, như là so sánh theo mã nào đó rồi so sánh tốc độ giữa VBA và Delphi lần nữa.
Phép tính số học thuần tuý có thể tốc độ không chênh nhiều, nhưng khi có nhiều yếu tố khác tham gia vào thì sẽ có sự khác khau nhiều hơn.
 
Lần chỉnh sửa cuối:
Upvote 0
Em thử làm thử yêu cầu tỉnh tổng có điều kiện, như là so sánh theo mã nào đó rồi so sánh tốc độ giữa VBA và Delphi lần nữa.
Phép tính số học thuần tuý có thể tốc độ không chênh nhiều, nhưng khi có nhiều yếu tố khác tham gia vào thì sẽ có sự khác khau nhiều hơn.
Em đã thử pha lẫn text và số, bên Delphi e check vartype(v)=8 , kết quả vẫn same same nhau, em có thử dùng ADO nhưng ADO thì rất chậm luôn, đợi gần 10 phút là chưa ra kết quả ( e có giảm số dòng để check thử code ADO thì vẫn tính ra)
Mã:
function GetSumBySQL(sFullName, sSQL: Variant): double; stdcall;
var
  cnn: TADOConnection;
  qry: TADOQuery;
  i:uint8;
  iSum:double;
begin
  cnn := TADOConnection.Create(nil);
  iSum:=0;
  try
    cnn.ConnectionString := 'Provider=Microsoft.ACE.OLEDB.12.0;Data Source=' + sFullName +
                                       ';Extended Properties="Excel 12.0 Xml;HDR=NO";';
    cnn.Connected := True;
    qry := TADOQuery.Create(nil);
    try
      qry.Connection := cnn;
      qry.SQL.Text := sSQL;
      qry.Open;
      while not qry.Eof do
      begin
        for i := 0 to 10 do
          begin
            iSum:=iSum+qry.fields[i].AsFloat;
          end;
      qry.Next;
      end;
      Result := iSum;
    finally
      qry.Free;
    end;
  finally
    cnn.Free;
  end;
end;
 
Upvote 0
Dubgf ADO làm gì vì nó không liên quan đến test array. Ý anh là em chạy vòng lặp rồi tính tổng những dòng thoả mãn điều kiện làm cở sở so sánh với VBA. Chỉ test cho em biết thôi chứ Delphi nhanh thì không còn phải thử nghiệm nhiều đâu.
 
Upvote 0
Dubgf ADO làm gì vì nó không liên quan đến test array. Ý anh là em chạy vòng lặp rồi tính tổng những dòng thoả mãn điều kiện làm cở sở so sánh với VBA. Chỉ test cho em biết thôi chứ Delphi nhanh thì không còn phải thử nghiệm nhiều đâu.
như e đã nói bên trên dữ liệu xem kẽ text và số, thời gian chạy VBA 1,4s, Delphi 1,2S . Có thể trên Excel đã quen với việc dùng mảng, nhưng nếu giải thuật lấy dữ liệu lên và xử lý bằng SQL nhanh hơn thì cũng tốt a
Code Delphi
Mã:
function GetSumRange(var arr: Variant): Double; stdcall;
 var
  i,j: uint32;
  iSum: Double;
 begin
  iSum := 0;
  for i := VarArrayLowBound(arr,1) to VarArrayhighBound(arr,1) do
    begin
      for j := varArrayLowBound(arr,2) to varArrayHighBound(arr,2) do
        begin
         if vartype(arr[i,j])=5 then
            iSum :=  iSum + TVarData(arr[i,j]).VDouble;
        end;
    end;
  Result :=iSum;
 end;
Code VBA
Mã:
Sub TestGetSumVBA()
    Dim lr As Long, arr As Variant, isum As Double, i As Long, j As Long, t As Single
    t = Timer
    With Sheet1
        lr = .Range("A1000000").End(3).Row
        arr = .Range("A1:K" & lr).value
    End With
    For i = 1 To UBound(arr)
        For j = 1 To UBound(arr, 2)
            If IsNumeric(arr(i, j)) Then isum = isum + arr(i, j)
        Next j
    Next i
    MsgBox isum, , Timer - t
End Sub
 
Upvote 0
Bài test của em không đúng ý mình và không chạm vào tình huống thực tế. Giả xử trong mảng có cột là mã hàng, cột còn lại là số cần tính tổng. Em hãy viết code tổng tiền của một mã hàng. Không dùng SQL để so sánh VBA và Delphi. Code trên của em dùng so sánh số long/internet không có ý nghĩa gì về phép toán liên quan đến tốc độ cả.

Vấn đề về SQL không liên quan đến chủ đề em đang hỏi vì SQL là một lĩnh vực gần như độc lập với ngôn ngữ và trình biên dịch. Ví dụ hai ngôn ngữ lập trình đều dùng ADO thì nó có thể coi là như nhau vì tốc độ nó nằm bên trong thằng ADO chứ không phải ngôn ngữ lập trình.
 
Upvote 0
Em viết code này, kiểm tra mãi không hiểu nó sai chỗ nào mà các mã hàng đều giống nhau nhưng số lượng nó đã cộng dồn (cái "Code33843" đó chính là cái mã cuối cùng mà nó tìm thấy và add vào dic), tức là nó đã nhận đối tượng khi mình duyệt .values, nhờ anh check giúp, với a cho e hỏi vài câu ạ:
1. Khi debug code : các biến truyền từ excel vào nó đều ko hiện giá trị trong quá trình chạy code, như hình bên dưới ( có cách nào để xem ko ạ)
2. Cái dòng em bôi đỏ là giải phóng PD: theo nguyên lý thì mình duyệt xong tất cả rồi mới giải phóng chứ, mà khi em viết như thế thì nó hiện cảnh báo là PD có thể ko được khởi tạo, còn nếu giải phóng bên trong vòng lặp, nghĩa là mỗi lần duyệt là mỗi lần giải phóng thì nó ko cảnh báo, vậy cái nào tốt hơn a.
Mã:
type
  Product=class
    private
    ProductCode:PWideChar;
    quantity: Double;
    public
    Constructor Create(PC:PWideChar;Qu:Double); overload;
  end;
{$R *.res}
{ Product }
Constructor Product.Create(PC: PWideChar; Qu: Double);
begin
   ProductCode:=PC;
   quantity:=Qu;
end;
function TotalProduct(var arr:Variant): Variant;stdcall;
var
  dic:TDictionary<string,Product>;
  i: UInt32;
  Key:PWideChar;
  Value:Double;
  PD:Product;
  data:Variant;
begin
  dic:=TDictionary<string,Product>.Create;
  try
  for i := VarArrayLowBound(arr,1) to VarArrayHighBound(arr,1) do
    begin
      Key:=TvarData(arr[i,1]).VOleStr;
      Value:=tvardata(arr[i,2]).vdouble;
      if dic.ContainsKey(Key) then
        begin
        PD:=dic.Items[key];
        PD.quantity:=PD.quantity + Value;
        end
      else
        begin
        PD:=Product.Create(Key,Value);
        dic.Add(Key,PD);
        end;
    end;
  data:=VarArrayCreate([1,dic.Count,1,2],varVariant);
  i:=0;
  for PD in dic.Values do
  begin
    i:=i+1;
    data[i,1]:=WideCharToString(PD.ProductCode);
    data[i,2]:=PD.quantity;
  end;
  if PD<>nil then PD.Free;
  finally
    dic.Free;
    Result:=data;
  end;
end;
1633145114218.png1633145159432.png
 
Upvote 0
Em viết code này, kiểm tra mãi không hiểu nó sai chỗ nào mà các mã hàng đều giống nhau nhưng số lượng nó đã cộng dồn (cái "Code33843" đó chính là cái mã cuối cùng mà nó tìm thấy và add vào dic), tức là nó đã nhận đối tượng khi mình duyệt .values, nhờ anh check giúp, với a cho e hỏi vài câu ạ:
1. Khi debug code : các biến truyền từ excel vào nó đều ko hiện giá trị trong quá trình chạy code, như hình bên dưới ( có cách nào để xem ko ạ)
2. Cái dòng em bôi đỏ là giải phóng PD: theo nguyên lý thì mình duyệt xong tất cả rồi mới giải phóng chứ, mà khi em viết như thế thì nó hiện cảnh báo là PD có thể ko được khởi tạo, còn nếu giải phóng bên trong vòng lặp, nghĩa là mỗi lần duyệt là mỗi lần giải phóng thì nó ko cảnh báo, vậy cái nào tốt hơn a.
Mã:
type
  Product=class
    private
    ProductCode:PWideChar;
    quantity: Double;
    public
    Constructor Create(PC:PWideChar;Qu:Double); overload;
  end;
{$R *.res}
{ Product }
Constructor Product.Create(PC: PWideChar; Qu: Double);
begin
   ProductCode:=PC;
   quantity:=Qu;
end;
function TotalProduct(var arr:Variant): Variant;stdcall;
var
  dic:TDictionary<string,Product>;
  i: UInt32;
  Key:PWideChar;
  Value:Double;
  PD:Product;
  data:Variant;
begin
  dic:=TDictionary<string,Product>.Create;
  try
  for i := VarArrayLowBound(arr,1) to VarArrayHighBound(arr,1) do
    begin
      Key:=TvarData(arr[i,1]).VOleStr;
      Value:=tvardata(arr[i,2]).vdouble;
      if dic.ContainsKey(Key) then
        begin
        PD:=dic.Items[key];
        PD.quantity:=PD.quantity + Value;
        end
      else
        begin
        PD:=Product.Create(Key,Value);
        dic.Add(Key,PD);
        end;
    end;
  data:=VarArrayCreate([1,dic.Count,1,2],varVariant);
  i:=0;
  for PD in dic.Values do
  begin
    i:=i+1;
    data[i,1]:=WideCharToString(PD.ProductCode);
    data[i,2]:=PD.quantity;
  end;
  if PD<>nil then PD.Free;
  finally
    dic.Free;
    Result:=data;
  end;
end;
View attachment 267096View attachment 267097

Chỉ test một chút về tốc độ thôi nhưng em lại đưa cả Dic vào làm thêm việc rồi. Code của em trong Constructor bị thiếu một dòng lệnh quan trọng.

Mình sẽ chỉnh cách viết code trong Delphi, cách đặt tên, format nó bạn chú ý, so sánh và nên làm theo chuẩn ngôn ngữ:
1. Kiểu dữ liệu định nghĩa có chứ "T" đứng đầu. Ví dụ class của em tạo tên "Produce" thì nên đặt là "TProduct".
2. Ký tự đầu mỗi word thì viết hoa. Ví dụ "productname" thì Pascal quy ước viết là ProductName.
3. Pascal trước kia do giới hạn không gian soạn thảo thì người ta viết liền các toán tử và biểu thức. Delphi thì tạo một khoảng cách "space". VÍ dụ, Pascal viết là: v:=1+2+3; thì Delphi định dạng là: v := 1 + 2 + 3;
...
Còn vài nguyên tắc nữa em nên tìm hiểu thêm

Mã:
type
  TProduct = class
  private
    ProductCode: PWideChar;
    Quantity: Double;
  public
    constructor Create(PC: PWideChar; Qu: Double); overload;
  end;

{$R *.res}
  { TProduct }

constructor TProduct.Create(PC: PWideChar; Qu: Double);
begin
  Inherited Create; // Phải thêm vào thừa kế
  ProductCode := PC;
  Quantity := Qu;
end;

function TotalProduct(var arr: Variant): Variant; stdcall;
var
  dic: TDictionary<string, TProduct>;
  i: Integer;
  Key: PWideChar;
  Value: Double;
  PD: TProduct;
  Data: Variant;
begin
  dic := TDictionary<string, TProduct>.Create;
  try
    for i := VarArrayLowBound(arr, 1) to VarArrayHighBound(arr, 1) do
    begin
      //Kiểm tra tồn tại không đồng thời nhận luôn Product nếu đã có
      Key := TVarData(arr[i, 1]).VOleStr;
      if dic.TryGetValue(Key, PD) then
        PD.Quantity := PD.Quantity + TVarData(arr[i, 2]).VDouble
      else
      begin
        PD := TProduct.Create(Key, TVarData(arr[i, 2]).VDouble);
        dic.Add(Key, PD);
      end;
    end;
    Data := VarArrayCreate([1, dic.Count, 1, 2], varVariant);
    i := 0;
    for PD in dic.Values do
    begin
      i := i + 1;
      Data[i, 1] := string(PD.ProductCode);
      Data[i, 2] := PD.Quantity;
      PD.Free; //Giải phóng trong vòng lặp
    end;
  finally
    dic.Free;
    Result := Data;
  end;
end;

Trả lời các câu hỏi:
1. Trong Delphi không như bên VB6/VBA, khi debug nó không hiển thị giá trị cũng như các thành phần của các Interface, Variant. Việc này người lập trình phải nắm rõ cấu trúc hoặc phân tách giá trị để test.
2. Lỗi khi giải phòng là do trong Constructor không có "Inherited". Anh đã sửa trong code trên.

Ý của anh là em chỉ cần viết một thủ tục test nhỏ thế này thôi để so sánh tốc độ. VÌ khi so sách tốc độ thì giảm tối đa việc đưa các thành phần bên ngoài chen vào.

Code test:
Mã:
function TestSUM(var arr: Variant): Double; stdcall;
var
  I: Integer;
begin
  Result := 0;
  for I := VarArrayLowBound(arr, 1) to VarArrayHighBound(arr, 1) do
  begin
    if TVarData(arr[I, 1]).VOleStr = 'ABC' then
      Result := Result + TVarData(arr[I, 2]).VDouble
  end;
end;

Đoạn code mình sửa và thêm chưa test, nhưng đoán 100% ok. Khi em chạy nếu có lỗi đâu thì sửa đó.
 
Lần chỉnh sửa cuối:
Upvote 0
2. Lỗi khi giải phòng là do trong Constructor không có "Inherited". Anh đã sửa trong code trên.
1. Cái này ko phải lỗi mà chỉ là warning thôi a, và khi thêm Inherited thì vẫn còn cái cảnh báo đó, nhưng e nghĩ nó cũng ko quan trọng vì mình hiểu được sẽ huỷ lúc nào.
2. Code bên trên a viết thiếu chỗ gán biến Key nếu key chưa tồn tại, nhưng gán xong kết quả vẫn như ban đầu e gửi lên
Mã:
if dic.TryGetValue(TVarData(arr[i, 1]).VOleStr, PD) then
        PD.Quantity := PD.Quantity + TVarData(arr[i, 2]).VDouble
      else
      begin
        Key:=TVarData(arr[i, 1]).VOleStr;
        PD := TProduct.Create(Key, TVarData(arr[i, 2]).VDouble);
        dic.Add(Key, PD);
      end;
3. Code này e sửa các biến Pwidechar thành string là nó chạy a, tốc độ quá lý tưởng, 0,9s, trong khi code trên VBA nếu code đúng theo logic code delphi thì chạy treo máy luôn ( ko biết có phải lỗi ko), còn code khai báo mảng mặc định ban đầu thì chạy 5,5s
==> code như delphi ( chạy treo máy)
Mã:
Sub testTotalProductVBA()
    Dim arr As Variant, t As Single, dic As Object, i As Long, kq()
    t = Timer
    Set dic = CreateObject("Scripting.Dictionary")
    With Sheet1
        arr = .Range("A3:B500000").value
        For i = 1 To UBound(arr)
            If dic.exists(arr(i, 1)) Then
                dic.Item(arr(i, 1)) = dic.Item(arr(i, 1)) + arr(i, 2)
            Else
                dic.Add arr(i, 1), arr(i, 2)
            End If
        Next i
        ReDim kq(1 To dic.Count, 1 To 2)
        For i = 1 To dic.Count
            kq(i, 1) = dic.keys()(i - 1)
            kq(i, 2) = dic.items()(i - 1)
            If i = 1000 Then Stop
        Next i
        .Range("D3").Resize(1000000, 2).ClearContents
        .Range("D3").Resize(i - 1, 2).value = kq
    End With
    MsgBox Timer - t
End Sub
==> Code chạy 5,5s
Mã:
Sub testTotalProductVBA2()
    Dim arr As Variant, t As Single, dic As Object, i As Long, kq(), k As Long
    t = Timer
    Set dic = CreateObject("Scripting.Dictionary")
    With Sheet1
        arr = .Range("A3:B500000").value
        ReDim kq(1 To UBound(arr), 1 To 2)
        For i = 1 To UBound(arr)
            If dic.exists(arr(i, 1)) Then
                kq(dic.Item(arr(i, 1)), 2) = kq(dic.Item(arr(i, 1)), 2) + arr(i, 2)
            Else
                k = k + 1
                kq(k, 1) = arr(i, 1)
                kq(k, 2) = arr(i, 2)
                dic.Add arr(i, 1), k
            End If
        Next i
        .Range("D3").Resize(1000000, 2).ClearContents
        .Range("D3").Resize(k, 2).value = kq
    End With
    MsgBox Timer - t
End Sub
 
Upvote 0
1. Cái này ko phải lỗi mà chỉ là warning thôi a, và khi thêm Inherited thì vẫn còn cái cảnh báo đó, nhưng e nghĩ nó cũng ko quan trọng vì mình hiểu được sẽ huỷ lúc nào.
2. Code bên trên a viết thiếu chỗ gán biến Key nếu key chưa tồn tại, nhưng gán xong kết quả vẫn như ban đầu e gửi lên
Mã:
if dic.TryGetValue(TVarData(arr[i, 1]).VOleStr, PD) then
        PD.Quantity := PD.Quantity + TVarData(arr[i, 2]).VDouble
      else
      begin
        Key:=TVarData(arr[i, 1]).VOleStr;
        PD := TProduct.Create(Key, TVarData(arr[i, 2]).VDouble);
        dic.Add(Key, PD);
      end;
3. Code này e sửa các biến Pwidechar thành string là nó chạy a, tốc độ quá lý tưởng, 0,9s, trong khi code trên VBA nếu code đúng theo logic code delphi thì chạy treo máy luôn ( ko biết có phải lỗi ko), còn code khai báo mảng mặc định ban đầu thì chạy 5,5s
==> code như delphi ( chạy treo máy)
Mã:
Sub testTotalProductVBA()
    Dim arr As Variant, t As Single, dic As Object, i As Long, kq()
    t = Timer
    Set dic = CreateObject("Scripting.Dictionary")
    With Sheet1
        arr = .Range("A3:B500000").value
        For i = 1 To UBound(arr)
            If dic.exists(arr(i, 1)) Then
                dic.Item(arr(i, 1)) = dic.Item(arr(i, 1)) + arr(i, 2)
            Else
                dic.Add arr(i, 1), arr(i, 2)
            End If
        Next i
        ReDim kq(1 To dic.Count, 1 To 2)
        For i = 1 To dic.Count
            kq(i, 1) = dic.keys()(i - 1)
            kq(i, 2) = dic.items()(i - 1)
            If i = 1000 Then Stop
        Next i
        .Range("D3").Resize(1000000, 2).ClearContents
        .Range("D3").Resize(i - 1, 2).value = kq
    End With
    MsgBox Timer - t
End Sub
==> Code chạy 5,5s
Mã:
Sub testTotalProductVBA2()
    Dim arr As Variant, t As Single, dic As Object, i As Long, kq(), k As Long
    t = Timer
    Set dic = CreateObject("Scripting.Dictionary")
    With Sheet1
        arr = .Range("A3:B500000").value
        ReDim kq(1 To UBound(arr), 1 To 2)
        For i = 1 To UBound(arr)
            If dic.exists(arr(i, 1)) Then
                kq(dic.Item(arr(i, 1)), 2) = kq(dic.Item(arr(i, 1)), 2) + arr(i, 2)
            Else
                k = k + 1
                kq(k, 1) = arr(i, 1)
                kq(k, 2) = arr(i, 2)
                dic.Add arr(i, 1), k
            End If
        Next i
        .Range("D3").Resize(1000000, 2).ClearContents
        .Range("D3").Resize(k, 2).value = kq
    End With
    MsgBox Timer - t
End Sub

Đã làm Delphi thì cứ yên tâm về tốc độ. Theo hiểu biết cũng như những gì mình test thì tốc độ như C++. Xét chung thì VBA không có cửa để so sánh tốc độ với Delphi đâu. Một bên là script một bên là mã máy (Delphi), chưa kể những hàm hệ thống các chuyên gia viết bằng Assemply.
 
Upvote 0
Web KT
Back
Top Bottom