tối ưu code so sánh dữ liệu giữa sheet1 và sheet2 (2 người xem)

Liên hệ QC

Người dùng đang xem chủ đề này

hoabattu3387

Thành viên chính thức
Tham gia
11/9/08
Bài viết
91
Được thích
2
Hi các anh/chị diễn đàn
em có một file gồm 3 sheets dữ liệu. mục đích là đối chiếu các trường thông tin theo dòng của sheet1 và sheet2 với nhau (từ cột A đến cột H, bỏ qua cột G). nếu dữ liệu nào có ở sheet2 mà ko có ở sheet1 hoặc dữ liệu có ở sheet1 mà không có ở sheet2 thì kết quả gửi sheet3. dữ liệu nào có cả 2 sheet1 và sheet2 thì sum tổng tiền cột C ạ.
Em có viết một đoạn code so sánh một chiều giữa sheet2 và sheet1, nhưng code dài quá, e thấy loằng ngoằng và ko tối ưu, nên chưa viết chiều so sánh giữa sheet1 và sheet2. nhờ các anh/chị giúp e cải thiện nhé!


Sub Oval1_Click()
Dim dic, dic1 As Object, arr1(), arr2(), wrong(), notwrong(), i, j As Integer
Set dic = CreateObject("Scripting.Dictionary")
With Sheet1
arr1 = .Range(.[a1], .[h65536].End(xlUp)).Value
For i = 1 To UBound(arr1, 1)
orgin = Val(.Range("B" & i).Value) & Val(.Range("C" & i).Value) & .Range("e" & i).Value & Val(.Range("h" & i).Value)
If Not dic.exists(orgin) Then
dic.Add orgin, i
End If
Next
End With
With Sheet2
arr2 = .Range(.[a1], .[j65536].End(xlUp)).Value
ReDim wrong(1 To UBound(arr2, 1), 1 To 10)
ReDim notwrong(1 To UBound(arr2, 1), 1 To 10)
k = 1
H = 1
For j = 1 To UBound(arr2, 1)
ORGIN1 = Val(.Range("B" & j).Value) & Val(.Range("C" & j).Value) & .Range("e" & j).Value & .Range("h" & j).Value
If Not dic.exists(ORGIN1) Then
dic.Add ORGIN1, j
For t = 1 To 10
wrong(k, t) = arr2(j, t)
Next
k = k + 1
Else
For m = 1 To 10
notwrong(H, m) = arr2(j, m)
Next
tong = tong + notwrong(H, 3)
H = H + 1
End If
Next
End With
MsgBox (tong)
With Sheet3
.[a1].Resize(k, 10) = wrong


End With
End Sub


 

File đính kèm

bạn cần giải thích sự liên quan của 2 dòng này
đối chiếu các trường thông tin theo dòng của sheet1 và sheet2 với nhau (từ cột A đến cột H, bỏ qua cột G).

Val(.Range("B" & i).Value) & Val(.Range("C" & i).Value) & .Range("e" & i).Value & Val(.Range("h" & i).Value)

code của bạn tôi xem sơ qua thấy không xài được , nếu bạn nói rõ yêu cầu may ra tôi còn làm , chứ kêu tôi đi sửa code đó thì bạn đợi thành viên khác
 
Upvote 0
Hi các anh/chị diễn đàn
em có một file gồm 3 sheets dữ liệu. mục đích là đối chiếu các trường thông tin theo dòng của sheet1 và sheet2 với nhau (từ cột A đến cột H, bỏ qua cột G). nếu dữ liệu nào có ở sheet2 mà ko có ở sheet1 hoặc dữ liệu có ở sheet1 mà không có ở sheet2 thì kết quả gửi sheet3. dữ liệu nào có cả 2 sheet1 và sheet2 thì sum tổng tiền cột C ạ.
chỉ cần trong dòng đó có 1 dữ liệu khác là cho qua sheet3 hết cả à.
ý của bạn là lấy dữ liệu dòng 2 của sheet 1 đem so sánh với dữ liệu từ dòng 2 đến dòng n của sheet 2 nếu không trùng thì cho qua sheet 3
sau đó tiếp dòng 3 của sheet 1
như vậy đúng chứ
tại chỗ này
dữ liệu nào có cả 2 sheet1 và sheet2 thì sum tổng tiền cột C
sau khi sum rồi điền vào chỗ nào
có thể gợi ý bạn thế này bạn tự điều chỉnh lại nha
code bạn viết bằng mảng chắc bạn biết dùng mảng rồi

