Lấy số lượng nhập lần đầu, kế cuối và gần nhất bằng code VBA

Liên hệ QC

thao nguyen01

Thành viên thường trực
Tham gia
8/12/19
Bài viết
214
Được thích
25
Kính gửi anh/chị,

E bị vướng khi lấy số lượng nhập lần đầu, kế cuối và gần nhất. E có để data và kết quả mong muốn theo file đính kèm ạ. Mong các anh/chị xem và giúp đỡ em ạ. E cảm ơn nhiều ạ.
 

File đính kèm

  • Lay so luong gan nhat và lan dau.xlsx
    11.1 KB · Đọc: 20
Kính gửi anh/chị,

E bị vướng khi lấy số lượng nhập lần đầu, kế cuối và gần nhất. E có để data và kết quả mong muốn theo file đính kèm ạ. Mong các anh/chị xem và giúp đỡ em ạ. E cảm ơn nhiều ạ.
Bạn nói rõ mua hàng lần đầu điều kiện là gì,Mua hàng kế cuối điều kiện gì,Và mua hàng gần nhất điều kiện là gì.
 
Bạn nói rõ mua hàng lần đầu điều kiện là gì,Mua hàng kế cuối điều kiện gì,Và mua hàng gần nhất điều kiện là gì.

Dạ, e vd trong data dữ liệu của e: mặt hàng A: mua hàng lần đầu sẽ được lấy số lượng theo thời gian nhập mặt hàng đó lần đầu tiên ạ; mua hàng gần nhất là lần cuối cùng nhập mặt hàng đó ạ và mua hàng kế cuối là lần nhập trước lần cuối ạ. Tất cả e sẽ lấy theo mã hàng và theo thời gian ạ. Vì e diễn giải hơi khó hiểu nên e có làm bảng kết quả kế bên ạ. Anh @snow25 xem giúp e ạ. Nếu không rõ, a cứ nhắn e ạ. E cảm ơn anh.
 
Có ít nhất 3 cách làm (tức là hiện tại tôi biết 3, có thể có ngừoi biết thêm cách khác)

1. Cách truyền thống nhất:
- Sắp xếp bảng theo mặt hàng và ngày (giảm dần)
- Đọc bảng đã sắp xếp, dòng đầu tiên của mặt hàng là ngày gần nhất, dòng kế là ngày cận kế, dòng cuối là ngày đầu tiên (xa nhất).

2. Cách mà GPE "khoái" dùng, tức là dùng đít sần:
- Lập ra 2 mảng. Mảng A gồm 3 cột chứa ngày (3 ngày: gần nhất, cận kế, và xa nhất), và mảng B gồm bốn cột chứa số liệu (gần nhất, cận kế, đầu tiên, và tổng)
- Đọc bảng, dùng đít sần để ghi chỉ số tương ứng của mặt hàng trong hai mảng A, B. Đồng thời:
-- cộng tổng vào cột cuối của mảng B
-- dò ngày xem dòng hiện đang đọc thuộc về gần nhất, cận kế hay xa nhất, và cập nhật cột tương ứng trong bảng B

3. Cách mà GPE cũng tương đối "khoái", tức là dùng ADO để gom dữ liệu.
Cách này thì code căn bản ở đây có cả đống. Vấn đề là cái câu SQL hơi khó.
 
Có ít nhất 3 cách làm (tức là hiện tại tôi biết 3, có thể có ngừoi biết thêm cách khác)

1. Cách truyền thống nhất:
- Sắp xếp bảng theo mặt hàng và ngày (giảm dần)
- Đọc bảng đã sắp xếp, dòng đầu tiên của mặt hàng là ngày gần nhất, dòng kế là ngày cận kế, dòng cuối là ngày đầu tiên (xa nhất).

2. Cách mà GPE "khoái" dùng, tức là dùng đít sần:
- Lập ra 2 mảng. Mảng A gồm 3 cột chứa ngày (3 ngày: gần nhất, cận kế, và xa nhất), và mảng B gồm bốn cột chứa số liệu (gần nhất, cận kế, đầu tiên, và tổng)
- Đọc bảng, dùng đít sần để ghi chỉ số tương ứng của mặt hàng trong hai mảng A, B. Đồng thời:
-- cộng tổng vào cột cuối của mảng B
-- dò ngày xem dòng hiện đang đọc thuộc về gần nhất, cận kế hay xa nhất, và cập nhật cột tương ứng trong bảng B

