And one more alternative example:
-- Test table
CREATE TABLE dummy_data (
author_id int,
message text
);
-- Test data
INSERT INTO dummy_data ( author_id, message )
VALUES
( 123, '"message!"' ),
( 123, '"message!"' ),
( 123, '"different message"' ),
( 124, '"message!"' ),
( 124, '"message!"' ),
( 125, '"message!"' );
-- Delete query
DELETE FROM dummy_data
WHERE ctid NOT IN (
SELECT max( ctid )
FROM dummy_data
GROUP BY message -- this is important to specify
)
-- just for test returning deleted records,
-- you may ignore it, if don't want
RETURNING *;
-- Confirming result:
SELECT * FROM dummy_data ;
author_id | message
-----------+---------------------
123 | "different message"
125 | "message!"
(2 rows)
See more about system columns: https://www.postgresql.org/docs/current/static/ddl-system-columns.html
EDIT:
Additional example as was requested limiting the range by IDs (author_id).
Pure query:
DELETE FROM dummy_data
USING ( SELECT ARRAY[ 123, 124] ) v(id)
WHERE author_id = ANY ( v.id )
AND ctid NOT IN (
SELECT max( ctid )
FROM dummy_data
WHERE author_id = ANY ( v.id )
GROUP BY message
);
Same query with comments:
DELETE FROM dummy_data
-- Add your 'author_id' values into array here.
-- Reason we list it here with USING statement is
-- because we need to compare values in two places
-- and if list is too big it would be annoyance to
-- write it 2 times :)
USING ( SELECT ARRAY[ 123, 124] ) v(id)
-- First we get all the authors in the batch by ID
WHERE author_id = ANY ( v.id )
-- Secondly we get max CTID to ignore using same
-- authors range in batch scope
AND ctid NOT IN (
SELECT max( ctid )
FROM dummy_data
WHERE author_id = ANY ( v.id )
GROUP BY message
);
-- This will delete following rows:
author_id | message
-----------+------------
123 | "message!"
123 | "message!"
124 | "message!"
(3 rows)
-- Leaving the state to table:
author_id | message
-----------+---------------------
123 | "different message"
124 | "message!"
125 | "message!"
(3 rows)
author_id123 and 124 have "message2" )? then what is desirable result ?123, 'message!'row to delete, should it delete all of these rows?