(It is assumed that you are using DB2 for Linux / Unix / Windows, other platforms may vary slightly)
DELETE FROM (SELECT ROWNUMBER() OVER (PARTITION BY ONE, TWO, THREE) AS RN FROM SESSION.TEST) AS A WHERE RN > 1;
Gotta get what you are looking for.
The query uses the OLAP ROWNUMBER() function to assign a number to each row in each ONE , TWO , THREE combination. DB2 can then match the rows referenced by fullselect (A) as rows that the DELETE should remove from the table. In order to be able to use fullselect as the target for the delete clause, it must comply with the rules for the view to be deleted (see "Delete view" in the notes section).
Below is some proof (tested on LUW 9.7):
DECLARE GLOBAL TEMPORARY TABLE SESSION.TEST ( one CHAR(2), two CHAR(2), three CHAR(2), name CHAR(3) ) ON COMMIT PRESERVE ROWS; INSERT INTO SESSION.TEST VALUES ('A1', 'B1', 'C1', 'xyz'), ('A1', 'B1', 'C1', 'pqr'), ('A1', 'B1', 'C1', 'lmn'), ('A2', 'B2', 'C2', 'abc'), ('A2', 'B2', 'C2', 'def'), ('A3', 'B3', 'C3', 'ghi'); DELETE FROM (SELECT ROWNUMBER() OVER (PARTITION BY ONE, TWO, THREE) AS RN FROM SESSION.TEST) AS A WHERE RN > 1; SELECT * FROM SESSION.TEST;
Edit March 2, 2017:
In response to a question from Ahmed Anwar, if you need to capture what was deleted, you can also combine the deletion using the data change operator . "In this example, you can do something like the following, which will give you the column" rn ", one, two and three:
SELECT * FROM OLD TABLE ( DELETE FROM (SELECT ROWNUMBER() OVER (PARTITION BY ONE, TWO, THREE) AS RN ,ONE ,TWO ,THREE FROM SESSION.TEST) AS A WHERE RN > 1 ) OLD;