Dim Mang1, Mang2, Mang3
Mang1= <Dữ liệu sheet 1>
Mang2 = <Dữ liệu sheet2>
for i = 0 to Ubound(Mang1) ' vong lap chay tu 0 den dong cuoi cung cua mang 1
for j = 0 to Ubound(Mang2)' vong lap chay tu 0 den dong cuoi cung cua mang 2
for k =1 to 8
if Mang1(i,k)=Mang2(j,k) then
Tam = True
Else
Tam = False
k=8
end if
if k = 8 and Tam = true then
for k =1 to 8
Mang3(k,i)=Mang1(k,i)
next k
end if
next k
if k =6 then k=k+1' k=6 la cot F +1 next k +1 nua la 8 tuc cot H
next k
next j
next i

chạy hết rồi điền vào Sheet3
vụ tính tổng bạn không nói kỹ nên không giải quyết đc, bạn tự nghĩ tiếp nha
xin lỗi nếu code trên không đúng (vì mình dốt về mảng trong vba lắm)
 
Lần chỉnh sửa cuối:
Upvote 0
Tức là đối chiếu từng dòng của sheet2 (từ cột B đến H bỏ qua cột H) với từng dòng sheet1, nếu không tìm đc dòng nào giống thế thì cả dòng đó sẽ copy sang sheet3 bạn ạ
P/s: khả năng diễn đạt của mình kém quá, hy vọng bạn hiểu. Nếu bạn có ý tưởng khác mình thì góp ý mình nhé. Tks bạn
 
Upvote 0
dúng rồi bạn ạ, đây là dưc liệu của 2 hệ thống của bên mình và đối tác, phần sai khác sẽ cho sang sheet3, phần giống nhau sẽ sum số tiền để thanh toán. Cảm ơn bạn nhé.
 
Upvote 0
dúng rồi bạn ạ, đây là dưc liệu của 2 hệ thống của bên mình và đối tác, phần sai khác sẽ cho sang sheet3, phần giống nhau sẽ sum số tiền để thanh toán. Cảm ơn bạn nhé.
bạn xem đúng yêu cầu không nha
mình có thay đổi dữ liệu để kiểm tra code thế nào
bạn copy lại dữ liệu cũ đi nha
ở đây tại sheet1 và sheet2 có 2 dòng khác nhau
vì vậy sheet 3 sẽ lấy 2 dòng đó luôn
nếu bạn chỉ muốn lấy những cái nào mà sheet1 khác sheet2 thì bỏ khúc code bên trên nha lấy khúc dưới. mình có ký hiệu lại đó.
còn vụ tính tổng bạn chưa nói rõ nên không làm được nha
cái vụ so sánh này làm bù đầu quá (nghĩ hoài chả biết làm thế nào để so sánh)
mai thi rồi mà giờ chưa học chữ nào
 

File đính kèm

Upvote 0
bạn xem đúng yêu cầu không nha
mình có thay đổi dữ liệu để kiểm tra code thế nào
bạn copy lại dữ liệu cũ đi nha
ở đây tại sheet1 và sheet2 có 2 dòng khác nhau
vì vậy sheet 3 sẽ lấy 2 dòng đó luôn
nếu bạn chỉ muốn lấy những cái nào mà sheet1 khác sheet2 thì bỏ khúc code bên trên nha lấy khúc dưới. mình có ký hiệu lại đó.
còn vụ tính tổng bạn chưa nói rõ nên không làm được nha
cái vụ so sánh này làm bù đầu quá (nghĩ hoài chả biết làm thế nào để so sánh)
mai thi rồi mà giờ chưa học chữ nào

Bạn nên cố nghĩ đi ah, làm cho gốt ráo cho xong đi. ...hehe
Nói thế thui, cứ để người hỏi giải thích và tự thân vận động nữa, cứ thế này thì sau mắc lại gọi bạn dậy.
 
Upvote 0
Tối ưu có nghĩa là sao? Code của bạn đã chạy đúng chưa?
Có 2 quan điểm tối ưu:
1. code chạy nhanh, chớp mắt là xong
2. thuật toán vững vàng, dễ kiểm soát.
Hai phương pháp đó rất ít khi có thể hoà hợp với nhau.

