2016-07-21 21 views
-1

Ich habe die folgende Tabelle mit diesen Aufzeichnungen als Beispiel:SQL wählen Sie in derselben Tabelle verbinden durch den Vergleich 2 Felder

 
+------+---------+---------+ 
| key | amount1 | amount2 | 
|------|---------|---------| 
| A | 100  | 0  | 
| B | 0  | 100  | 
| C | 100  | 0  | 
| D | 66  | 34  | 
| E | 99  | 12  | 
| F | 100  | 12  | 
| G | 12  | 99  | 
+------+---------+---------+ 

Ich mag würde alle Datensätze, in denen Felder amount1 einer Zeile löschen = Betrag2 eines anderen Zeile, aber nur paarweise: Wenn ich 2 Datensätze mit Betrag1 = Betrag2 finde, kann ich sie löschen, wenn ich einen dritten Eintrag finde, muss ich ihn behalten.

Beispiel: Die 1. Datensatz oben mit key = A hat amount1 = 100 und Betrag2 = 0 ist, hier wird das Paar Schlüssel B, wo amount1 = 0 und Betrag2 = 100. Zeile mit key = C gehalten werden müssen.

 
+------+---------+---------+ 
| key | amount1 | amount2 | 
+------+---------+---------+ 
| A | 100  | 0  | 
| B | 0  | 100  | Pair found with key = A : Delete key = A and key = B 
| C | 100  | 0  | No pair found as the 2 first records compose a pair 
| D | 66  | 34  | No pair found 
| E | 99  | 12  | No pair found 
| F | 100  | 12  | No pair found 
| G | 12  | 99  | Pair found with key = E : Delete records with key = E and key = G 
+------+---------+---------+ 

Das erwartete Ergebnis ist die unter einem:

 
+------+---------+---------+ 
| key | amount1 | amount2 | 
+------+---------+---------+ 
| C | 100  | 0  | 
| D | 66  | 34  | 
| F | 100  | 12  | 
+------+---------+---------+ 

Also entweder würde ich alle Paare bestimmen, wie dann löschen, oder direkt ohne Paar nur die Zeilen angezeigt werden soll.

Irgendwelche Hinweise?

Vielen Dank im Voraus für Ihre Hilfe,

+5

'mysql' oder' SQL-Server' oder 'Oracle'? – Jens

+0

Brauchen Sie in der Zeile A nur den Betrag1 und in der Zeile B den gleichen Betrag2, oder brauchen Sie gleichzeitig in der Zeile A den Betrag2 gleich dem Betrag1 in der Zeile B, bevor Sie die Zeilen A und B löschen können? Angenommen, Zeile E ist nicht vorhanden. Können Sie einfach die Zeilen F und G löschen, weil Zeile G Betrag1 gleich Zeile R Betrag2 ist (sie sind beide 12), obwohl der "andere Wert" 100 in einer Zeile und 99 in der anderen Zeile ist? – mathguy

+0

auch streng genommen, durch Ihre Beschreibung, wenn eine Zeile beam1 = betrag2 hat, dann könnte sich die Zeile selbst löschen. Ist das das gewünschte Verhalten oder sollte die Zeile immer eine ** andere ** Zeile abbrechen? – mathguy

Antwort

0

Eine Möglichkeit, es zu tun, und nicht besonders schön.

DELETE a 
FROM some_table a 
INNER JOIN 
(
    SELECT a.amount1 AS am1, 
      a.amount2 AS am2, 
      b.amount1 AS bm1, 
      b.amount2 AS bm2, 
      MIN(a.`key`) AS del_key 
    FROM some_table a 
    INNER JOIN some_table b 
    ON a.amount1 = b.amount2 
    AND a.amount2 = b.amount1 
    GROUP BY a.amount1, 
      a.amount2, 
      b.amount1, 
      b.amount2 
    UNION 
    SELECT a.amount1 AS am1, 
      a.amount2 AS am2, 
      b.amount1 AS bm1, 
      b.amount2 AS bm2, 
      MIN(b.`key`) del_key 
    FROM some_table a 
    INNER JOIN some_table b 
    ON a.amount1 = b.amount2 
    AND a.amount2 = b.amount1 
    GROUP BY a.amount1, 
      a.amount2, 
      b.amount1, 
      b.amount2 
) b 
ON a.key = b.del_key 

