Truy vấn bằng SQL dữ liệu từ 2 bảng: bảng tồn và bảng nhập xuất

Liên hệ QC

Hoàng Nhật Phương

Thành viên gắn bó
Tham gia
5/11/15
Bài viết
1,894
Được thích
1,213
Xin chào các bạn, OT có một vấn đề sau chưa giải quyết được lên đưa lên đây nhờ tất cả mọi người cùng xem và giúp đỡ.
Cụ thể có 2 bảng dữ liệu 'Inventory' và 'Data':

1687490944197.png
1687491005761.png

Nhờ các bạn giúp đỡ câu lệnh SQL để lấy dữ liệu từ 2 bảng trên trả về kết quả sau:


1687491048123.png

Xin trân trọng cảm ơn
 

File đính kèm

  • NXT_SQL.xlsx
    12.6 KB · Đọc: 14
Xin chào các bạn, OT có một vấn đề sau chưa giải quyết được lên đưa lên đây nhờ tất cả mọi người cùng xem và giúp đỡ.
Cụ thể có 2 bảng dữ liệu 'Inventory' và 'Data':

View attachment 291800
View attachment 291801

Nhờ các bạn giúp đỡ câu lệnh SQL để lấy dữ liệu từ 2 bảng trên trả về kết quả sau:


View attachment 291802

Xin trân trọng cảm ơn
Bạn phải miêu tả là làm sao ra được số lượng StockIn và StockOut nha hôn.
 
Nếu nói về SQL:
- Khóa liên kết = Item & LotNo & StockNo
- Select If (StockType = "Out" Or StockType = "Mov") and StockNo = "Kho-004" then Quantity else 0 As StockOut
thêm If StockType = "In" and StockNo = "Kho-004" Or Or StockType = "Mov" and StockNoTo = "Kho-004" then Quantity else 0 As Stockin
- Where Item = "MH002"

Đại khái vậy
 
Cảm ơn chú Mỹ và bạn @xì tin đã quan tâm và góp ý.
Chú Mỹ giúp con câu lệnh hoàn chỉnh với, con loay hoay mãi rồi nhưng chưa được.
Nếu là VBA thì code sẽ như thế này ạ:

Mã:
Option Explicit

Sub testCalcInventory()

    Dim dic As Object, sheet As Worksheet
    Dim inventory As Variant, scan As Variant, result As Variant, Key As Variant
    Dim sPart As String, sLot As String, sTockNo As String, sTockNoTo As String, str As String, sTockNoSearch As String
    Dim i As Long, j As Long, r As Long, u As Long, k As Long
    Dim quantity As Double
    Dim c As Integer, style As String
    
    Const sDELIM As String = "|"
    
    Set sheet = ThisWorkbook.ActiveSheet
    inventory = sheet.Range("A3:E6").Value
    scan = sheet.Range("G3:O29").Value
    
    sTockNoSearch = "*KHO_004*"
    
    If IsArray(inventory) Then r = UBound(inventory, 1)
    If IsArray(scan) Then r = r + UBound(scan, 1)
    
    If (r = 0) Then
        Exit Sub
    End If
    
    If Not IsArray(scan) Then
        GoTo Iventory_
    End If
    