Thuật toán tối uu:
- copy sheet1 sang sheet3
- copy sheet2 tiếp theo
- đặt 1 cột, nối các cột A đến F, và Z (chừa G ra). Lý do lả vì Excel đời cũ không cho sort nhiều cột quá.
- sort theo cột này.
- đặt cột điều kiện IF (nếu bằng dòng trên hoặc dưới nó)
- filter theo điều kiện trên
- những cái true là trùng giũa 2 sheets, chép qua 1 bên.
- những cái false là khong trùng, chép qua bên khác.
Code hay làm tay đều được
 
Lần chỉnh sửa cuối:
Upvote 0
Đã tạo Dictionary thì phải tận dụng hết những tính trội của nó chứ.
Nên thêm cột 11 trên sheet3 để ghi dòng nào, sheet nào còn tìm sau này

Mã:
Sub Test()
Dim Dic As Object, Tm1, Tm2, MyStr, eR, a, b, i, j
Set Dic = CreateObject("Scripting.Dictionary")


Tm1 = Sheet1.Range(Sheet1.[a2], Sheet1.[h65536].End(xlUp)).Value
Tm2 = Sheet2.Range(Sheet2.[a2], Sheet2.[j65536].End(xlUp)).Value
eR = IIf(UBound(Tm1, 1) > UBound(Tm2, 1), UBound(Tm1, 1), UBound(Tm2, 1))
For i = 1 To eR
If i <= UBound(Tm1, 1) Then
MyStr = Join(Array(Format(Tm1(i, 2), "000000000000"), Tm1(i, 3), Tm1(i, 5), Val(Tm1(i, 8))), ";")
If Dic.exists(MyStr) Then
Dic.Item(MyStr) = Dic.Item(MyStr) & ";" & i & "-S1"
Else
Dic.Add MyStr, i & "-S1"
End If
End If
If i <= UBound(Tm2, 1) Then
MyStr = Join(Array(Format(Tm2(i, 2), "000000000000"), Tm2(i, 3), Tm2(i, 5), Val(Tm2(i, 8))), ";")
If Dic.exists(MyStr) Then
Dic.Item(MyStr) = Dic.Item(MyStr) & ";" & i & "-S2"
Else
Dic.Add MyStr, i & "-S2"
End If
End If
Next
a = Dic.items
b = Dic.keys
For i = 0 To Dic.Count - 1
If InStr(1, a(i), ";") > 0 Then Dic.Remove (b(i))
Next
Sheet3.[A2:J65000].ClearContents
If Dic.Count > 0 Then
a = Dic.items
eR = 1
For i = 0 To Dic.Count - 1
eR = eR + 1
If InStr(1, a(i), "S1") > 0 Then
For j = 1 To 8
Sheet3.Cells(eR, j) = IIf(j < 3, "'", "") & Tm1(Split(a(i), "-")(0), j)
Next
Sheet3.Cells(eR, 11) = "Dong: " & Split(a(i), "-")(0) & " - " & Sheet1.Name
Else
For j = 1 To 10
Sheet3.Cells(eR, j) = IIf(j < 3, "'", "") & Tm2(Split(a(i), "-")(0), j)
Next
Sheet3.Cells(eR, 11) = "Dong: " & Split(a(i), "-")(0) & " - " & Sheet2.Name
End If
Next
End If
End Sub
 
Upvote 0
Cái này cùng là dịp tốt đẻ bạn nào muốn luyện nghề viết SQL. Có ai muốn thử ADO hôn?
 
Upvote 0
Em cảm ơn anh nhiều ạ! Đây đúng là phương pháp tối ưu mà e cần tìm:)
 
Upvote 0
Hi các anh/chị diễn đàn
em có một file gồm 3 sheets dữ liệu. mục đích là đối chiếu các trường thông tin theo dòng của sheet1 và sheet2 với nhau (từ cột A đến cột H, bỏ qua cột G). nếu dữ liệu nào có ở sheet2 mà ko có ở sheet1 hoặc dữ liệu có ở sheet1 mà không có ở sheet2 thì kết quả gửi sheet3. dữ liệu nào có cả 2 sheet1 và sheet2 thì sum tổng tiền cột C ạ.
Em có viết một đoạn code so sánh một chiều giữa sheet2 và sheet1, nhưng code dài quá, e thấy loằng ngoằng và ko tối ưu, nên chưa viết chiều so sánh giữa sheet1 và sheet2. nhờ các anh/chị giúp e cải thiện nhé!
Bạn xem file này nhé.
 