3. Cách mà GPE cũng tương đối "khoái", tức là dùng ADO để gom dữ liệu.
Cách này thì code căn bản ở đây có cả đống. Vấn đề là cái câu SQL hơi khó.
Dạ, e cảm ơn bác @VetMini ạ. Nhưng e có viết code theo cách 2 nhưng chưa ra ạ. Bác @VetMini có thể giúp e theo cách 2 được không ạ. E cảm ơn bác @VetMini
 
Tôi chỉ nói là GPE "khoái dùng" chứ đâu phải đâu phải là chuyên nghiệp của tôi.
Tôi là dân truyền thống và bảo thủ mờ.
 
Kính gửi anh/chị,

E bị vướng khi lấy số lượng nhập lần đầu, kế cuối và gần nhất. E có để data và kết quả mong muốn theo file đính kèm ạ. Mong các anh/chị xem và giúp đỡ em ạ. E cảm ơn nhiều ạ.
Thử Code
Mã:
Sub ABC()
  Dim sArr(), Res(), Ma$
  Dim eR&, i&, k&, iD&, N&, sRow&, TongSo#
  With Sheets("Sheet1")
    eR = .Range("I" & Rows.Count).End(xlUp).Row 'Dong cuoi
    If eR > 1 Then .Range("I2:N" & eR).Clear 'Xoa ket qua cu
    eR = .Range("A" & Rows.Count).End(xlUp).Row 'Dong cuoi
    If eR < 2 Then MsgBox ("Khong co du lieu"): Exit Sub
    Res = .Range("A2:C" & eR).Value 'Du lieu goc
    .Range("A2:C" & eR).Sort .[B2], 1, .[A2], , 1, Header:=xlNo ' Sort du lieu
    sArr = .Range("A2:C" & eR + 1).Value 'Mang du lieu da Sort
    .Range("A2:C" & eR).Value = Res 'Tra lai du lieu goc
    .Range("D1").Formula = "=SUMPRODUCT(1/COUNTIF(B2:B" & eR & ",B2:B" & eR & "))" 'So ma hang
    N = .Range("D1").Value * 4 'So dong ket qua
    .Range("D1").ClearContents 'Xoa o tam
  End With
  ReDim Res(1 To N, 1 To 6)
  sRow = UBound(sArr)
  For i = 1 To sRow - 1
    If Ma <> sArr(i, 2) Then 'Lan dau
      iD = i
      Ma = sArr(i, 2)
      TongSo = 0
      k = k + 1
      Call GanKetQua(sArr, Res, k, TongSo, i, 3)
    End If
    If Ma <> sArr(i + 1, 2) Then
      If i > iD + 1 Then 'Lan ke cuoi
        k = k + 1
        Call GanKetQua(sArr, Res, k, TongSo, i - 1, 4)
      End If
      If i > iD Then 'Lan gan nhat
        k = k + 1
        Call GanKetQua(sArr, Res, k, TongSo, i, 5)
      End If
      k = k + 1 'Dong tong
      Res(k, 1) = sArr(i, 2)
      Res(k, 6) = TongSo
    End If
  Next i
  With Sheets("Sheet1")
    .Range("I2").Resize(k, 6) = Res
    .Range("I2").Resize(k, 6).Borders.LineStyle = 1
  End With
End Sub
Private Sub GanKetQua(sArr, Res, k, TongSo, ByVal iR&, ByVal jC&)
  Res(k, 1) = sArr(iR, 2)
  Res(k, 2) = sArr(iR, 1)
  Res(k, jC) = sArr(iR, 3)
  Res(k, 6) = sArr(iR, 3)
  TongSo = TongSo + sArr(iR, 3)
End Sub
 