Scaner_:
    
    Set dic = CreateObject("Scripting.Dictionary")
    c = 8:     ReDim result(1 To r * 2, 1 To c)
    
    For i = LBound(scan, 1) To UBound(scan, 1)
        
        style = scan(i, 1)
        sPart = scan(i, 2)
        sTockNoTo = scan(i, 3)
        sTockNo = scan(i, 4)
        sLot = scan(i, 7)
        quantity = scan(i, 8)

        str = Join(Array(sPart, sTockNoTo, sLot), sDELIM)
        If Not dic.Exists(str) And (style = "MOV") Then
            If UCase(sTockNoTo) Like UCase(sTockNoSearch) Then
                k = k + 1
                dic.Add str, k
                result(k, 1) = k
                result(k, 2) = sPart
                result(k, 3) = sLot
                result(k, 4) = sTockNoTo
            End If
        End If
        
        Key = Join(Array(sPart, sTockNo, sLot), sDELIM)
        If Not dic.Exists(Key) Then
            If UCase(sTockNo) Like UCase(sTockNoSearch) Then
                k = k + 1
                dic.Add Key, k
                result(k, 1) = k
                result(k, 2) = sPart
                result(k, 3) = sLot
                result(k, 4) = sTockNo
            End If
        End If
        
        u = dic.Item(str):  r = dic.Item(Key)
        
        Select Case style
           Case "IN" 'NHAP KHO
                If (r > 0) Then result(r, 6) = result(r, 6) + quantity
                If (r > 0) Then result(r, 8) = result(r, 8) + quantity
           Case "OUT" 'XUAT KHO
                If (r > 0) Then result(r, 7) = result(r, 7) + quantity
                If (r > 0) Then result(r, 8) = result(r, 8) - quantity
           Case "MOV" 'CHUYEN KHO
                If (u > 0) Then result(u, 6) = result(u, 6) + quantity
                If (u > 0) Then result(u, 8) = result(u, 8) + quantity
                If (r > 0) Then result(r, 7) = result(r, 7) + quantity
                If (r > 0) Then result(r, 8) = result(r, 8) - quantity
        End Select
                
    Next i

    If Not IsArray(inventory) Then
        GoTo Result_
    End If
    
Iventory_:
    For i = LBound(inventory, 1) To UBound(inventory, 1)
        sPart = inventory(i, 1)
        sTockNo = inventory(i, 2)
        sLot = inventory(i, 3)
        quantity = inventory(i, 4)
        Key = Join(Array(sPart, sTockNo, sLot), sDELIM)
        str = Join(Array(sDELIM, sPart, sTockNo, sLot), sDELIM)
        If UCase(sTockNo) Like UCase(sTockNoSearch) Then
            If Not dic.Exists(Key) And (quantity > 0) Then
                k = k + 1
                dic.Add Key, k
                result(k, 1) = k
                result(k, 2) = sPart
                result(k, 3) = sLot
                result(k, 4) = sTockNo
                result(k, 5) = quantity
                result(k, 6) = quantity
            Else
                If Not dic.Exists(str) And (quantity > 0) Then
                    dic.Add str, quantity:  r = dic.Item(Key)
                    result(r, 5) = quantity
                    result(r, 8) = result(r, 5) + result(r, 6) - result(r, 7)
                End If
            End If
        End If
    Next i
    
Result_:
    If (k > 0) Then sheet.Range("P13").Resize(k, c).Value = result
    
End Sub
 
Xin chào các bạn, OT có một vấn đề sau chưa giải quyết được lên đưa lên đây nhờ tất cả mọi người cùng xem và giúp đỡ.
Cụ thể có 2 bảng dữ liệu 'Inventory' và 'Data':

View attachment 291800
View attachment 291801

Nhờ các bạn giúp đỡ câu lệnh SQL để lấy dữ liệu từ 2 bảng trên trả về kết quả sau:


View attachment 291802

Xin trân trọng cảm ơn
Dữ liệu và kết quả trong 1 file dùng dic nhanh và dể viết hơn SQL nhiều
 
Dữ liệu và kết quả trong 1 file dùng dic nhanh và dể viết hơn SQL nhiều
Con chào bác Hiếu,
Dạ con mô tả vậy thôi bác, dữ liệu là lấy trực tiếp từ SQL ra đó bác. Con muốn thay vì lấy về sau đó mới xử lý thì sẽ xử lý luôn bằng câu lệnh SQL bác ạ. Với lại con cũng muốn tìm hiểu về câu lệnh SQL với trường hợp này ạ, vì trong thực tế con gặp nhiều.
Cảm ơn bác đã góp ý ạ.
 
Con chào bác Hiếu,
Dạ con mô tả vậy thôi bác, dữ liệu là lấy trực tiếp từ SQL ra đó bác. Con muốn thay vì lấy về sau đó mới xử lý thì sẽ xử lý luôn bằng câu lệnh SQL bác ạ. Với lại con cũng muốn tìm hiểu về câu lệnh SQL với trường hợp này ạ, vì trong thực tế con gặp nhiều.
Cảm ơn bác đã góp ý ạ.
Gặp ca phức tạp bạn nên dùng nhiều query, trường hợp nầy cần 2 query lấy dữ liệu từ 2 bảng và 1 query lấy kết quả từ 2 query trên, SQL được đơn giản hóa
 
