Nhờ VBA cho nhiều file excel

Liên hệ QC

Gió mùa

Thành viên mới
Tham gia
15/2/19
Bài viết
6
Được thích
1
Em xin chào các anh/chị trong diễn đàn.
Em xin nhờ mọi người giúp em viết VBA để giải quyết vấn đề như trong file.
Mục đích:
1.Em muốn lấy tổng Qty từ sheet L1->L4 vào cột D của sheet L0, sau đó so sánh ở cột E.
2.Em có khoảng 300 model tương ứng với 300 file excel lưu trong forder C:\Users\22A5525\Desktop\Model, với mỗi file excel đều có format giống nhau(có khác nhau về số sheet)
Em nhờ các anh/chị viết code để có thể thực hiện vấn đề 1 cho cả 300 file excel này.
Em cảm ơn.
 

File đính kèm

  • Model HR-NC5525.xlsx
    15.2 KB · Đọc: 20
Em xin chào các anh/chị trong diễn đàn.
Em xin nhờ mọi người giúp em viết VBA để giải quyết vấn đề như trong file.
Mục đích:
1.Em muốn lấy tổng Qty từ sheet L1->L4 vào cột D của sheet L0, sau đó so sánh ở cột E.
2.Em có khoảng 300 model tương ứng với 300 file excel lưu trong forder C:\Users\22A5525\Desktop\Model, với mỗi file excel đều có format giống nhau(có khác nhau về số sheet)
Em nhờ các anh/chị viết code để có thể thực hiện vấn đề 1 cho cả 300 file excel này.
Em cảm ơn.
Copy File vào chung thư mục với các file cần xử lý, mở file và bấn mặt cười
Mã:
Sub GPE()
  Dim wb As Workbook, sh As Worksheet, Dic As Object
  Dim Fso As Object, ObjFoder As Object, ObjFile As Object
  Dim Arr(), sArr(), Res()
  Dim i As Long, ik As Long, n As Long, eRow As Long

  Set Dic = CreateObject("Scripting.dictionary")
  Set Fso = CreateObject("Scripting.FileSystemObject")
  Set ObjFoder = Fso.GetFolder(ThisWorkbook.Path)
  Application.ScreenUpdating = False
  On Error Resume Next
  For Each ObjFile In ObjFoder.Files
    If ObjFile.Name <> ThisWorkbook.Name And Fso.GetExtensionName(ObjFile) Like "xlsx" Then
      Set wb = Workbooks.Open(ObjFile)
      With wb.Sheets("L 0")
        eRow = .Range("A1000000").End(xlUp).Row
        If eRow > 1 Then
          Arr = .Range("A2:B" & eRow).Value
          ReDim Res(1 To UBound(Arr), 1 To 2)
          For i = 1 To UBound(Arr)
            Dic.Item(Arr(i, 1)) = i
          Next i
          For n = 1 To 4
            Set sh = wb.Sheets("L " & n)
            eRow = sh.Range("A1000000").End(xlUp).Row
            If eRow > 1 Then
              sArr = sh.Range("A2:B" & eRow).Value
              For i = 1 To UBound(sArr)
                ik = Dic.Item(sArr(i, 1))
                If ik > 0 Then Res(ik, 1) = Res(ik, 1) + sArr(i, 2)
              Next i
            End If
          Next n
          For i = 1 To UBound(Arr)
            Res(i, 2) = Arr(i, 2) - Res(i, 1)
          Next i
          .Range("D2:E2").Resize(UBound(Res)) = Res
        End If
      End With
      wb.Close True
    End If
  Next
  Application.ScreenUpdating = True
  Set Fso = Nothing:  Set ObjFoder = Nothing: Set ObjFile = Nothing
  Set wb = Nothing: Set sh = Nothing: Set Dic = Nothing
End Sub
 

File đính kèm

  • GPE.xlsm
    17.5 KB · Đọc: 19
