O Microsoft SQL Server tem o que considero uma função notavelmente sensata, try_cast()
que retorna a null
se a conversão não for bem-sucedida, em vez de gerar um erro.
Isso torna possível usar uma CASE
expressão ou um coalesce
para recorrer. Por exemplo:
SELECT coalesce(try_cast(data as int),0);
A questão é, o PostgreSQL tem algo semelhante?
A pergunta é feita para preencher algumas lacunas no meu conhecimento, mas também há o princípio geral de que alguns preferem uma reação menos dramática a alguns erros do usuário. Retornar a null
é mais fácil de se fazer em SQL do que um erro. Por exemplo SELECT * FROM data WHERE try_cast(value) IS NOT NULL;
. Na minha experiência, os erros do usuário às vezes são melhor tratados se houver um plano B.
Justificativa
É difícil envolver algo como o SQL Server
TRY_CAST
em uma função genérica do PostgreSQL. A entrada e a saída podem ser qualquer tipo de dados, mas o SQL é estritamente tipado e as funções do Postgres exigem que os tipos de parâmetro e retorno sejam declarados no momento da criação.O Postgres tem o conceito de tipos polimórficos , mas as declarações de funções aceitam no máximo um tipo polimórfico. O manual:
CAST ( expression AS type )
pareceria uma exceção a esta regra, pegando qualquer tipo e retornando qualquer (outro) tipo. Mascast()
só se parece com uma função enquanto é um elemento de sintaxe SQL sob o capô. O manual:Existe uma função separada para cada combinação de tipo de entrada e saída. (Você pode criar o seu próprio com
CREATE CAST
...)Função
Meu compromisso é usar
text
como entrada, pois qualquer tipo pode ser convertido emtext
. O elenco extra paratext
significa custo extra (embora não muito). O polimorfismo também adiciona um pouco de sobrecarga. Mas as partes moderadamente caras são o SQL dinâmico de que precisamos, a concatenação de strings envolvida e, acima de tudo, o tratamento de exceções.Dito isso, essa pequena função pode ser usada para qualquer combinação de tipos, incluindo tipos de matriz. (Mas modificadores de tipo como in
varchar(20)
são perdidos):O
INOUT
parâmetro_out
serve a dois propósitos:Você não chamaria como no seu exemplo:
.. onde
COALESCE
também elimina valores NULL genuínos da fonte (!!), provavelmente não como pretendido. Mas simplesmente:.. que retorna
NULL
naNULL
entrada, ou0
na entrada inválida.A sintaxe curta funciona while
data
é um tipo de caractere (comotext
ouvarchar
) e porque0
é um literal numérico que é digitado implicitamente comointeger
. Em outros casos, talvez seja necessário ser mais explícito:Exemplo de chamadas
Os literais de string não digitados funcionam imediatamente:
Valores digitados que têm uma conversão implícita registrada para
text
funcionar fora da caixa também:Lista abrangente de tipos de dados com conversão implícita registrada para
text
:Todos os outros tipos de entrada exigem uma conversão explícita para
text
:Poderíamos facilmente fazer o corpo da função funcionar para qualquer tipo, mas a resolução do tipo de função falha. Relacionado:
Se a conversão de um tipo específico para outro tipo específico for suficiente, você pode fazer isso com uma função PL/pgSQL:
Então
Devoluções
Se isso for apenas para números, outra abordagem seria usar uma expressão regular para verificar se a string de entrada é um número válido. Isso provavelmente seria mais rápido do que capturar exceções quando você espera muitos valores incorretos.
Aqui está um teste genérico, provavelmente muito lento.
Isso não aceitará tipos como
varchar(20)
(embora possamos adicionar outro parâmetro para aceitar "typemod" como20
.esta função retorna text porque as funções postgreqsl devem ter um tipo de retorno fixo. então você pode precisar de uma conversão explícita fora da função para forçar o resultado para o tipo desejado.
Versão genérica ligeiramente diferente - isso retorna verdadeiro ou falso, dependendo se o valor pode ou não ser convertido. Funciona também com domínios definidos pelo usuário. Isso funciona no PostgreSQL 12.
Aceitei uma resposta, mas pensei em adicioná-la para a posteridade. Aqui está a versão que eu uso no meu trabalho:
Isso é basicamente derivado da resposta de a_horse_with_no_name acima, mas um pouco mais consolidado.
O MSSQL
TRY_CAST
é mais genérico quando se trata de tipos de dados, assim como aCAST
própria função. Esta versão requer funções diferentes para diferentes tipos de dados, comocast_date
. Você pode expandi-lo para verificar o tipo de dados por conta própria, mas isso pode estar indo longe demais.Com o PostgreSQL, internamente, quando uma coerção falha, gera uma exceção fatal usando
ereport
. Isso é irrecuperável em coerções.Dados de amostra
Vamos supor uma taxa de erro de 1/5
Lista negra de dados ruins.
A solução normal para esse problema é aceitar liberalmente quando você cria tipos. É mais ou menos assim que as coisas funcionam agora. Se você precisar se proteger contra algum tipo de entrada falsa, em vez de capturar em caso de falha, apenas defina-o como nulo antes que ele falhe.
Se você quiser, pode colocar isso em uma
IMMUTABLE
instrução SQL também.Você também pode usar regexes e o que mais quiser no que diz respeito à verificação.
Tentar/Pegar
Este método é muito lento.
Se você precisar, então, por todos os meios, você precisa, no entanto, não seria a primeira ferramenta que eu pegaria.