Thử Code
Mã:
Sub ABC()
  Dim sArr(), Res(), Ma$
  Dim eR&, i&, k&, iD&, N&, sRow&, TongSo#
  With Sheets("Sheet1")
    eR = .Range("I" & Rows.Count).End(xlUp).Row 'Dong cuoi
    If eR > 1 Then .Range("I2:N" & eR).Clear 'Xoa ket qua cu
    eR = .Range("A" & Rows.Count).End(xlUp).Row 'Dong cuoi
    If eR < 2 Then MsgBox ("Khong co du lieu"): Exit Sub
    Res = .Range("A2:C" & eR).Value 'Du lieu goc
    .Range("A2:C" & eR).Sort .[B2], 1, .[A2], , 1, Header:=xlNo ' Sort du lieu
    sArr = .Range("A2:C" & eR + 1).Value 'Mang du lieu da Sort
    .Range("A2:C" & eR).Value = Res 'Tra lai du lieu goc
    .Range("D1").Formula = "=SUMPRODUCT(1/COUNTIF(B2:B" & eR & ",B2:B" & eR & "))" 'So ma hang
    N = .Range("D1").Value * 4 'So dong ket qua
    .Range("D1").ClearContents 'Xoa o tam
  End With
  ReDim Res(1 To N, 1 To 6)
  sRow = UBound(sArr)
  For i = 1 To sRow - 1
    If Ma <> sArr(i, 2) Then 'Lan dau
      iD = i
      Ma = sArr(i, 2)
      TongSo = 0
      k = k + 1
      Call GanKetQua(sArr, Res, k, TongSo, i, 3)
    End If
    If Ma <> sArr(i + 1, 2) Then
      If i > iD + 1 Then 'Lan ke cuoi
        k = k + 1
        Call GanKetQua(sArr, Res, k, TongSo, i - 1, 4)
      End If
      If i > iD Then 'Lan gan nhat
        k = k + 1
        Call GanKetQua(sArr, Res, k, TongSo, i, 5)
      End If
      k = k + 1 'Dong tong
      Res(k, 1) = sArr(i, 2)
      Res(k, 6) = TongSo
    End If
  Next i
  With Sheets("Sheet1")
    .Range("I2").Resize(k, 6) = Res
    .Range("I2").Resize(k, 6).Borders.LineStyle = 1
  End With
End Sub
Private Sub GanKetQua(sArr, Res, k, TongSo, ByVal iR&, ByVal jC&)
  Res(k, 1) = sArr(iR, 2)
  Res(k, 2) = sArr(iR, 1)
  Res(k, jC) = sArr(iR, 3)
  Res(k, 6) = sArr(iR, 3)
  TongSo = TongSo + sArr(iR, 3)
End Sub
Dạ, e cảm ơn Thầy @HieuCD nhiều lắm ạ
 
Lý do chính tôi không muốn code bài này là vì cái lô gic của bảng kết quả chưa được chặt.
Đọc cái bảng ấy khó hiểu bỏ bố.
Cột cuối là trường hợp đại kỵ của bảng tính:
1. dữ liệu dòng bị lặp lại.
2. tổng không phải là tổng đúng của các phát sinh. Chỉ là tổng của các phát sinh đứng hạng thôi.
 
Lần chỉnh sửa cuối:
2. Cách mà GPE "khoái" dùng, tức là dùng đít sần:
...
- Đọc bảng, dùng đít sần để
Đúng là 100 người thì 101 gu. Tôi thì khoái món "đít thon". Cực chẳng đã mới chấp nhận "đít to". Còn "đít sần" nhiều mụn thì nhường bác VetMini.
 
Hôm nay thử nói về thuật toán dùng đít sần.
Dic chỉ là một công cụ giúp truy vấn dữ liệu kiểu key-item. Phần lớn bài trên diễn đàn này sử dụng Dic vì muốn lợi dụng tính chất duy nhất của key để giải đáp bài toán lọc duy nhất.

Và bài này, nếu sử dụng Dic cũng chẳng qua cái thông lệ đó.
Đã là thông lệ thì nó rất nhàm chán. Dic chỉ dùng để lọc mã duy nhất, với mã là key và item là chỉ số của mảng kết quả. Code này ở diễn đàn có đến hàng trăm.

Rốt cuộc lại thì con toán khó khăn quy về cách góp dọn dữ liệu cho mảng kết quả. Bài toán này là tìm LỚN NHẤT, LỚN NHÌ, và NHẤT.