Upvote 0
Copy File vào chung thư mục với các file cần xử lý, mở file và bấn mặt cười
Mã:
Sub GPE()
  Dim wb As Workbook, sh As Worksheet, Dic As Object
  Dim Fso As Object, ObjFoder As Object, ObjFile As Object
  Dim Arr(), sArr(), Res()
  Dim i As Long, ik As Long, n As Long, eRow As Long

  Set Dic = CreateObject("Scripting.dictionary")
  Set Fso = CreateObject("Scripting.FileSystemObject")
  Set ObjFoder = Fso.GetFolder(ThisWorkbook.Path)
  Application.ScreenUpdating = False
  On Error Resume Next
  For Each ObjFile In ObjFoder.Files
    If ObjFile.Name <> ThisWorkbook.Name And Fso.GetExtensionName(ObjFile) Like "xlsx" Then
      Set wb = Workbooks.Open(ObjFile)
      With wb.Sheets("L 0")
        eRow = .Range("A1000000").End(xlUp).Row
        If eRow > 1 Then
          Arr = .Range("A2:B" & eRow).Value
          ReDim Res(1 To UBound(Arr), 1 To 2)
          For i = 1 To UBound(Arr)
            Dic.Item(Arr(i, 1)) = i
          Next i
          For n = 1 To 4
            Set sh = wb.Sheets("L " & n)
            eRow = sh.Range("A1000000").End(xlUp).Row
            If eRow > 1 Then
              sArr = sh.Range("A2:B" & eRow).Value
              For i = 1 To UBound(sArr)
                ik = Dic.Item(sArr(i, 1))
                If ik > 0 Then Res(ik, 1) = Res(ik, 1) + sArr(i, 2)
              Next i
            End If
          Next n
          For i = 1 To UBound(Arr)
            Res(i, 2) = Arr(i, 2) - Res(i, 1)
          Next i
          .Range("D2:E2").Resize(UBound(Res)) = Res
        End If
      End With
      wb.Close True
    End If
  Next
  Application.ScreenUpdating = True
  Set Fso = Nothing:  Set ObjFoder = Nothing: Set ObjFile = Nothing
  Set wb = Nothing: Set sh = Nothing: Set Dic = Nothing
End Sub
Anh Hiếu ơi 300 file dùng ADO được không anh.Anh Làm mẫu em học tập.:D.
 
Upvote 0
Em xin chào các anh/chị trong diễn đàn.
Em xin nhờ mọi người giúp em viết VBA để giải quyết vấn đề như trong file.
Mục đích:
1.Em muốn lấy tổng Qty từ sheet L1->L4 vào cột D của sheet L0, sau đó so sánh ở cột E.
2.Em có khoảng 300 model tương ứng với 300 file excel lưu trong forder C:\Users\22A5525\Desktop\Model, với mỗi file excel đều có format giống nhau(có khác nhau về số sheet)
Em nhờ các anh/chị viết code để có thể thực hiện vấn đề 1 cho cả 300 file excel này.
Em cảm ơn.
Bài #2 đã có câu trả lời cho bạn rồi.
Tôi góp vui 1 chút.
Vẫn là copy file này vào cùng thư mục với 300 file bạn cần thao tác và chạy code
PHP:
Sub GPE()
    'Dim FSo As Scripting.FileSystemObject
    'Set FSo = New Scripting.FileSystemObject
    Dim FSo As Object
    Set FSo = CreateObject("Scripting.FileSystemObject")
    Dim oFolder As Object
    Dim File As Variant, Wb As Workbook
    
    Set oFolder = FSo.GetFolder(ThisWorkbook.Path)
    
    Application.ScreenUpdating = False
    Application.DisplayAlerts = False
    
    For Each File In oFolder.Files
       If Right(FSo.GetFileName(File), 4) = "xlsx" Then
            Set Wb = Workbooks.Open(File)
            With Wb.Sheets("L 0")
                .Range("A2", .Range("A2").End(xlDown)).Offset(, 3).Formula = _
                        "=SUMPRODUCT(SUMIF(INDIRECT(""'""&{""L 1"";""L 2"";""L 3""}&""'!$A$2:$A$100""),'L 0'!$A2,INDIRECT(""'""&{""L 1"";""L 2"";""L 3""}&""'!$B$2:$B$100"")))"
                .Range("A2", .Range("A2").End(xlDown)).Offset(, 4).Formula = "=B2-D2"
            End With
            Wb.Close True
       End If
    Next File
    
    Application.ScreenUpdating = True
    Application.DisplayAlerts = True
    
    Set FSo = Nothing: Set oFolder = Nothing
    MsgBox "Done", vbInformation, "GPE"