File đính kèm

Upvote 0
Cái này cùng là dịp tốt đẻ bạn nào muốn luyện nghề viết SQL. Có ai muốn thử ADO hôn?
anh í đã có lòng thì mình có cái đầu , giơ ra cho anh í gõ thôi
Mã:
Sub Oval1_Click()
Dim cn As Object
Set cn = CreateObject("adodb.Connection")
cn.Open "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & ThisWorkbook.FullName & _
    ";Extended Properties=""Excel 12.0;HDR=NO;ReadOnly=true"";"
Sheet3.Range("A2").CopyFromRecordset cn.Execute("select a.*,'Sheet1' from (select * from [sheet1$A2:H]) a " & _
" left join (select * from [sheet2$A2:H]) b on val(a.f2) = b.f2 and val(a.f3) = b.f3 and val(a.f5) = b.f5 " & _
" where b.f2 is null union all select b.*,'Sheet2' from (select * from [sheet1$A2:H]) a " & _
" right join (select * from [sheet2$A2:H]) b on val(a.f2) = b.f2 and val(a.f3) = b.f3 and val(a.f5) = b.f5 " & _
" where a.f2 is null")
cn.Close
End Sub
 
Upvote 0
Còn phần "nếu có cả 2 sheets thì sum lại" bỏ đâu rồi?

Nếu dữ liệu không có lặp lại thì dùng UNION ALL để gộp 2 bảng lại rồi GROUP BY để chọn duy nhất - công thêm cột count(*). Nếu cột count này là 1 thì nó là dữ liệu chỉ có trong sheet 1 hoặc 2, nếu lớn hơn 1 thì nó có cả 2 sheet.

Nếu dữ liệu có lặp lại thì hơi khó hơn một chút. Dùng join để lấy phần trùng, chép ra. Sau đó dùng NOT EXISTS đẻ lây các phần không trùng.
 
Upvote 0
Còn phần "nếu có cả 2 sheets thì sum lại" bỏ đâu rồi?

Nếu dữ liệu không có lặp lại thì dùng UNION ALL để gộp 2 bảng lại rồi GROUP BY để chọn duy nhất - công thêm cột count(*). Nếu cột count này là 1 thì nó là dữ liệu chỉ có trong sheet 1 hoặc 2, nếu lớn hơn 1 thì nó có cả 2 sheet.

Nếu dữ liệu có lặp lại thì hơi khó hơn một chút. Dùng join để lấy phần trùng, chép ra. Sau đó dùng NOT EXISTS đẻ lây các phần không trùng.

thật khó nghĩ theo hướng dùng count group by , bởi trong 1 lần truy vấn sao có thể trả về kết quả nguyên bảng được anh
ở đây 2 dòng được coi là giống nhau khi cột B,C,E là giống nhau , cột G khác nhau hoàn toàn
như vậy để truy vấn được count group by ta chỉ có thể lựa ra 3 cột này mà nhóm
Mã:
Sub Oval1_Click()
Dim cn As Object
Set cn = CreateObject("adodb.Connection")
cn.Open "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & ThisWorkbook.FullName & _
    ";Extended Properties=""Excel 12.0;HDR=NO;ReadOnly=true"";"
Sheet3.Range("A2:K60000").ClearContents
Sheet3.Range("A2").CopyFromRecordset cn.Execute("select c.f22,c.f23,c.f25,count(c.f22) from " & _
" (select val(f2) as f22,val(f3) as f23,val(f5) as f25  from [sheet1$A2:H] " & _
" union all select f2,f3,f5 from [sheet2$A2:H]) c group by c.f22,c.f23,c.f25 having count(c.f22) = 1")
cn.Close
End Sub

rồi sau đó làm sao lấy nguyên bảng với kết quả này anh ? anh cho thêm gợi ý với
 
Upvote 0
Chép 2 lần. Lần thứ nhất having count(*) < 2. Lần thứ hai having count(*) > 1, và sum(cột C)
 
Upvote 0
Chép 2 lần. Lần thứ nhất having count(*) < 2. Lần thứ hai having count(*) > 1, và sum(cột C)
tới khúc này mới cay nè
Mã:
Sub Oval1_Click()
Dim cn As Object
Set cn = CreateObject("adodb.Connection")
cn.Open "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & ThisWorkbook.FullName & _
    ";Extended Properties=""Excel 12.0;HDR=NO;ReadOnly=true"";"
