2017-07-21 3 views
6

Ich habe zwei Tabellen. Einer steht für noch offene Rechnungen (Tabelle #OPEN) und der andere für verfügbares Geld (Tabelle #overpay). Beide haben eine Spalte USERID, mit denen sie verbunden werden:Wie zwei Tabellen zu verbinden, aber jede Zeile nur einmal verwenden

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

Die Tabelle hat eine Spalte #Entfernen OpenRow durch welche die offenen Mengen bestellt werden (pro Nutzer). Ich möchte Einträge aus der Tabelle kartieren #OVERPAY auf Einträge in der Tabelle #OPEN auf folgende Weise:

  • Ein Eintrag wird abgebildet, wenn # OVERPAY.Rest> = # OPEN.AMT
  • Kleinere Werte in #Entfernen. OpenRow erste abgebildet bekommen können
  • Jeder Eintrag in #OVERPAY nur einmal verwendet werden
  • Jeder Eintrag in #Entfernen kann nur einmal verwendet werden

Die letzten beiden Punkte in dieser Liste sind, was gibt mir ein Kopfschmerzen.

Dies sind einige verspottet Daten:

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) 

Das gewünschte Ergebnis wäre:

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

Ich weiß, wie ich es mit einem CURSOR tun könnte:

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 

Aber da Das ist schrecklich in Bezug auf die Leistung (Ich arbeite mit einer riesigen Menge von Daten) Ich suche nach einer Option zu tun es ohne

Antwort

6

Versuchen 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

Looping YES! Das scheint genau zu sein, wonach ich suche. Ich werde es in meinem realen Szenario ausprobieren und danach zurückkommen. Vielen Dank! –

+0

Es funktioniert wie ein Zauber. Sehr schöne Lösung! –

+0

Und nette Frage. – Serg

Verwandte Themen