Gặp ca phức tạp bạn nên dùng nhiều query, trường hợp nầy cần 2 query lấy dữ liệu từ 2 bảng và 1 query lấy kết quả từ 2 query trên, SQL được đơn giản hóa
Dạ dùng nhiều query cũng được bác con cũng thường làm như vậy, khi kết nối đến CSDL thì sẽ cho chạy hết một lượt ạ.
Vấn đề là con cũng đang chưa biết câu query này như thế nào bác:
1 query lấy kết quả từ 2 query trên
Nhờ bác và mọi người chỉ giúp ạ.
 
Nếu nói về SQL:
- Khóa liên kết = Item & LotNo & StockNo
- Select If (StockType = "Out" Or StockType = "Mov") and StockNo = "Kho-004" then Quantity else 0 As StockOut
thêm If StockType = "In" and StockNo = "Kho-004" Or Or StockType = "Mov" and StockNoTo = "Kho-004" then Quantity else 0 As Stockin
- Where Item = "MH002"

Đại khái vậy
Trường hợp này ta có thể không cần dùng khoá như bạn đề cập.
 
Table1 là Master File. Chứa dữ liệu căn bản của Inventory (hàng hóa)
Table2 là Transaction File. Chứa các phát sinh cho Inventory (xuất nhập,...)
Đối với mọi CSDL, phân biệt loại bảng là điều quan trọng, cần phải nhớ rõ.

Bài này cần khá nhiều bảng dữ liệu tạm.
1. Lọc dữ liệu theo mã inventory
(Select * From Table2 Where Item = 'MH002') As Table22.
2. Lọc theo IN
(Select *, StockNo As PosNo, Quantity As QtyIN, 0 As QtyOUT From Table22 Where StockType = 'IN')
Lọc theo OUT
(Select *, StockNo As PosNo, 0 As QtyIN, Quantity As QtyOUT From Table22 Where StockType = 'OUT')
Lọc theo MOV (in)
(Select *, StockNoTo As PosNo, Quantity As QtyIN, 0 As QtyOUT From Table22 Where StockType = 'MOV')
Lọc theo MOV (out)
(Select *, StockNo As PosNo, 0 As QtyIN, Quantity As QtyOUT From Table22 Where StockType = 'MOV')

UNION ALL 4 cái lọc trên, bạn sẽ được một bảng transaction (phát sinh) mới cho Item chọn lựa và IN/OUT đã phân biệt rõ.
Công việc kế tiếp là GROUP chúng lại theo ITEM, POSNO, LOTNO; và tổng các Qty's
Công việc bây giờ của bạn chỉ là JOIN nó (Item và LotNo, POSNO với STOCKNO) với Table1 để lấy thêm dữ liệu (tồn đầu)

Đó là cách làm căn bản để lọc lại Transactions với nhiều kiểu (IN/OUT/MOV)
Nó rắc rối bởi vì cách thiết kế hai bảng Master và Transaction của bạn không đạt chuẩn.

Cách làm gọn hơn là theo ý của bài #227, các Transactions chỉ lọc một lần và dùng lô gic IF-THEN-ELSE để phân biệt cách đối xử khi gặp các loại StockType khác nhau (lưu ý là bài của bạn dùng từ Stocktype là sai, tên đúng thì phải là TransType)

Tuy nhiên, bài #227 trên chỉ gợi ý. SQL của Access không hỗ trợ lệnh If-Then-Else hay Case cho nên bạn phải dùng IIF hoặc Switch.
Lưu ý là Table2 của bạn thiết kế rất kém cho nên việc xử lý loại "MOV" rất phức tạp. Làm cách nào để đổi một MOV thành 1 OUT (dễ) cộng 1 IN (rất khó).
 