Dies erhält die passenden Beträge und den minimalen Wert der Schlüssel für jeden. Dann wird dies als Unterabfrage verwendet, um als Teil der DELETE-Anweisung der ursprünglichen Tabelle beizutreten.

+0

Eine Situation, die diese Abfrage nicht zu erfassen scheint: Sie haben zwei Zeilen mit Beträgen (12, 99) und fünf Zeilen mit Beträgen (99, 12). Das Ergebnis ist, dass die zwei Zeilen mit Beträgen (12, 99) und genau zwei der fünf Zeilen mit Beträgen (99, 12) gelöscht werden sollten. Ich glaube, dass diese Lösung nur einen von ihnen löschen wird, immer noch ein anderes Paar lassen, das könnte (sollte?) Gelöscht werden. – mathguy

+0

@mathguy - Sie haben recht, es löscht die ersten Duplikate. Angesichts der Demo-Daten nicht 100% sicher, was das OP will. Pluspunkt ist, dass es ziemlich Standard-SQL ist – Kickstart

0

So etwas wie das. Beachten Sie den zusätzlichen Aufwand, der erforderlich ist, um Paare von Zeilen mit Betrag1 = Betrag2 zu entfernen. Ich fügte ein paar zusätzliche Zeilen in meine Testdaten ein, um die ordnungsgemäße Ausführung der Abfrage zu testen.

Ich illustriere (mit dem MINUS-Operator) die Möglichkeit, nur die "verbleibenden" Zeilen in einer Abfrage anzuzeigen, nachdem die gepaarten Zeilen gelöscht (MINUS) wurden. Etwas Ähnliches kann für eine DELETE-Anweisung verwendet werden - obwohl es vielleicht eine bessere Vorgehensweise ist, eine zusätzliche Spalte flag zu haben, um die gepaarten Zeilen zu markieren, anstatt sie zu löschen (in der realen Welt wollen Sie nicht einfach historische Informationen löschen).

HINZUGEFÜGT: Diese Lösung ist für Oracle; Ich weiß nicht, welche Änderungen (wenn überhaupt) in anderen DB-Produkten benötigt werden. Beachten Sie auch, dass in dieser Lösung, wenn Sie zwei Zeilen mit Beträgen (12,99) und fünf mit (99,12) haben, nach "Löschungen" drei Zeilen mit (99,12) übrig sind. Es scheint mir, dass dies die logische Interpretation der Anforderungen in diesem Fall ist.

with 
    input_data (key, amount1, amount2) as (
     select  'A',  100,  0 from dual union all 
     select  'B',  0,  100 from dual union all 
     select  'C',  100,  0 from dual union all 
     select  'D',  66,  34 from dual union all 
     select  'E',  99,  12 from dual union all 
     select  'F',  100,  12 from dual union all 
     select  'G',  12,  99 from dual union all 
     select  'H',  200,  200 from dual union all 
     select  'I',  200,  200 from dual union all 
     select  'J',  200,  200 from dual union all 
     select  'K',  300,  300 from dual 
    ), 
    prep (key, amount1, amount2, rn) as (
     select key, amount1, amount2, 
       row_number() over (partition by amount1, amount2 order by key) 
     from input_data 
    ) 
select key, amount1, amount2 
from input_data 
minus 
select a.key, a.amount1, a.amount2 
from prep a inner join prep b 
       on 
       (
         a.amount1 = b.amount2 
        and a.amount2 = b.amount1 
        and a.amount1 != a.amount2 
        and a.rn   = b.rn 
       ) 
       or 
       (
         a.amount1 = b.amount2 
        and a.amount2 = b.amount1 
        and a.amount1 = b.amount1 
        and 
         (
          mod(a.rn, 2) = 1 
         and b.rn   = a.rn + 1 
         ) 
         or 
         (
          mod(a.rn, 2) = 0 
         and b.rn   = a.rn - 1      
         ) 
       ) 
; 

KEY AMOUNT1 AMOUNT2 
--- ---------- ---------- 
D   66   34 
F   100   12 
J   200  200 
K   300  300