Sheet3.Range("A2:K60000").ClearContents
Sheet3.Range("A2").CopyFromRecordset cn.Execute("select * from (select *,'Sheet1' from [Sheet1$A2:H] " & _
" union all select *,'Sheet2' from [Sheet2$A2:H]) a right join (select f22,f3,f5,count(f22) from " & _
" (select val(f2) as f22,f3,f5  from [sheet1$A2:H] union all select f2,f3,f5 from [sheet2$A2:H]) " & _
" group by f22,f3,f5 having count(f22) = 1) b on val(a.f2) = b.f22 and a.f3 = b.f3 and [COLOR=#ff0000][SIZE=3][B]a.f5 = b.f5[/B][/SIZE][/COLOR]")
cn.Close
End Sub

cho ra kết quả bị sót số Mobile 988553968 trong Sheet2
bởi vì cột 5 của dòng này là Null , trong bảng "having count" cũng là Null
Null với Null bị coi là khác nhau .....
Xin anh VetMini giúp gỡ cái tình huống này
 
Upvote 0
Bạn đếm sô dòng chứ đâu phải cột phải hôn? Khi đếm dòng dùng count(*), chỉ dùng count(trường) khi ta muốn bỏ qua null.

"select f1, f2, f3, f5 from (select f1, f2, f3, f5 from [sheetNum1] union all select f1, f2, f3, f5 from [sheetNum1]) group by f1, f2, f3, f5 having count(*) < 2"

"select f1, f2, f3, f5, sum(f4) from (select f1, f2, f3, f4, f5 from [sheetNum1] union all select f1, f2, f3, f4, f5 from [sheetNum1]) group by f1, f2, f3, f5 having count(*) >= 2"
 
Upvote 0
Mình hoàn thiện lại Code dùng Dic

Mã:
Sub Test()
Dim Dic As Object, Tm1, Tm2, MyStr, eR, mSum, a, i, j
Set Dic = CreateObject("Scripting.Dictionary")


Tm1 = Sheet1.Range(Sheet1.[a2], Sheet1.[h65536].End(xlUp)).Value
Tm2 = Sheet2.Range(Sheet2.[a2], Sheet2.[j65536].End(xlUp)).Value
eR = IIf(UBound(Tm1, 1) > UBound(Tm2, 1), UBound(Tm1, 1), UBound(Tm2, 1))
For i = 1 To eR
If i <= UBound(Tm1, 1) Then
MyStr = Join(Array(Format(Tm1(i, 2), "000000000000"), Tm1(i, 3), Tm1(i, 5), Val(Tm1(i, 8))), ";")
If Dic.exists(MyStr) Then
Dic.Item(MyStr) = ""
Else
Dic.Add MyStr, Array(Tm1(i, 1), Tm1(i, 2), Tm1(i, 3), Tm1(i, 4), Tm1(i, 5), _
Tm1(i, 6), Tm1(i, 7), Val(Tm1(i, 8)), "", "", "Row: " & i & " - " & Sheet1.Name)
End If
mSum = mSum + Tm1(i, 3)
End If
If i <= UBound(Tm2, 1) Then
MyStr = Join(Array(Format(Tm2(i, 2), "000000000000"), Tm2(i, 3), Tm2(i, 5), Val(Tm2(i, 8))), ";")
If Dic.exists(MyStr) Then
Dic.Item(MyStr) = ""
Else
Dic.Add MyStr, Array(Tm2(i, 1), Tm2(i, 2), Tm2(i, 3), Tm2(i, 4), Tm2(i, 5), _
Tm2(i, 6), Tm2(i, 7), Val(Tm2(i, 8)), Tm2(i, 9), Tm2(i, 10), "Row: " & i & " - " & Sheet2.Name)
End If
mSum = mSum + Tm2(i, 3)
End If
Next
Sheet3.[A2:M65000].ClearContents
If Dic.Count = 0 Then Exit Sub
eR = 1
a = Dic.Items
For i = 0 To Dic.Count - 1
If IsArray(a(i)) Then
eR = eR + 1
Sheet3.Cells(eR, 1).Resize(, 11) = a(i)
mSum = mSum - a(i)(2)
End If
Next
MsgBox "Tong tien so trung la: " & Format(mSum, "#,##0")
Set Dic = Nothing
End Sub
 
Upvote 0
Web KT

Bài viết mới nhất

Back
Top Bottom