End Sub
 

File đính kèm

  • FileSystemObject.xlsb
    14.4 KB · Đọc: 4
Upvote 0
Copy File vào chung thư mục với các file cần xử lý, mở file và bấn mặt cười
Mã:
Sub GPE()
  Dim wb As Workbook, sh As Worksheet, Dic As Object
  Dim Fso As Object, ObjFoder As Object, ObjFile As Object
  Dim Arr(), sArr(), Res()
  Dim i As Long, ik As Long, n As Long, eRow As Long

  Set Dic = CreateObject("Scripting.dictionary")
  Set Fso = CreateObject("Scripting.FileSystemObject")
  Set ObjFoder = Fso.GetFolder(ThisWorkbook.Path)
  Application.ScreenUpdating = False
  On Error Resume Next
  For Each ObjFile In ObjFoder.Files
    If ObjFile.Name <> ThisWorkbook.Name And Fso.GetExtensionName(ObjFile) Like "xlsx" Then
      Set wb = Workbooks.Open(ObjFile)
      With wb.Sheets("L 0")
        eRow = .Range("A1000000").End(xlUp).Row
        If eRow > 1 Then
          Arr = .Range("A2:B" & eRow).Value
          ReDim Res(1 To UBound(Arr), 1 To 2)
          For i = 1 To UBound(Arr)
            Dic.Item(Arr(i, 1)) = i
          Next i
          For n = 1 To 4
            Set sh = wb.Sheets("L " & n)
            eRow = sh.Range("A1000000").End(xlUp).Row
            If eRow > 1 Then
              sArr = sh.Range("A2:B" & eRow).Value
              For i = 1 To UBound(sArr)
                ik = Dic.Item(sArr(i, 1))
                If ik > 0 Then Res(ik, 1) = Res(ik, 1) + sArr(i, 2)
              Next i
            End If
          Next n
          For i = 1 To UBound(Arr)
            Res(i, 2) = Arr(i, 2) - Res(i, 1)
          Next i
          .Range("D2:E2").Resize(UBound(Res)) = Res
        End If
      End With
      wb.Close True
    End If
  Next
  Application.ScreenUpdating = True
  Set Fso = Nothing:  Set ObjFoder = Nothing: Set ObjFile = Nothing
  Set wb = Nothing: Set sh = Nothing: Set Dic = Nothing
End Sub
Em cảm ơn anh nhiều ạ. kết quả tuyệt vời.
Em cũng đang tập tành tìm hiểu về VBA.
Em có 1 thắc mắc là sao trong bài này anh dùng Dic duyệt qua nhiều sheet mà lại chỉ redim Res mà không phải là Redim Preserve?
Em cảm ơn.
 
Upvote 0
Em cảm ơn anh nhiều ạ. kết quả tuyệt vời.
Em cũng đang tập tành tìm hiểu về VBA.
Em có 1 thắc mắc là sao trong bài này anh dùng Dic duyệt qua nhiều sheet mà lại chỉ redim Res mà không phải là Redim Preserve?
Em cảm ơn.
Thắc mắc trật chỗ rồi.
Đáng lẽ phải hỏi là tại sao không xoá trống Dic ở mỗi sheet

(muốn biết tại sao không Redim Preserve thì thử sửa code, nhét "Preserve" vô thì biết liền)
 
Upvote 0
Thắc mắc trật chỗ rồi.
Đáng lẽ phải hỏi là tại sao không xoá trống Dic ở mỗi sheet

