在Python中从字符串中推断出适当的数据库类型声明

时间:2009-06-04 19:14:59

标签: python sqlite postgresql types

我正在从Python词典构建一些Postgres表,其中{'key':'value'}对对应于列'key'和字段'value'。这些是从.dbf文件生成的 - 我现在将.dbf文件的内容传递到一个脚本列表中,该脚本返回如下列表:

{'Warngentyp': '', 'Lon': '-81.67170', 'Zwatch_war': '0', 'State':...

目前我将这些放入没有类型声明的sqlite数据库,然后将其转储到.sql文件,手动编辑架构,然后导入Postgres。

我希望能够推断出正确的类型声明,基本上遍历一个字符串列表,如['0','3','5']或['ga','ca','tn' ]或['-81.009','135.444',' - 80.000']并生成类似'int','varchar(2)','float'的内容。 (我会对Python,Postgres或SQLite工具同样满意。)

是否存在执行此操作的程序包或实现它的简单方法?

4 个答案:

答案 0 :(得分:5)

不要使用eval。如果有人插入了错误的代码,它可能会阻塞您的数据库或服务器。

改为使用这些

def isFloat(s):
try:
    float(s)
    return True
except (ValueError, TypeError), e:
    return False


str.isdigit()

其他一切都可以是varchar

答案 1 :(得分:2)

你不需要宣传类型宣言!!!

您可以直接从.dbf文件派生出您想要的内容。每列都有一个名称,一个类型代码(C =字符,N =数字,D =日期(yyyymmdd),L =逻辑(T / F),如果文件来自Foxpro,还有更多类型),长度(相关时间) ),以及一些小数位(对于N型)。

用于从.dbf文件中挖掘数据所需的任何软件,使用该信息将每个数据转换为适当的Python数据类型。

字典?为什么?通过少量工作,可以修改该软件以基于这些列定义生成CREATE TABLE语句,并为每行数据生成INSERT语句。

我认为您正在使用几个已发布的Python DBF读取模块之一。其中任何一个都应该具备您需要的功能:打开.dbf文件,获取列名,获取列类型等信息,获取每行数据。如果您对所使用的模块不满意,请与我联系;我有一个未发表的,就读DBF而言,它结合了其他的更好的功能,避免了最糟糕的功能,就像你使用纯Python实现一样快,处理所有的Visual Foxpro数据类型和_NullFlags伪 - 列,处理备忘录等。

HTH

====== 附录: 当我说你不需要推断类型时,你没有说明你有一堆包含数字的C类字段。

FIPS字段:有些是有一些,有些没有前导零。如果你要使用它们,你将面临'012'!='12'!= 12问题。我建议剥离前导零并将它们保留在整数列中,在报告中恢复前导零或者如果你真的需要的话还原。为什么每个国家的fips和县fips都有2个?

填充:在示例文件中,几乎所有都是整数。四个像40552.0000,合理的数字是空白/空。你似乎认为人口是重要的,并问“有可能一小部分人口领域包含......?”数据中有任何可能的东西。不要怀疑和猜测,调查!我强烈建议您按人口顺序对数据进行排序并注意它;你会发现同一个州的多个地方共享相同的人口数。例如。在纽约州有35个地方,其pop'n被称为8,008,278;它们分布在6个县。其中29个PL_FIPS值为51000; 5有5100 - 看起来像一个尾随的零问题: - (

提示在float和int之间做出决定:首先尝试anum = float(chars) ;如果成功,检查int(anum)== anum。

ID:精彩的“唯一ID”; 59个案例不是int - 几个在加拿大(该网站称“美国城市”;这是一个未解决的边界争议的文物吗?),有些包含“数字”这个词,有些是空的。

低调的果实:我认为推断人口实际上是整数是0.1英寸以上: - )

如果全部([int(value)...逻辑:

)存在严重缺陷
>>> all([int(value) for value in "0 1 2 3 4 5 6 7 8 9".split()])
False
>>> all([int(value) for value in "1 2 3 4 5 6 7 8 9".split()])
True
>>>

你显然认为你正在测试所有的字符串都可以转换成int,但是你要添加骑手“并且都是非零的”。 Ditto后来漂了几行。

如果只有一个零值,则声明该列不是整数。 即使在修复之后,如果只有一个空值,则将其称为varchar。 我建议的是:计算有多少是空的(在规范化空格(应该包括NBSP)之后),有多少合格为整数,有多少非整数非空的合格为float,以及有多少“其他”。检查“其他”的;决定是否拒绝或修复;重复直到开心: - )

