2017-07-21 18 views
6

Tôi có hai bảng. Một đại diện cho các hóa đơn vẫn mở (bảng #OPEN) và một số khác thể hiện số tiền có sẵn (bảng #overpay). Cả hai đều có một cột USERID mà họ có thể được tham gia:Cách tham gia hai bảng nhưng chỉ sử dụng mỗi hàng một lần

CREATE TABLE #OVERPAY(OID INT, USERID CHAR(1), Rest INT) 
CREATE TABLE #OPEN(IID INT, USERID CHAR(1), Amt INT, OpenROW INT) 

CáC#OPEN bảng có một cột OpenRow mà số tiền mở được sắp xếp (mỗi người dùng). Tôi muốn để lập bản đồ mục từ bảng #OVERPAY tới mục trong bảng #OPEN theo cách sau:

  • Một mục được ánh xạ khi # OVERPAY.Rest> = # OPEN.AMT
  • nhỏ hơn giá trị trong #OPEN. OpenRow được ánh xạ đầu tiên
  • Mỗi mục trong #OVERPAY chỉ có thể được sử dụng một lần
  • Mỗi mục trong #OPEN chỉ có thể được sử dụng một lần

hai điểm cuối cùng trong danh sách đó là những gì mang lại cho tôi một đau đầu.

Đây là một số dữ liệu chế giễu:

OID USERID REST 
-------------------- 
1  'A'  10 
2  'A'  15 
3  'F'  5 
4  'H'  20 
5  'H'  5 

INSERT INTO #OVERPAY(OID, USERID, Rest) 
VALUES (1, 'A', 10), (2, 'A', 15), (3, 'F', 5), 
     (4, 'H', 20), (5, 'H', 5) 

OID USERID Amt OpenRow 
----------------------------- 
1  'A'  10 1 
2  'A'  10 2 
3  'A'  15 3 
4  'F'  5  1 
5  'H'  15 1 
6  'H'  10 2 
7  'P'  33 1 

INSERT INTO #OPEN(IID, USERID, Amt, OpenROW) 
VALUES (1, 'A', 10, 1), (2, 'A', 10, 2), 
     (3, 'A', 15, 3), (4, 'F', 5, 1), 
     (5, 'H', 15, 1), (6, 'H', 10, 2), 
     (7, 'P', 33, 1) 

Kết quả mong muốn sẽ là:

OID IID 
---------- 
1  1 
2  2 
3  4 
4  5 

tôi biết làm thế nào tôi có thể làm điều đó với một CURSOR:

CREATE TABLE #map (OID INT, IID INT) 
CREATE TABLE #usedIID(IID INT) 

DECLARE @OID INT, @USERID CHAR(1), @Rest INT 

DECLARE ov_cursor CURSOR FOR 
    SELECT OID, USERID, REST 
    FROM #OVERPAY 

OPEN ov_cursor 
FETCH NEXT FROM ov_cursor INTO @OID, @USERID, @REST 

WHILE @@FETCH_STATUS = 0 
BEGIN 
    DECLARE @IID INT 

    INSERT INTO #map (OID, IID) 
    OUTPUT inserted.IID INTO #usedIID (IID) 
     SELECT TOP 1 @OID, o.IID 
     FROM #OPEN o 
     LEFT JOIN #usedIID u ON u.IID = o.IID 
     WHERE o.USERID = @USERID AND o.Amt <= @REST AND u.IID IS NULL 

    FETCH NEXT FROM ov_cursor INTO @OID, @USERID, @REST 
END 

CLOSE ov_cursor 
DEALLOCATE ov_cursor 

Nhưng kể từ điều này thật khủng khiếp về hiệu suất (tôi đang làm việc với một bộ dữ liệu khổng lồ) Tôi đang tìm kiếm một tùy chọn để làm điều đó mà không cần bất kỳ vòng lặp

Trả lời

6

Hãy thử DENSE_RANK

SELECT OID, IID 
FROM (
    SELECT op.OID, n.IID, OpenRow 
     , dense_rank() over(partition by iid order by oid) rnkIid 
     , dense_rank() over(partition by oid order by OpenRow) rnkOid 
    FROM #OVERPAY op 
    JOIN #OPEN n ON op.USERID = n.USERID AND op.Rest >= n.AMT 
) t 
WHERE rnkIid = rnkOid 
ORDER BY OID, IID 
+0

YES! Điều đó dường như chính xác, những gì tôi đang tìm kiếm. Tôi sẽ thử nó trong kịch bản thế giới thực của tôi và quay lại sau đó. Cảm ơn bạn! –

+0

Nó hoạt động như một sự quyến rũ. Giải pháp rất đẹp! –

+0

Và câu hỏi hay. – Serg

Các vấn đề liên quan