Table1 là Master File. Chứa dữ liệu căn bản của Inventory (hàng hóa)
Table2 là Transaction File. Chứa các phát sinh cho Inventory (xuất nhập,...)
Đối với mọi CSDL, phân biệt loại bảng là điều quan trọng, cần phải nhớ rõ.

Bài này cần khá nhiều bảng dữ liệu tạm.
1. Lọc dữ liệu theo mã inventory
(Select * From Table2 Where Item = 'MH002') As Table22.
2. Lọc theo IN
(Select *, StockNo As PosNo, Quantity As QtyIN, 0 As QtyOUT From Table22 Where StockType = 'IN')
Lọc theo OUT
(Select *, StockNo As PosNo, 0 As QtyIN, Quantity As QtyOUT From Table22 Where StockType = 'OUT')
Lọc theo MOV (in)
(Select *, StockNoTo As PosNo, Quantity As QtyIN, 0 As QtyOUT From Table22 Where StockType = 'MOV')
Lọc theo MOV (out)
(Select *, StockNo As PosNo, 0 As QtyIN, Quantity As QtyOUT From Table22 Where StockType = 'MOV')

UNION ALL 4 cái lọc trên, bạn sẽ được một bảng transaction (phát sinh) mới cho Item chọn lựa và IN/OUT đã phân biệt rõ.
Công việc kế tiếp là GROUP chúng lại theo ITEM, POSNO, LOTNO; và tổng các Qty's
Công việc bây giờ của bạn chỉ là JOIN nó (Item và LotNo, POSNO với STOCKNO) với Table1 để lấy thêm dữ liệu (tồn đầu)

Đó là cách làm căn bản để lọc lại Transactions với nhiều kiểu (IN/OUT/MOV)
Nó rắc rối bởi vì cách thiết kế hai bảng Master và Transaction của bạn không đạt chuẩn.

Cách làm gọn hơn là theo ý của bài #227, các Transactions chỉ lọc một lần và dùng lô gic IF-THEN-ELSE để phân biệt cách đối xử khi gặp các loại StockType khác nhau (lưu ý là bài của bạn dùng từ Stocktype là sai, tên đúng thì phải là TransType)

Tuy nhiên, bài #227 trên chỉ gợi ý. SQL của Access không hỗ trợ lệnh If-Then-Else hay Case cho nên bạn phải dùng IIF hoặc Switch.
Lưu ý là Table2 của bạn thiết kế rất kém cho nên việc xử lý loại "MOV" rất phức tạp. Làm cách nào để đổi một MOV thành 1 OUT (dễ) cộng 1 IN (rất khó).
Con cảm ơn bác VetMini nhiều, một gợi ý rất cơ bản và logic con sẽ thử xem con có làm được không ạ.
 
Table1 là Master File. Chứa dữ liệu căn bản của Inventory (hàng hóa)
Table2 là Transaction File. Chứa các phát sinh cho Inventory (xuất nhập,...)
Đối với mọi CSDL, phân biệt loại bảng là điều quan trọng, cần phải nhớ rõ.

Bài này cần khá nhiều bảng dữ liệu tạm.
1. Lọc dữ liệu theo mã inventory
(Select * From Table2 Where Item = 'MH002') As Table22.
2. Lọc theo IN
(Select *, StockNo As PosNo, Quantity As QtyIN, 0 As QtyOUT From Table22 Where StockType = 'IN')
Lọc theo OUT
(Select *, StockNo As PosNo, 0 As QtyIN, Quantity As QtyOUT From Table22 Where StockType = 'OUT')
Lọc theo MOV (in)
(Select *, StockNoTo As PosNo, Quantity As QtyIN, 0 As QtyOUT From Table22 Where StockType = 'MOV')
Lọc theo MOV (out)
(Select *, StockNo As PosNo, 0 As QtyIN, Quantity As QtyOUT From Table22 Where StockType = 'MOV')

UNION ALL 4 cái lọc trên, bạn sẽ được một bảng transaction (phát sinh) mới cho Item chọn lựa và IN/OUT đã phân biệt rõ.
Công việc kế tiếp là GROUP chúng lại theo ITEM, POSNO, LOTNO; và tổng các Qty's
Công việc bây giờ của bạn chỉ là JOIN nó (Item và LotNo, POSNO với STOCKNO) với Table1 để lấy thêm dữ liệu (tồn đầu)