(muốn biết tại sao không Redim Preserve thì thử sửa code, nhét "Preserve" vô thì biết liền)
Dạ. em cảm ơn bác đã ghóp ý.
nhưng mà quả thực em mới tập tành ngâm cứu code thôi nên chưa đủ "trình" để hỏi được đến như vậy.
Đọc bài trên GPE thấy duyệt qua nhiều sheet có sử dụng Redim Preserve nên em mới thắc mắc.
Vậy để thử nhét vô vậy.
Em cảm ơn.
 
Upvote 0
Em cảm ơn anh nhiều ạ. kết quả tuyệt vời.
Em cũng đang tập tành tìm hiểu về VBA.
Em có 1 thắc mắc là sao trong bài này anh dùng Dic duyệt qua nhiều sheet mà lại chỉ redim Res mà không phải là Redim Preserve?
Em cảm ơn.
Redim là xóa toàn bộ dữ liệu trước, Redim preserve thường mở rộng mảng và giữ lại dữ liệu trước
Code quên xóa Dic, chỉnh lại
Mã:
Sub GPE()
  Dim wb As Workbook, sh As Worksheet, Dic As Object
  Dim Fso As Object, ObjFoder As Object, ObjFile As Object
  Dim Arr(), sArr(), Res()
  Dim i As Long, ik As Long, n As Long, eRow As Long

  Set Dic = CreateObject("Scripting.dictionary")
  Set Fso = CreateObject("Scripting.FileSystemObject")
  Set ObjFoder = Fso.GetFolder(ThisWorkbook.Path)
  Application.ScreenUpdating = False
  On Error Resume Next
  For Each ObjFile In ObjFoder.Files
    If ObjFile.Name <> ThisWorkbook.Name And Fso.GetExtensionName(ObjFile) Like "xlsx" Then
      Set wb = Workbooks.Open(ObjFile)
      With wb.Sheets("L 0")
        eRow = .Range("A1000000").End(xlUp).Row
        If eRow > 1 Then
          Arr = .Range("A2:B" & eRow).Value
          ReDim Res(1 To UBound(Arr), 1 To 2)
          For i = 1 To UBound(Arr)
            Dic.Item(Arr(i, 1)) = i
          Next i
          For n = 1 To 4
            Set sh = wb.Sheets("L " & n)
            eRow = sh.Range("A1000000").End(xlUp).Row
            If eRow > 1 Then
              sArr = sh.Range("A2:B" & eRow).Value
              For i = 1 To UBound(sArr)
                ik = Dic.Item(sArr(i, 1))
                If ik > 0 Then Res(ik, 1) = Res(ik, 1) + sArr(i, 2)
              Next i
            End If
          Next n
          For i = 1 To UBound(Arr)
            Res(i, 2) = Arr(i, 2) - Res(i, 1)
          Next i
          .Range("D2:E2").Resize(UBound(Res)) = Res
          Dic.removeall
        End If
      End With      
      wb.Close True
    End If
  Next
  Application.ScreenUpdating = True
  Set Fso = Nothing:  Set ObjFoder = Nothing: Set ObjFile = Nothing
  Set wb = Nothing: Set sh = Nothing: Set Dic = Nothing
End Sub
 