Bài toán tìm lớn/nhỏ nhất là bài căn bản của lập trình, thường được nằm trong bài tập của chương dạy về mảng. Bài này quá dễ, không cần nói thêm.
Bài toán tìm n số lớn nhất là bài hơi cần động não một chút. Và nó là bài tôi muốn nhắc đến ở đây.

Thuật toán tìm n số lớn nhất trong một mảng số:
1. lập một mảng n phần tử
2. với mỗi số, so sánh nó với mảng n và chèn vào đúng chỗ.
Thuật toán chèn, (mảng n chứa các số đã sắp xếp từ lớn xuống nhỏ):
1. vòng lặp duyệt mảng n
2. so sánh số với từng trị trong mảng:
2.1 nếu nhỏ hơn, qua phần tử kế
2.2 nếu bằng, thoát vòng lặp
2.3 nếu lớn hơn, thay trị này và đẩy các phần tử xuống
(các xảo thuật khác nhau nằm ở thuật toán "đẩy phần tử" này)

Code minh hoạ:
For i = LBound(mangSo) To UBound(mangSo)
so = mangSo(i)
For i2 = lbMangSapXep To ubMangSapXep
If so = mangSX(i2) Then ' số này có rồi, không cần nữa
Exit For
ElseIf so > mangSX(i2) Then ' chèn vào, và đẩy xuống
tmp = mangSX(i2)
mangSX(i2) = so
so = tmp
End If
Next i2
Next i

Bài này chỉ tìm 2 số lớn nhất, và 1 số nhỏ nhất. Thuật toán phức tạp hơn nhiều.
 
Hôm nay thử nói về thuật toán dùng đít sần.
Dic chỉ là một công cụ giúp truy vấn dữ liệu kiểu key-item. Phần lớn bài trên diễn đàn này sử dụng Dic vì muốn lợi dụng tính chất duy nhất của key để giải đáp bài toán lọc duy nhất.

Và bài này, nếu sử dụng Dic cũng chẳng qua cái thông lệ đó.
Đã là thông lệ thì nó rất nhàm chán. Dic chỉ dùng để lọc mã duy nhất, với mã là key và item là chỉ số của mảng kết quả. Code này ở diễn đàn có đến hàng trăm.

Rốt cuộc lại thì con toán khó khăn quy về cách góp dọn dữ liệu cho mảng kết quả. Bài toán này là tìm LỚN NHẤT, LỚN NHÌ, và NHẤT.

Bài toán tìm lớn/nhỏ nhất là bài căn bản của lập trình, thường được nằm trong bài tập của chương dạy về mảng. Bài này quá dễ, không cần nói thêm.
Bài toán tìm n số lớn nhất là bài hơi cần động não một chút. Và nó là bài tôi muốn nhắc đến ở đây.

Thuật toán tìm n số lớn nhất trong một mảng số:
1. lập một mảng n phần tử
2. với mỗi số, so sánh nó với mảng n và chèn vào đúng chỗ.
Thuật toán chèn, (mảng n chứa các số đã sắp xếp từ lớn xuống nhỏ):
1. vòng lặp duyệt mảng n
2. so sánh số với từng trị trong mảng:
2.1 nếu nhỏ hơn, qua phần tử kế
2.2 nếu bằng, thoát vòng lặp
2.3 nếu lớn hơn, thay trị này và đẩy các phần tử xuống
(các xảo thuật khác nhau nằm ở thuật toán "đẩy phần tử" này)

Code minh hoạ:
For i = LBound(mangSo) To UBound(mangSo)
so = mangSo(i)
For i2 = lbMangSapXep To ubMangSapXep
If so = mangSX(i2) Then ' số này có rồi, không cần nữa
Exit For
ElseIf so > mangSX(i2) Then ' chèn vào, và đẩy xuống
tmp = mangSX(i2)
mangSX(i2) = so
so = tmp
End If
Next i2
Next i

Bài này chỉ tìm 2 số lớn nhất, và 1 số nhỏ nhất. Thuật toán phức tạp hơn nhiều.
Dạ. E cảm ơn bác @VetMini nhiều ạ.
 
Web KT
Back
Top Bottom