我希望其中一些有帮助。

答案 2 :(得分:1)

您可以type(eval(elem))不安全地确定整数和浮点数,其中elem是列表的元素。 (但是你需要检查elem是否存在可能的错误代码)

更安全的方法可以是执行以下操作

a = ['24.2', '.2', '2']
try:
    if all(elem.isdigit() for elem in a):
        print("int")
    elif all(float(elem) for elem in a):
        print("float")
except:
    i = len(a[0])
    if all(len(elem)==i for elem in a):
        print("varchar(%s)"%i)
    else:
        print "n/a"

答案 3 :(得分:1)

感谢您的帮助,这对于更新来说有点长,这是我如何结合答案。我从一个这样的dicts列表开始,从dbf文件生成:

dbf_list = [{'Warngentyp': '', 'Lon': '-81.67170', 'Zwatch_war': '0', 'State':...

然后是一个函数,每列返回1000个值以测试最佳数据库类型声明:{'column_name':['list', 'of', 'sample', 'values'], 'col2':['1','2','3','4'...,如下所示:

def sample_fields(dicts_, number=1000): #dicts_ would be dbf_list from above
    sample = dict([[item, []] for item in dicts_[1]])
    for dict_ in dicts_[:number]:
        for col_ in dict_:
            sample[col_].append(dict_[col_])
    return sample

然后你结合了Unknown和jacob方法:varchar是一个很好的默认值,浮点数和整数基本上足以满足其他所有要求,all清晰而快速:

def find_typedefs(sample_dict): #arg is output of previous function
    defs_ = {}
    for key in sample_dict:
        defs_[key] = 'varchar(255)'
        try:
            if all([int(value) for value in sample_dict[key]]):
                defs_[key] = 'int'
        except:
            try:
                if all([float(value) for value in sample_dict[key]]):
                    defs_[key] = 'float'
            except:
                continue
    return defs_

然后将返回的dict格式化为create table语句,迭代原始大列表中的值并将它们提供给数据库。它工作得很好,我现在正在跳过中间的sqlite步骤,再次感谢。

John Machin的更新:我正在使用随PostGIS一起发布的shp2pgsql库。它使用类似this one的源:

创建如下所示的架构
   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              | 

那里有些东西必须是错误的 - Fips是联邦信息处理标准,它应该是0到100,000之间的整数。人口,海拔等等。也许我有更多的postgres特定问题,我不介意丢失少量数据,或者将其推入表中以查找错误或其他内容,同时尝试更改类型,例如人口字段。 dbf类型检查有多严格?例如,我看到每个shp2pgsql的数量是varchar(12)。是否有可能某些小部分人口字段包含“2,445 Est”之类的内容?如果我采用我在这个问题中提出的方法,使用前千条记录,我得到一个这样的模式:

   Column   |          Type          |
------------+------------------------+-
 warngentyp | character varying(255) | 
 lon        | double precision       | 
 zwatch_war | character varying(255) | 
 state      | character varying(255) | 
 recnum     | character varying(255) | 
 pop_1990   | integer                | 
 land_water | character varying(255) | 
 elevation  | integer                | 
 prog_disc  | integer                | 
 comboflag  | character varying(255) | 
 sfips      | integer                | 
 zprog_disc | integer                | 
 pl_fips    | integer                | 
 county_fip | integer                | 
 population | integer                | 
 watch_warn | integer                | 
 name       | character varying(255) | 
 st         | character varying(255) | 
 lat        | double precision       | 
 st_fips    | integer                | 
 cfips      | integer                | 
 id         | integer                | 
 warngenlev | integer                |

另一方面,如果我检查all中的每个值(['list','of','everything'...]),我会得到一个更像第一个的模式。我可以在这里容忍一些数据丢失 - 如果一些城镇的入口是错误的,并且它不会显着影响人口数据等。

我只使用一个名为dbview的旧包来将dbf文件传输到这些脚本中 - 我不是试图映射任何格式的本机功能。我认为shp2pgsql会在这方面选择低调的果实。对dbview或其他软件包的任何建议都是受欢迎的 - 尽管在其他情况下我可能不会使用dbf文件,并且无论如何都需要找到最好的类型。我还要问一个关于postgresql的问题,看看我是否能找到该级别的解决方案。