Upvote 0
Redim là xóa toàn bộ dữ liệu trước, Redim preserve thường mở rộng mảng và giữ lại dữ liệu trước
Code quên xóa Dic, chỉnh lại
Mã:
Sub GPE()
  Dim wb As Workbook, sh As Worksheet, Dic As Object
  Dim Fso As Object, ObjFoder As Object, ObjFile As Object
  Dim Arr(), sArr(), Res()
  Dim i As Long, ik As Long, n As Long, eRow As Long

  Set Dic = CreateObject("Scripting.dictionary")
  Set Fso = CreateObject("Scripting.FileSystemObject")
  Set ObjFoder = Fso.GetFolder(ThisWorkbook.Path)
  Application.ScreenUpdating = False
  On Error Resume Next
  For Each ObjFile In ObjFoder.Files
    If ObjFile.Name <> ThisWorkbook.Name And Fso.GetExtensionName(ObjFile) Like "xlsx" Then
      Set wb = Workbooks.Open(ObjFile)
      With wb.Sheets("L 0")
        eRow = .Range("A1000000").End(xlUp).Row
        If eRow > 1 Then
          Arr = .Range("A2:B" & eRow).Value
          ReDim Res(1 To UBound(Arr), 1 To 2)
          For i = 1 To UBound(Arr)
            Dic.Item(Arr(i, 1)) = i
          Next i
          For n = 1 To 4
            Set sh = wb.Sheets("L " & n)
            eRow = sh.Range("A1000000").End(xlUp).Row
            If eRow > 1 Then
              sArr = sh.Range("A2:B" & eRow).Value
              For i = 1 To UBound(sArr)
                ik = Dic.Item(sArr(i, 1))
                If ik > 0 Then Res(ik, 1) = Res(ik, 1) + sArr(i, 2)
              Next i
            End If
          Next n
          For i = 1 To UBound(Arr)
            Res(i, 2) = Arr(i, 2) - Res(i, 1)
          Next i
          .Range("D2:E2").Resize(UBound(Res)) = Res
          Dic.removeall
        End If
      End With     
      wb.Close True
    End If
  Next
  Application.ScreenUpdating = True
  Set Fso = Nothing:  Set ObjFoder = Nothing: Set ObjFile = Nothing
  Set wb = Nothing: Set sh = Nothing: Set Dic = Nothing
End Sub
Đầu tiên em nghĩ cần phải xoá Dic.Nhưng giờ xem lại chắc không cần.Anh có thể giải thích chỗ lỗi được không anh.
 
Upvote 0
Đầu tiên em nghĩ cần phải xoá Dic.Nhưng giờ xem lại chắc không cần.Anh có thể giải thích chỗ lỗi được không anh.
Em xin phép bác @HieuCD để trả lời chỗ này:
- Dic trong trường hợp này được sử dụng để tổng hợp giá trị của các Key duy nhất.
- Các Key duy nhất trong 1 file nhưng có thể tồn tại trong các file khác nhau.
--> sau khi duyệt qua từng file cần phải xóa toàn bộ dữ liệu để làm mới Dic.
 
Upvote 0
Em xin phép bác @HieuCD để trả lời chỗ này:
- Dic trong trường hợp này được sử dụng để tổng hợp giá trị của các Key duy nhất.
- Các Key duy nhất trong 1 file nhưng có thể tồn tại trong các file khác nhau.
--> sau khi duyệt qua từng file cần phải xóa toàn bộ dữ liệu để làm mới Dic.
Nhưng vấn đề ở đây là.Anh Hiếu chỉ lấy vị trí của item đó.
Dic.Item(Arr(i, 1)) = i
Nên khi qua file mới thì nó sẽ duyệt lại rồi nên không cần xóa Dic cũng được mà.Vì khi đó nếu Item nào trùng nó sẽ bị thay thế rồi.Còn mà không trùng kệ nó.:D
 
Upvote 0
Đầu tiên em nghĩ cần phải xoá Dic.Nhưng giờ xem lại chắc không cần.Anh có thể giải thích chỗ lỗi được không anh.
Do mình dùng:
ReDim Res(1 To UBound(Arr), 1 To 2)
Dic.Item(Arr(i, 1)) = i
Nếu số dòng các mảng Arr như nhau thì code luôn chạy không bị lổi
Gọi Arr_n là mảng của sheet sau, Arr_n-1 là mảng sheet trước, nếu ubound(Arr_n-1) > ubound(Arr_n)
lúc đó có khả năng giá trị của sArr không có trong Arr_n và có trong Arr_n-1, và ik > ubound(Arr_n)
với ik = Dic.Item(sArr(i, 1))
Lúc đó sẽ bị lỗi dòng lệnh
If ik > 0 Then Res(ik, 1) = Res(ik, 1) + sArr(i, 2)
 
