Cómo eliminar los valores en un Oracle-SQL-Tabla que significan lo mismo?
Pregunta
Tengo una tabla como la siguiente:
ID | Val1 | Val2
---------------------
1 | 1 | 2
2 | 1 | 3
3 | 2 | 1
4 | 2 | 3
5 | 3 | 1
6 | 3 | 2
Ahora mi problema es que 1 - 2 mediante el mismo como el 2 - 1 (mirada @ ID 1 e ID 3, por ejemplo) y quiero eliminar todas las entradas donde el valor 1 - valor 2 significa el mismo como valor de 2 - valor1 (espero que se podía seguir mi lógica aquí).
Solución
¿Qué tal esto:
DELETE t
WHERE ID IN
(SELECT t1.id
FROM t t1 JOIN t t2
ON (t1.val1 = t2.val2 AND
t1.val2 = t2.val1 AND
t1.id < t2.id));
I arbitrariamente mantuvo la fila con el valor más grande ID.
Ejemplo:
SQL> CREATE TABLE t (ID INTEGER, val1 INTEGER, val2 INTEGER);
Table created
SQL> INSERT INTO t VALUES(1,1,2);
1 row inserted
SQL> INSERT INTO t VALUES(2,1,3);
1 row inserted
SQL> INSERT INTO t VALUES(3,2,1);
1 row inserted
SQL> INSERT INTO t VALUES(4,2,3);
1 row inserted
SQL> INSERT INTO t VALUES(5,3,1);
1 row inserted
SQL> INSERT INTO t VALUES(6,3,2);
1 row inserted
SQL> INSERT INTO t VALUES(7,4,4);
1 row inserted
SQL> INSERT INTO t VALUES(8,4,4);
1 row inserted
SQL> SELECT * FROM t;
ID VAL VAL
--- --- ---
1 1 2
2 1 3
3 2 1
4 2 3
5 3 1
6 3 2
7 4 4
8 4 4
8 rows selected
SQL> DELETE t
2 WHERE ID IN (SELECT t1.id
3 FROM t t1 JOIN t t2 ON (t1.val1 = t2.val2 AND t1.val2 = t2.val1 AND t1.id < t2.id));
4 rows deleted
SQL> SELECT * FROM t;
ID VAL VAL
--- --- ---
3 2 1
5 3 1
6 3 2
8 4 4
SQL>
fácilmente adaptable para mantener diferentes filas, por ejemplo,
DELETE t
WHERE ID IN
(SELECT t1.id
FROM t t1 JOIN t t2
ON (t1.val1 = t2.val2 AND
t1.val2 = t2.val1 AND
(t2.val1 < t1.val1 OR (t2.val1 = t1.val1 AND t2.id > t1.id))));
ACTUALIZACIÓN: No podría pensar en una manera muy inteligente, así que aquí está el método de fuerza bruta para responder a la pregunta en su comentario:
CREATE TABLE t (ID INTEGER, val1 INTEGER, val2 INTEGER, val3 INTEGER);
INSERT INTO t VALUES (1, 1, 2, 3);
INSERT INTO t VALUES (2, 1, 3, 2);
INSERT INTO t VALUES (3, 2, 1, 3);
INSERT INTO t VALUES (4, 2, 3, 1);
INSERT INTO t VALUES (5, 3, 1, 2);
INSERT INTO t VALUES (6, 3, 2, 1);
INSERT INTO t VALUES (7, 1, 2, 4);
INSERT INTO t VALUES (8, 1, 3, 5);
INSERT INTO t VALUES (9, 1, 4, 2);
INSERT INTO t VALUES (10, 1, 1, 1);
INSERT INTO t VALUES (11, 1, 1, 1);
INSERT INTO t VALUES (12, 1, 3, 5);
SQL> select * from t order by id;
ID VAL VAL VAL
--- --- --- ---
1 1 2 3
2 1 3 2
3 2 1 3
4 2 3 1
5 3 1 2
6 3 2 1
7 1 2 4
8 1 3 5
9 1 4 2
10 1 1 1
11 1 1 1
12 1 3 5
12 rows selected
DELETE FROM t
WHERE ID IN (SELECT t1.ID FROM t t1 JOIN t t2 ON (t1.val1 = t2.val1 AND
t1.val2 = t2.val2 AND
t1.val3 = t2.val3 AND t1.id < t2.id)
UNION ALL
SELECT t1.ID FROM t t1 JOIN t t2 ON (t1.val1 = t2.val1 AND
t1.val2 = t2.val3 AND
t1.val3 = t2.val2 AND t1.id < t2.id)
UNION ALL
SELECT t1.ID FROM t t1 JOIN t t2 ON (t1.val1 = t2.val2 AND
t1.val2 = t2.val1 AND
t1.val3 = t2.val3 AND t1.id < t2.id)
UNION ALL
SELECT t1.ID FROM t t1 JOIN t t2 ON (t1.val1 = t2.val2 AND
t1.val2 = t2.val3 AND
t1.val3 = t2.val1 AND t1.id < t2.id)
UNION ALL
SELECT t1.ID FROM t t1 JOIN t t2 ON (t1.val1 = t2.val3 AND
t1.val2 = t2.val1 AND
t1.val3 = t2.val2 AND t1.id < t2.id)
UNION ALL
SELECT t1.ID FROM t t1 JOIN t t2 ON (t1.val1 = t2.val3 AND
t1.val2 = t2.val2 AND
t1.val3 = t2.val1 AND t1.id < t2.id));
select * from t order by id;
ID VAL VAL VAL
--- --- --- ---
6 3 2 1
9 1 4 2
11 1 1 1
12 1 3 5
Otros consejos
no recuerdo si esta sintaxis es válida en Oracle o no (en su mayoría el uso de un alias para el sujeto DELETE), pero se puede intentar esto:
DELETE
T1
FROM
My_Table T1
INNER JOIN My_Table T2 ON
T2.val1 = T1.val2 AND
T2.val2 = T1.val2 AND
WHERE
T1.val1 < T1.val2
Dado que no parece que el orden de las columnas a la materia, me gustaría hacer una decisión arbitraria y yo añadiría una restricción a la mesa para comprobar que val1 Por supuesto, usted también necesita asegurarse de que cualquier aplicación o código que inserta filas en la tabla sabe que la convención (val1 siempre debe ser el más pequeño de los dos valores) y la sigue.
Si eliminar los medios no aparece, intente esto, esto no devuelve ninguna fila para esas condiciones
select * from YourTable t1
where not exists (select * from YourTable t2
where t1.Val1 = t2.Val2
and t1.Val2 = t2.Val1)
delete from YourTable
where (Val1, Val2)
in (select Val2, Val1 from YourTable where Val1 > Val2)
Hay un caso esquina que no se maneja bien aquí. Ese es el caso en el que Val1 y Val2 son iguales. Eliminación de todas menos una ocurrencia de tales filas es un poco más complicado. Alguien tiene alguna idea?