一旦数据在系统中,就修复坏的BAD数据库设计


哇塞
2025-03-18 08:46:17 (10天前)


各种(200多个)国家。

ID
种类
阿富汗
阿尔巴尼亚
阿尔及利亚
美属萨摩亚
安道尔
安哥拉
….
西

撒哈拉
</跨度>

也门
赞比亚
津巴布韦

数据的样本将是这样的

18 条回复
  1. 0# 红蜻蜓 | 2019-08-31 10-32



    当我遇到这些时,我编写了一个脚本来进行转换,而不是尝试在SQL中执行。对我来说,它通常更快更容易。选择您喜欢的任何语言。


  2. 1# 扬尘 | 2019-08-31 10-32



    如果我必须创建一堆类似的SQL语句并执行所有这些语句,我经常发现Excel非常方便。采取原始查询。如果您在A列中有一个国家/地区列表,而在B列中有您的SQL语句,则将其格式化为文本(在引号中),并在sql中插入单元格引用



    例如=“INSERT INTO new_table SELECT …(species。”&amp; A1&amp;“)= …));”



    然后只需复制公式以创建200个不同的SQL语句,将列复制/粘贴到编辑器并按F5。当然,您可以根据需要使用尽可能多的变量来执行此操作。


  3. 2# 满目山河 | 2019-08-31 10-32



    您可能想要创建替换表。脚本类型取决于您可以使用的脚本语言,但您应该只需列出您现在拥有的表的列就可以创建国家/地区ID表。完成后,您可以执行一些字符串替换以遍历所有唯一的国家/地区名称,并插入到itemsFoundInCountry表中,其中给定的国家/地区列不为空。


  4. 3# 明天会更好 | 2019-08-31 10-32



    当我读到“糟糕的BAD数据库设计”这个标题时,我很想知道它有多糟糕。你没有让我失望:)



    正如其他人提到的,脚本是最简单的方法。这可以通过在PHP中编写大约15行代码来实现。




    1. SELECT * FROM ugly_table;
      while(row)
      foreach(row as field => value)
      if(value == 1)
      SELECT country_id from country_table WHERE country_name = field;

    2. if(field == Species’)
      SELECT species_id from species_table WHERE species_name = value;

    3. INSERT INTO better_table (…)

    4. </code>


    显然这是伪代码,不会像现在这样工作。您还可以通过在此处添加插入语句来动态填充国家/地区和物种表。


  5. 4# 你瞎啊!撞我心了 | 2019-08-31 10-32



    在SQL Server中,这将生成您演示的自定义选择。您可以推断插入





    1. select
      SELECT Species.ID, Country.CountryID FROM Country, Species WHERE (((Species.’ +
      c.name +
      ‘)=1)) AND (((Country.Country)=”‘ +
      c.name +
      ‘“))’
      from syscolumns c
      inner join sysobjects o
      on o.id = c.id
      where o.name = old_table_name

    2.   </code>
    3. </pre>

  6. 5# Coolkid | 2019-08-31 10-32



    当我遇到类似问题时,我发现生成生成SQL脚本的脚本很方便。这是你提供的样本,抽象使用%PAR1%代替阿富汗。




    1. SELECT Species.ID, Country.CountryID
      FROM Country, Species
      WHERE (((Species.%PAR1%)=1)) AND (((Country.Country)=”%PAR1%”))
      UNION

    2. </code>


    此外,关键词union已被添加为组合所有选择的一种方式。



    接下来,您需要一个根据现有数据生成的国家/地区列表:



    阿富汗
    阿尔巴尼亚





    接下来,您需要一个可以遍历国家/地区列表的脚本,并且对于每次迭代,
    产生一个输出,在第一次迭代时替代阿富汗%PAR1%,在第二次迭代时替代阿尔巴尼亚,依此类推。该算法就像文字处理器中的邮件合并一样。编写这个脚本有点工作。但是,一旦你拥有它,你可以在像这样的几十个一次性项目中使用它。



    最后,您需要手动将最后一个“UNION”更改回分号。



    如果您可以使用Access来执行此巨型联合,则可以以所需的形式获取所需的数据,并将其插入新表中。


  7. 6# 张三岁 | 2019-08-31 10-32



    如果这是SQL Server,则可以使用sys.columns表查找原始表的所有列。然后,您可以使用动态SQL和pivot命令来执行您想要的操作。查看那些在线的语法。


  8. 7# afs-loliaholic | 2019-08-31 10-32



    您可能会聪明地查询系统表中的列名,然后构建一个动态查询字符串来执行,但老实说,这可能比快速脚本更难以为您生成SQL语句。



    希望您没有太多动态SQL代码来访问隐藏在代码库中的旧表。那可能是



    困难的部分。


  9. 8# v-star*위위 | 2019-08-31 10-32



    和其他人一样,我很可能会以任何适合你的方式快速修复它。



    通过这些类型的转换,它们是一个关闭的项目,快速修复,并且代码不必是优雅的,它只需要工作。对于这些类型的事情,我已经做了很多方面。


  10. 9# 镜乃Kagamino | 2019-08-31 10-32



    OBTW,



    如果您有针对旧表运行的查询,则需要创建一个使用新表复制旧表的视图。您需要执行一个组来对表进行非规范化。



    告诉您的用户将来不支持旧表/视图,所有新查询或旧查询的更新都必须使用新表。


  11. 10# 呵呵 | 2019-08-31 10-32



    我会通过对您的SpeciesFoundInCountry表进行轻微的临时修改来实现它的三步过程。我会在该表中添加一列来存储国家/地区名称。然后步骤如下。



    1)创建/运行一个脚本,该脚本遍历源表中的列,并在SpeciesFoundInCountry中为每个具有true值的列创建记录。此记录将包含国家/地区名称。
    2)运行SQL语句,通过加入Country Name上的Country表来更新SpeciesFoundInCountry.CountryID字段。
    3)通过删除CountryName列清除SpeciesFoundInCountry表。



    这是一个小的MS Access VB / VBA伪代码,为您提供要点




    1. Public Sub CreateRelationshipRecords()

    2. Dim rstSource as DAO.Recordset
      Dim rstDestination as DAO.Recordset
      Dim fld as DAO.Field
      dim strSQL as String
      Dim lngSpeciesID as Long

    3. strSQL = SELECT * FROM [ORIGINALTABLE]”
      Set rstSource = CurrentDB.OpenRecordset(strSQL)
      set rstDestination = CurrentDB.OpenRecordset(“SpeciesFoundInCountry”)

    4. rstSource.MoveFirst

    5. Step through each record in the original table
      Do Until rstSource.EOF
      lngSpeciesID = rstSource.ID
      Now step through the fields(columns). If the field
      value is one (1), then create a relationship record
      using the field name as the Country Name
      For Each fld in rstSource.Fields
      If fld.Value = 1 then
      with rstDestination
      .AddNew
      .Fields(“CountryID”).Value = Null
      .Fields(“CountryName”).Value = fld.Name
      .Fields(“SpeciesID”).Value = lngSpeciesID
      .Update
      End With
      End IF
      Next fld
      rstSource.MoveNext
      Loop

    6. Clean up
      rstSource.Close
      Set rstSource = nothing
      ….

    7. End Sub

    8. </code>


    在此之后,您可以运行一个简单的SQL语句来更新SpeciesFoundInCountry表中的CountryID值。



    UPDATE SpeciesFoundInCountry INNER JOIN Country ON SpeciesFoundInCountry.CountryName = Country.CountryName SET SpeciesFoundInCountry.CountryID = Country.CountryID;



    最后,您所要做的就是通过删除CountryName列来清理SpeciesFoundInCountry表。



    ** SIDE注意:我发现让国家/地区表格也包含ISO缩写(国家/地区代码)非常有用。有时,它们在其他表中用作外键,因此不必在查询中包含到Country表的连接。



    欲了解更多信息:

    http://en.wikipedia.org/wiki/Iso_country_codes


  12. 11# 石茶 | 2019-08-31 10-32



    我会使用脚本生成所有单个查询,因为这是一次性导入过程。



    某些程序(如Excel)擅长混合不同维度的数据(将列名与行内数据进行比较),但关系数据库很少。



    但是,您可能会发现某些系统(例如Microsoft Access,令人惊讶)具有可用于规范化数据的便捷工具。就个人而言,我发现编写脚本的速度更快,但是你使用Access和脚本编写的相关技能可能与我的不同。


  13. 12# IP地址 | 2019-08-31 10-32



    如果这是SQL Server,您将使用Unpivot命令,但查看您为其分配的标记以供访问 - 我是对的吗?



    虽然有一个

    访问中的枢轴命令

    ,没有反向声明。



    看起来可以通过复杂的连接来完成。检查一下

    有趣的文章

    关于如何在select命令中取消忽略的问题。


  14. 13# 氷寒 | 2019-08-31 10-32



    为什么要在SQL中执行此操作?只需编写一个执行转换的小脚本。


  15. 14# AsuRa | 2019-08-31 10-32



    这是(希望)一次性的练习,所以一个不优雅的解决方案可能不会听起来那么糟糕。



    问题(因为,我确定你只是太清楚了!)是在你的查询的某个时刻,你必须列出所有这些列。 :(问题是,最优雅的方法是什么?下面是我的尝试。它看起来很笨,因为有很多列,但它可能是你所追求的,或者至少它可能会指向你正确的方向。



    可能的SQL解决方案




    1. / if you have N countries /
      CREATE TABLE Country
      (id int,
      name varchar(50))

    2. INSERT Country
      SELECT 1, Afghanistan
      UNION SELECT 2, Albania’,
      UNION SELECT 3, Algeria ,
      UNION SELECT 4, American Samoa ,
      UNION SELECT 5, Andorra ,
      UNION SELECT 6, Angola ,

      UNION SELECT N-3, Western Sahara’,
      UNION SELECT N-2, Yemen’,
      UNION SELECT N-1, Zambia’,
      UNION SELECT N, Zimbabwe’,

    3. CREATE TABLE #tmp
      (key varchar(N),
      country_id int)
      / key field needs to be as long as N /

    4. INSERT #tmp
      SELECT 1__ _’, Afghanistan
      / 1 followed by underscores to make the length = N /

    5. UNION SELECT 1__ _’, Albania
      UNION SELECT
      1__ ‘, Algeria

      UNION SELECT _
      1‘, Zambia
      UNION SELECT __ __1’, Zimbabwe

    6. CREATE TABLE new_table
      (country_id int,
      species_id int)

    7. INSERT new_table
      SELECT species.id, country_id
      FROM species s ,

    8.    #tmp    t
    9. WHERE isnull( s.Afghanistan, ) +
      isnull( s.Albania, ) +
      +
      isnull( s.Zambia, ) +
      isnull( s.Zimbabwe, ) like t.key

    10. </code>


    我的建议



    就个人而言,我不会这样做。我会做一个快速而肮脏的解决方案,就像你提到的解决方案一样,除了我会硬编码国家ID(因为你只会这样做一次,对吧?你可以在创建之后立即执行此操作)国家表,所以你知道所有的ID是什么):




    1. INSERT new_table SELECT Species.ID, 1 FROM Species WHERE Species.Afghanistan = 1
      INSERT new_table SELECT Species.ID, 2 FROM Species WHERE Species.Albania= 1

      INSERT new_table SELECT Species.ID, 999 FROM Species WHERE Species.Zambia= 1
      INSERT new_table SELECT Species.ID, 1000 FROM Species WHERE Species.Zimbabwe= 1

    2. </code>

  16. 15# 12345 | 2019-08-31 10-32



    我会使用Union查询,非常粗略:




    1. Dim db As Database
      Dim tdf As TableDef

    2. Set db = CurrentDb

    3. Set tdf = db.TableDefs(“SO”)

    4. strSQL = SELECT ID, Species, “”” & tdf.Fields(2).Name _
      & “”” AS Country, [“ & tdf.Fields(2).Name & “] AS CountryValue FROM SO

    5. For i = 3 To tdf.Fields.Count - 1
      strSQL = strSQL & vbCrLf & UNION SELECT ID, Species, “”” & tdf.Fields(i).Name _
      & “”” AS Country, [“ & tdf.Fields(i).Name & “] AS CountryValue FROM SO
      Next

    6. db.CreateQueryDef UnionSO”, strSQL

    7. </code>


    然后,您将拥有一个可以附加到新设计的视图。


  17. 16# 哦哦了 | 2019-08-31 10-32



    @stomp



    在您键入答案的框上方,有几个按钮。 101010是代码示例。您选择所有代码文本,然后单击该按钮。然后它不会搞砸太多。




    1. cout>>”I dont know C
      cout>>”Hello World

    2. </code>

登录 后才能参与评论