这是一个在几种情况下出现的一般性问题,下面的例子具有代表性,但并非详尽无遗。我对任何学习使用Postgres的不完美(但足够接近)数据源的方式感兴趣。
具体案例 - 我正在使用Postgres和PostGIS来处理在shapefile和xml中发布的政府数据。使用随PostGIS一起发布的shp2pgsql模块(例如在this数据集上)我经常得到这样的模式:
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种 - 鱼腥草,海拔,种群等应该是整体的;但是当我试图将它们投射时,我会得到错误。总的来说,我认为我可以通过允许Postgres接受空字符串作为列的默认值来解决我的大多数问题 - 比如说int类型为0或-1 - 当更改列并更改类型时。这可能吗?
如果我在使用从原始数据源生成的类型声明导入之前创建表,我会获得比使用shp2pgsql更好的类型,并且可以迭代将它们提供给数据库的源条目,丢弃任何失败的插入。根本问题是,如果我有1%的坏字段,均匀分布在25列以上,我将丢失25%的数据,因为如果任何字段不好,给定的插入将失败。我希望能够做出尽力而为的插入并在以后修复任何问题,而不是丢失那么多行。
来自处理类似问题的人的任何意见都是受欢迎的 - 我不是一个试图打击PostgreSQL以犯下我习惯的所有相同错误的MySQL人 - 只处理我无法完全控制的数据
答案 0 :(得分:3)
你能从shp2pgsql生成一个SQL文件,并在执行之前对数据进行一些按摩吗?如果数据是COPY格式,则应该很容易解析并将“”更改为“\ N”(插入为空)列。
另一种可能性是使用shp2pgsql将数据加载到临时表中,其中所有字段都定义为“文本”类型,然后使用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)
这几乎相当于我上面建议的“临时表”的想法,除了现在登台表 是你的最终表。改变这样的列类型需要重写整个表:所以实际上,使用登台表并一次重新格式化多个列可能会更有效。