Tornando o PostgreSQL um pouco mais tolerante a erros?
-
12-09-2019 - |
Pergunta
Essa é uma espécie de pergunta geral que surgiu em vários contextos, o exemplo abaixo é representativo, mas não exaustivo. Estou interessado em aprender a trabalhar com o Postgres sobre fontes de dados imperfeitas (mas próximas o suficiente).
O caso específico - estou usando o Postgres com o PostGIS para trabalhar com dados do governo publicados em Shapefiles e XML. Usando o módulo shp2pgsql distribuído com pós -gis (por exemplo isto conjunto de dados) Eu costumo obter esquema assim:
Column | Type |
------------+-----------------------+-
gid | integer |
st_fips | character varying(7) |
sfips | character varying(5) |
county_fip | character varying(12) |
cfips | character varying(6) |
pl_fips | character varying(7) |
id | character varying(7) |
elevation | character varying(11) |
pop_1990 | integer |
population | character varying(12) |
name | character varying(32) |
st | character varying(12) |
state | character varying(16) |
warngenlev | character varying(13) |
warngentyp | character varying(13) |
watch_warn | character varying(14) |
zwatch_war | bigint |
prog_disc | bigint |
zprog_disc | bigint |
comboflag | bigint |
land_water | character varying(13) |
recnum | integer |
lon | numeric |
lat | numeric |
the_geom | geometry |
Eu sei que pelo menos 10 desses varchars - os FIPs, elevação, população etc. devem ser ints; Mas ao tentar lançá -los como tal, recebo erros. Em geral, acho que poderia resolver a maioria dos meus problemas, permitindo que o Postgres aceite uma string vazia como um valor padrão para uma coluna -digamos 0 ou -1 para um tipo int -ao alterar uma coluna e alterar o tipo. Isso é possível?
Se eu criar a tabela antes de importar com as declarações de tipo geradas a partir da fonte de dados original, obtenho tipos melhores do que com shp2pgsql e posso iterar sobre as entradas de origem alimentando -as para o banco de dados, descartando qualquer inserções com falha. O problema fundamental é que, se eu tiver 1% de campos ruins, distribuídos uniformemente em 25 colunas, perderei 25% dos meus dados, pois uma determinada inserção falhará se algum campo for ruim. Eu adoraria poder fazer uma inserção de melhor esforço e resolver quaisquer problemas mais tarde, em vez de perder tantas linhas.
Qualquer entrada de pessoas que tenham lidado com problemas semelhantes é bem -vindo - eu não sou um cara do MySQL tentando agredir o PostGresql a cometer os mesmos erros que estou acostumado - apenas lidando com dados que não tenho controle total.
Solução
Você poderia produzir um arquivo SQL a partir de shp2pgsql e fazer uma massagem dos dados antes de executá -los? Se os dados estiverem no formato de cópia, deve ser fácil analisar e alterar "" para " n" (insira como nulo) para colunas.
Outra possibilidade seria usar shp2pgsql para carregar os dados em uma tabela de estadiamento onde todos os campos são definidos como apenas tipo de 'texto' e depois usar uma instrução de inserção ... selecione para copiar os dados para o seu local final, com a possibilidade de massagear os dados na seleção para converter strings em branco para nulo etc.
Eu não acho que exista uma maneira de substituir o comportamento de como as strings são convertidas em INTs e assim por diante: possivelmente você pode criar seu próprio tipo ou domínio e definir um elenco implícito que era mais branda ... mas isso parece bastante desagradável , como os tipos são realmente apenas artefatos de como seus dados chegam ao sistema e não algo que você deseja manter depois disso.
Você perguntou sobre como consertá -lo ao alterar o tipo de coluna: Você também pode fazer isso, por exemplo:
steve@steve@[local] =# create table test_table(id serial primary key, testvalue text not null);
NOTICE: CREATE TABLE will create implicit sequence "test_table_id_seq" for serial column "test_table.id"
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "test_table_pkey" for table "test_table"
CREATE TABLE
steve@steve@[local] =# insert into test_table(testvalue) values('1'),('0'),('');
INSERT 0 3
steve@steve@[local] =# alter table test_table alter column testvalue type int using case testvalue when '' then 0 else testvalue::int end;
ALTER TABLE
steve@steve@[local] =# select * from test_table;
id | testvalue
----+-----------
1 | 1
2 | 0
3 | 0
(3 rows)
Que é quase equivalente à ideia da "tabela de estadiamento" que sugeri acima, exceto que agora a tabela de estadiamento é Sua tabela final. Alterar um tipo de coluna como esse requer reescrever a tabela inteira de qualquer maneira: então, é provável que, usando uma tabela de preparação e reformatando várias colunas de uma só vez, provavelmente será mais eficiente.