Eu tinha o mesmo tipo de necessidade e achei que isso funcionava bem para mim (postgres 8.4):
CAST((COALESCE(myfield,'0')) AS INTEGER)
Alguns casos de teste para demonstrar:
db=> select CAST((COALESCE(NULL,'0')) AS INTEGER);
int4
------
0
(1 row)
db=> select CAST((COALESCE('','0')) AS INTEGER);
int4
------
0
(1 row)
db=> select CAST((COALESCE('4','0')) AS INTEGER);
int4
------
4
(1 row)
db=> select CAST((COALESCE('bad','0')) AS INTEGER);
ERROR: invalid input syntax for integer: "bad"
Se você precisar lidar com a possibilidade de o campo ter texto não numérico (como "100bad"), você pode usar regexp_replace para eliminar caracteres não numéricos antes da conversão.
CAST(REGEXP_REPLACE(COALESCE(myfield,'0'), '[^0-9]+', '', 'g') AS INTEGER)
Valores de texto / varchar como "b3ad5" também fornecerão números
db=> select CAST(REGEXP_REPLACE(COALESCE('b3ad5','0'), '[^0-9]+', '', 'g') AS INTEGER);
regexp_replace
----------------
35
(1 row)
Para abordar a preocupação de Chris Cogdon com a solução de não dar 0 para todos os casos, incluindo um caso como "ruim" (sem caracteres de dígito), fiz esta declaração ajustada:
CAST((COALESCE(NULLIF(REGEXP_REPLACE(myfield, '[^0-9]+', '', 'g'), ''), '0')) AS INTEGER);
Funciona de maneira semelhante às soluções mais simples, exceto que dará 0 quando o valor a ser convertido for apenas caracteres que não sejam dígitos, como "incorreto":
db=> select CAST((COALESCE(NULLIF(REGEXP_REPLACE('no longer bad!', '[^0-9]+', '', 'g'), ''), '0')) AS INTEGER);
coalesce
----------
0
(1 row)