Đó là cách làm căn bản để lọc lại Transactions với nhiều kiểu (IN/OUT/MOV)
Nó rắc rối bởi vì cách thiết kế hai bảng Master và Transaction của bạn không đạt chuẩn.

Cách làm gọn hơn là theo ý của bài #227, các Transactions chỉ lọc một lần và dùng lô gic IF-THEN-ELSE để phân biệt cách đối xử khi gặp các loại StockType khác nhau (lưu ý là bài của bạn dùng từ Stocktype là sai, tên đúng thì phải là TransType)

Tuy nhiên, bài #227 trên chỉ gợi ý. SQL của Access không hỗ trợ lệnh If-Then-Else hay Case cho nên bạn phải dùng IIF hoặc Switch.
Lưu ý là Table2 của bạn thiết kế rất kém cho nên việc xử lý loại "MOV" rất phức tạp. Làm cách nào để đổi một MOV thành 1 OUT (dễ) cộng 1 IN (rất khó).
Trường hợp này ta nên thoải mái và thả lỏng nó ra chút nha bạn.
@Hoàng Nhật Phương:
Kết quả sẽ đúng yêu cầu nhưng thứ tự không được như ý bạn. Bạn có chịu không?

1687506469593.png
 
Trường hợp này ta nên thoải mái và thả lỏng nó ra chút nha bạn.
@Hoàng Nhật Phương:
Kết quả sẽ đúng yêu cầu nhưng thứ tự không được như ý bạn. Bạn có chịu không?

View attachment 291823
Dạ được ạ, chỉ cần ra kết quả đúng là đủ rồi ạ.
Còn thư tự theo kết quả mẫu không phải vấn đề,nhờ bạn giúp đỡ ạ.
Cảm ơn bạn nhiều.
 
Dạ được ạ, chỉ cần ra kết quả đúng là đủ rồi ạ.
Còn thư tự theo kết quả mẫu không phải vấn đề,nhờ bạn giúp đỡ ạ.
Cảm ơn bạn nhiều.
Với đoạn truy vấn sau sẽ ra kết quả. Tuy nhiên sẽ còn cách khác tối ưu hơn.

SQL:
SELECT ITEM,
       LOTNO,
       STOCKNO,
       Sum(OPENINGSTOCK),
       Sum(STOCKIN),
       Sum(STOCKOUT),
       Sum(OPENINGSTOCK) + Sum(STOCKIN) - Sum(STOCKOUT)
FROM   (SELECT ITEM,
               LOTNO,
               STOCKNO,
               0                                  AS OpeningStock,
               Iif(STOCKTYPE = 'IN', QUANTITY, 0) AS StockIn,
               Iif(STOCKTYPE = 'IN', 0, QUANTITY) AS StockOut
        FROM   [NXT$G2:O29]
        UNION ALL
        SELECT ITEM,
               LOTNO,
               STOCKNOTO,
               0,
               QUANTITY,
               0
        FROM   [NXT$G2:O29]
        WHERE  STOCKTYPE LIKE 'MOV'
        UNION ALL
        SELECT ITEM,
               LOTNO,
               STOCKNO,
               QUANTITY,
               0,
               0
        FROM   [NXT$A2:E6])
WHERE  STOCKNO LIKE 'KHO_004'
GROUP  BY ITEM,
          LOTNO,
          STOCKNO
 
Trường hợp này ta nên thoải mái và thả lỏng nó ra chút nha bạn.
Tôi không muốn nói chuyện về cái này. Thoải mái hya khong chỉ là chủ quan của mỗi người.

@Hoàng Nhật Phương:
Kết quả sẽ đúng yêu cầu nhưng thứ tự không được như ý bạn. Bạn có chịu không?
...
Nếu đã tới kết quả rồi thì chỉ cần sort theo 12 ký tự đầu của LotNo (descending) và các ký tự còn lại (ascending)
 
