Você não disse qual versão estava usando, mas no SQL 2005 e superior, você pode usar uma expressão de tabela comum com a cláusula OVER. Fica um pouco mais ou menos assim:
WITH cte AS (
SELECT[foo], [bar],
row_number() OVER(PARTITION BY foo, bar ORDER BY baz) AS [rn]
FROM TABLE
)
DELETE cte WHERE [rn] > 1
Brinque com ele e veja o que você consegue.
(Editar:Na tentativa de ser útil, alguém editou o
ORDER BY
cláusula no CTE. Para ser claro, você pode ordenar o que quiser aqui, não precisa ser uma das colunas retornadas pelo cte. Na verdade, um caso de uso comum aqui é que "foo, bar" é o identificador do grupo e "baz" é algum tipo de carimbo de data/hora. Para manter o mais recente, você faria ORDER BY baz desc
)