规范化低完整性的表

时间:2008-08-08 16:19:21

标签: database

我已经递了一张大约18000行的表格。每条记录描述一个客户的位置。问题是,当该人创建表时,他们没有为“公司名称”添加字段,只添加“位置名称”,而一个公司可以有多个位置。

例如,以下是一些描述同一客户的记录:

位置表

 ID  Location_Name     
 1   TownShop#1        
 2   Town Shop - Loc 2 
 3   The Town Shop     
 4   TTS - Someplace   
 5   Town Shop,the 3   
 6   Toen Shop4        

我的目标是让它看起来像:

位置表

 ID  Company_ID   Location_Name     
 1   1            Town Shop#1       
 2   1            Town Shop - Loc 2 
 3   1            The Town Shop     
 4   1            TTS - Someplace   
 5   1            Town Shop,the 3   
 6   1            Toen Shop4        

公司表

 Company_ID  Company_Name  
 1           The Town Shop 

没有“公司”表格,我必须从代​​表多个位置的最具描述性或最佳位置名称生成公司名称列表。

目前我认为我需要生成一个类似的位置名称列表,然后手动浏览该列表。

对于如何处理此问题的任何建议表示赞赏。

@Neall,感谢您的发言,但不幸的是,每个位置名称都是截然不同的,没有重复的位置名称,只是类似。因此,在您的陈述结果中,“repcount”在每行中为1。

@yukondude,你的第4步是我提问的核心。

5 个答案:

答案 0 :(得分:1)

我以前必须这样做。唯一真正的方法是手动匹配各个位置。使用数据库的控制台界面并对select语句进行分组。首先,添加“公司名称”字段。然后:

SELECT count(*) AS repcount, "Location Name" FROM mytable
 WHERE "Company Name" IS NULL
 GROUP BY "Location Name"
 ORDER BY repcount DESC
 LIMIT 5;

找出列表顶部位置所属的公司,然后使用UPDATE ... WHERE“Location Name”=“The Location”语句更新公司名称字段。

P.S。 - 您应该将公司名称和位置名称分解为单独的表,并通过主键引用它们。

更新: - 哇 - 没有重复?你有多少条记录?

答案 1 :(得分:0)

是的,我上一篇文章中的第4步是一个doozy。

无论如何,您可能需要手动执行此操作,但您可以自动执行大部分操作。对于您提供的示例位置,如下所示的查询将设置相应的company_id值:

UPDATE  Location
SET     Company_ID = 1
WHERE   (LOWER(Location_Name) LIKE '%to_n shop%'
OR      LOWER(Location_Name) LIKE '%tts%')
AND     Company_ID IS NULL;

我相信这会与您的示例相匹配(我添加了IS NULL部分以不覆盖以前设置的Company_ID值),但当然在18,000行中您必须非常有创造力才能处理各种组合

其他可能有用的方法是使用公司中的名称生成如上所述的查询。您可以执行以下操作(在MySQL中):

SELECT  CONCAT('UPDATE Location SET Company_ID = ',
        Company_ID, ' WHERE LOWER(Location_Name) LIKE ',
        LOWER(REPLACE(Company_Name), ' ', '%'), ' AND Company_ID IS NULL;')
FROM    Company;

然后运行它生成的语句。这可以为你做很多垃圾工作。

答案 2 :(得分:0)

请更新问题,您是否有可用的公司名称列表?我问,因为你可以使用Levenshtein algo找到你的CompanyNames和LocationNames列表之间的关系。


<强>更新

  

没有公司名称列表,我必须从代​​表多个位置的最具描述性或最佳位置名称生成公司名称。

好的......试试这个:

  1. 通过查找由大部分或全部字母字符组成的LocationNames来构建候选CompanyNames列表。您可以使用regular expressions。将此列表存储在单独的表中。
  2. 按字母顺序对该列表进行排序,并(手动)确定哪些条目应为CompanyNames。
  3. 将每个CompanyName与每个LocationName进行比较,并得出匹配分数(使用Levenshtein或其他字符串匹配算法)。将结果存储在单独的表中。
  4. 设置一个阈值分数,以便任何MatchScore&lt;阈值不会被视为给定CompanyName的匹配。
  5. 按CompanyName手动审核LocationNames LocationName | MatchScore,并确定哪些实际匹配。通过MatchScore订购可以减少这个过程的痛苦。
  6. 上述操作的全部目的是使部件自动化并限制问题的范围。它远非完美,但希望能为您省去手工扫描18K记录的麻烦。

答案 3 :(得分:0)

我打算推荐一些复杂的令牌匹配算法,但要正确起来真的很棘手,如果你的数据没有很多相关性(拼写错误等)那么它就不会给出非常好的结果。

我建议你向Amazon Mechanical Turk提交一份工作,然后让人来解决。

答案 4 :(得分:0)

理想情况下,您可能需要一个名为Company的单独表,然后在此“Location”表中的company_id列,它是Company表的主键的外键,可能称为id。这样可以避免在此表中出现相当多的文本重复(超过18,000行,整数外键可以在varchar列上节省相当多的空间)。

但是你仍然面临着一种加载该公司表的方法,然后将其与位置中的行正确关联。没有通用的解决方案,但你可以沿着这些方向做点什么:

  1. 创建Company表,其id列自动递增(取决于您的RDBMS)。
  2. 找到所有独特的公司名称并将其插入公司。
  3. 将一个列company_id添加到接受NULL的位置(暂时),这是Company.id列的外键。
  4. 对于位置中的每一行,确定相应的公司,并使用该公司的ID更新该行的company_id列。这可能是最具挑战性的一步。如果您的数据与您在示例中显示的数据类似,那么您可能需要使用各种字符串匹配方法进行多次运行。
  5. 一旦Location中的所有行都有一个company_id值,那么你可以改变Company表以向company_id列添加一个NOT NULL约束(假设每个位置必须有一个公司,这似乎是合理的)。
  6. 如果您可以复制Location表,则可以逐步构建一系列SQL语句来填充company_id外键。如果你犯了一个错误,你可以重新开始并重新运行脚本直到失败。