Сделать PostgreSQL немного более устойчивым к ошибкам?
-
12-09-2019 - |
Вопрос
Это своего рода общий вопрос, который возникал в нескольких контекстах, приведенный ниже пример является репрезентативным, но не исчерпывающим.Меня интересуют любые способы научиться работать с Postgres на несовершенных (но достаточно близких) источниках данных.
Конкретный случай - я использую Postgres с PostGIS для работы с правительственными данными, опубликованными в шейп-файлах и xml.Использование модуля shp2pgsql, распространяемого вместе с PostGIS (например, на это dataset) Я часто получаю схему, подобную этой:
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 |
Я знаю, что по крайней мере 10 из этих переменных - fips, elevation, population и т.д. - должны быть целыми числами;но при попытке использовать их как таковые я получаю ошибки.В общем, я думаю, что мог бы решить большинство своих проблем, разрешив Postgres принимать пустую строку в качестве значения по умолчанию для столбца - скажем, 0 или -1 для типа int - при изменении столбца и типа.Возможно ли это?
Если я создам таблицу перед импортом с объявлениями типов, сгенерированными из исходного источника данных, я получу лучшие типы, чем с shp2pgsql, и смогу перебирать исходные записи, передавая их в базу данных, отбрасывая все неудачные вставки.Основная проблема заключается в том, что если у меня будет 1% плохих полей, равномерно распределенных по 25 столбцам, я потеряю 25% своих данных, поскольку данная вставка завершится ошибкой, если какое-либо поле будет плохим.Я бы хотел иметь возможность сделать вставку с максимальным усилием и исправить любые проблемы позже, вместо того чтобы терять так много строк.
Любой вклад от людей, имевших дело с подобными проблемами, приветствуется - я не сторонник MySQL, пытающийся заставить PostgreSQL совершать все те же ошибки, к которым я привык, - просто имею дело с данными, над которыми у меня нет полного контроля.
Решение
Не могли бы вы создать SQL-файл из shp2pgsql и немного обработать данные перед его выполнением?Если данные находятся в формате КОПИРОВАНИЯ, их должно быть легко разобрать и изменить "" на " " (вставить как null) для столбцов.
Другой возможностью было бы использовать shp2pgsql для загрузки данных в промежуточную таблицу, где все поля определены как просто тип "text", а затем использовать INSERT...Оператор SELECT для копирования данных в ваше конечное местоположение, с возможностью массирования данных в SELECT для преобразования пустых строк в null и т.д.
Я не думаю, что есть способ переопределить поведение того, как строки преобразуются в целые числа и так далее:возможно, вы могли бы создать свой собственный тип или домен и определить неявное приведение, которое было бы более мягким...но это звучит довольно неприятно, поскольку типы на самом деле являются всего лишь артефактами того, как ваши данные поступают в систему, а не тем, что вы хотите сохранить после этого.
Вы спрашивали об исправлении этого при изменении типа столбца:вы тоже можете это сделать, например:
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)
Что почти эквивалентно идее "промежуточной таблицы", которую я предложил выше, за исключением того, что теперь промежуточная таблица является ваш последний стол.Подобное изменение типа столбца в любом случае требует переписывания всей таблицы:таким образом, на самом деле использование промежуточной таблицы и переформатирование нескольких столбцов одновременно, вероятно, будет более эффективным.