Tôi không muốn nói chuyện về cái này. Thoải mái hya khong chỉ là chủ quan của mỗi người.


Nếu đã tới kết quả rồi thì chỉ cần sort theo 12 ký tự đầu của LotNo (descending) và các ký tự còn lại (ascending)
Nếu dùng câu "Suy nghĩ thoáng hơn" thì hợp lý hơn. Như tôi đã nói, thứ tự sẽ không như mong muốn, Bạn hướng dẫn như trên chưa đạt 100% yêu cầu. Dĩ nhiên ta có cách sắp xếp theo thứ tự nhưng phức tạp không đáng.
 
Trường hợp này ta có thể không cần dùng khoá như bạn đề cập.
Bạn chưa thấy dữ liệu gốc bao giờ.
Bảng 1: Dữ liệu tồn kho bao giờ cũng có: Nhiều kho, nhiều mặt hàng, mỗi kho nhiều mặt hàng, mỗi mặt hàng nhiều kho, mỗi mặt hàng nhiều lot nhập về.
Bảng 2: dữ liệu phát sinh (hàng ngày) cũng nhiều dạng như vậy và nhân lên nhiều ngày.
Không có trường khóa tạo quan hệ là chết chắc.
Mấu chốt CSDL bắt buộc phải vậy chứ phát sinh nhập xuất lot này, kho này mà lấy tồn đầu kho khác, lot khác là báo cáo vứt đi ngay.
Tuy nhiên, bài #227 trên chỉ gợi ý.
Đúng vậy, tôi chỉ gợi ý. Đã biết SQL, hỏi trong chủ đề SQL, thì đáng lẽ chỉ gợi ý vậy thôi là làm được.
 
Bạn chưa thấy dữ liệu gốc bao giờ.
Bảng 1: Dữ liệu tồn kho bao giờ cũng có: Nhiều kho, nhiều mặt hàng, mỗi kho nhiều mặt hàng, mỗi mặt hàng nhiều kho, mỗi mặt hàng nhiều lot nhập về.
Bảng 2: dữ liệu phát sinh (hàng ngày) cũng nhiều dạng như vậy và nhân lên nhiều ngày.
Không có trường khóa tạo quan hệ là chết chắc.
Mấu chốt CSDL bắt buộc phải vậy chứ phát sinh nhập xuất lot này, kho này mà lấy tồn đầu kho khác, lot khác là báo cáo vứt đi ngay.

Đúng vậy, tôi chỉ gợi ý. Đã biết SQL, hỏi trong chủ đề SQL, thì đáng lẽ chỉ gợi ý vậy thôi là làm được.
Vậy cái Group by nó không có ý nghĩa gì trong trường hợp này sao bạn? Kho thì đã Group theo mã kho rồi. Có nhiều kho mà có cùng mã hàng thì đâu ảnh hưởng gì phải không nào.
 
Lần chỉnh sửa cuối:
Vậy cái Group by nó không có ý nghĩa gì trong trường hợp này sao bạn? Kho thì đã Group theo mã kho rồi. Có nhiều kho mà có cùng mã hàng thì đâu ảnh hưởng gì phải không nào.
Group by theo tôi chỉ là biện pháp chữa cháy. Mà thôi, tôi nói thừa rồi. Thừa với người giỏi như bạn, còn đối với dữ liệu khác, người khác thì ... chuyện gì xảy ra nếu có 1 mặt hàng tồn kho không nhập xuất, hoặc 1 mặt hàng không tồn kho nhưng có nhập xuất?
 
Group by theo tôi chỉ là biện pháp chữa cháy. Mà thôi, tôi nói thừa rồi. Thừa với người giỏi như bạn, còn đối với dữ liệu khác, người khác thì ... chuyện gì xảy ra nếu có 1 mặt hàng tồn kho không nhập xuất, hoặc 1 mặt hàng không tồn kho nhưng có nhập xuất?
Hi hi!!! Có ràng buộc dữ liệu là lúc nhập liệu á bạn, ở đây là xử lý dữ liệu thô đã có sẵn. Đúng không bạn?
 
Web KT
Back
Top Bottom