Lần chỉnh sửa cuối:
Upvote 0
Do mình dùng:
ReDim Res(1 To UBound(Arr), 1 To 2)
Dic.Item(Arr(i, 1)) = i
Nếu số dòng các mảng Arr như nhau thì code luôn chạy đúng
Gọi Arr_n là mảng của sheet sau, Arr_n-1 là mảng sheet trước, nếu ubound(Arr_n-1) > ubound(Arr_n)
lúc đó có khả năng giá trị của sArr không có trong Arr_n và có trong Arr_n-1, và ik > ubound(Arr_n)
với ik = Dic.Item(sArr(i, 1))
Lúc đó sẽ bị lỗi dòng lệnh
If ik > 0 Then Res(ik, 1) = Res(ik, 1) + sArr(i, 2)
À em Hiểu rồi cảm ơn Anh Hiếu nhé.
 
Upvote 0
Đầu tiên em nghĩ cần phải xoá Dic.Nhưng giờ xem lại chắc không cần.Anh có thể giải thích chỗ lỗi được không anh.
Sau 300 files, nếu các keys riêng biệt nhau thì khả năng cái đít phình to đi lạch bạch khá cao.

Redim là xóa toàn bộ dữ liệu trước, Redim preserve thường mở rộng mảng và giữ lại dữ liệu trước
...
Redim Preserve không cho thay độ lớn của chiều thứ nhất (ở đây là mảng 2 chiều).
Nếu dùng thì sẽ bị lỗi khi các dòng không bằng nhau. Và vì code này có bẫy On Error Resume Next cho nên nó sẽ nhảy qua chỗ đó, và kết quả sẽ tùm lum hết.

Chú: làm việc với số dữ liệu như vậy phải cẩn thận với bẫy lỗi. Với 300 files mà chỉ chừng 3-5 trường hợp bị lỗi thì rất khó mà biết được rằng kết quả cuối cùng là rác rưởi.
Đây là trường hợp điển hình của một trong những lý do khiến lệnh "On Error Resume Next" trở nên nguy hiểm.
 
Upvote 0
Sau 300 files, nếu các keys riêng biệt nhau thì khả năng cái đít phình to đi lạch bạch khá cao.


Redim Preserve không cho thay độ lớn của chiều thứ nhất (ở đây là mảng 2 chiều).
Nếu dùng thì sẽ bị lỗi khi các dòng không bằng nhau. Và vì code này có bẫy On Error Resume Next cho nên nó sẽ nhảy qua chỗ đó, và kết quả sẽ tùm lum hết.

Chú: làm việc với số dữ liệu như vậy phải cẩn thận với bẫy lỗi. Với 300 files mà chỉ chừng 3-5 trường hợp bị lỗi thì rất khó mà biết được rằng kết quả cuối cùng là rác rưởi.
Đây là trường hợp điển hình của một trong những lý do khiến lệnh "On Error Resume Next" trở nên nguy hiểm.
Dạ.em cảm ơn bác đã ghóp ý.
Lúc đầu bác nói tự thử rồi biết. em cũng muốn thử nhưng ngặt nỗi nhìn đống code thấy hơi hoảng nên lại thôi.
Rồi em nhìn lại code và tự ngẫm là chắc bác @HieuCD redim bằng 1 mảng cố định rồi, còn duyệt qua các sheet chỉ để lấy value.
Nhưng giờ bác nói thế này em lại thấy mênh mông quá.zzzzz
 
Upvote 0
Dạ.em cảm ơn bác đã ghóp ý.
Lúc đầu bác nói tự thử rồi biết. em cũng muốn thử nhưng ngặt nỗi nhìn đống code thấy hơi hoảng nên lại thôi.
Rồi em nhìn lại code và tự ngẫm là chắc bác @HieuCD redim bằng 1 mảng cố định rồi, còn duyệt qua các sheet chỉ để lấy value.
Nhưng giờ bác nói thế này em lại thấy mênh mông quá.zzzzz

Mảng động mới redim được. Mảng cố định không redim được.
Quả là bạn mênh mông quá. Nên tìm hiểu kỹ hơn căn bản về mảng trước khi học hỏi tiếp.
 
Upvote 0
Web KT
Back
Top Bottom