补坏坏的数据库设计一旦数据的系统
-
09-06-2019 - |
题
我知道,这不是一个问题...呃无论如何这是个问题。
我继承了一个数据库,有1(一)表在这看起来就像这个。其目的是记录是什么品种在各种(200奇怪的)国家。
ID
Species
Afghanistan
Albania
Algeria
American Samoa
Andorra
Angola
....
Western Sahara
Yemen
Zambia
Zimbabwe
一个样本数据就是像这样的东西
id Species Afghanistan Albania American Samoa
1 SP1 null null null
2 SP2 1 1 null
3 SP3 null null 1
在我看来这是一个典型的许多许多情况下,我想3的表格。物种、国家和SpeciesFoundInCountry
链接表(SpeciesFoundInCountry)将外键,在这两个物种和国家表。
(这是很难画的图!)
Species
SpeciesID SpeciesName
Country
CountryID CountryName
SpeciesFoundInCountry
CountryID SpeciesID
是否有一个魔术我可以产生的一个插入语句,将得到了从新的国家表的基础上的列名和SpeciesID有1在原来的大型的表?
我可以做它为一个国家(这是一个选择,以显示我想要什么了)
SELECT Species.ID, Country.CountryID
FROM Country, Species
WHERE (((Species.Afghanistan)=1)) AND (((Country.Country)="Afghanistan"));
(兆表被称为种类)
但使用这种策略,我需要做的查询每一列在原表。
是有这样做的一种方法在sql?
我想我可以载我在那里的条款在一起,并编写脚本,使sql,似乎不优雅的,但!
任何思想(或澄清要求的)?
解决方案
我会用一个脚本生成的所有个人查询,因为这是一个关闭的进程。
一些程序,例如Excel是在混合的不同方面的数据(比较的列名数据的内部行),但是关系数据库的数据很少。
但是,你可能会发现一些系统(例如,Microsoft访问,令人吃惊的是)有方便的工具,可以使用正常化的数据。我个人会发现它更快地编写脚本,但你的相对能力与获取和脚本可能不同于我的。
其他提示
为什么你想要做SQL?只写了一个小小的脚本的转换。
当我遇到这些我写的脚本来做转换而不是试图做到这一点在SQL。这是通常更快和更容易对我来说。选择任何语言,你是舒服。
你可能会想要创造替代表。脚本排序取决于脚本语言提供给你,但你应该能够创建的国家ID表简单地通过清单所列的表格你现在有。一旦你做了,你可以做一些串替代通过的所有的独特的国家的名字,并插入speciesFoundInCountry表在给定国家列不是空。
你很可能获得聪明和查询系统表的列名,然后建立一个动态查询串到执行,但是说实话这可能会更丑比快脚本生成的SQL报表。
希望你没有太多动态SQL代码的访问旧表埋在你的代码。那可能是 真的 困难的部分。
在SQL Server这将生成定制选择你证明。你可以推断,插入
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'
与其他人我会很可能只是做它作为一个时间了速战速决,不论以何种方式对你的作品。
与这些类型的转换,他们是一个关闭的项目,快速修复,以及代码没有被优雅的,它只是有工作。对于这些类型的事情我做了很多种方法。
如果这是SQL服务器,可以使用sys.列表查找所有的列的原始表格。然后你就可以使用动态SQL和枢轴命令去做你想要什么。看看这些了在线语法。
我肯定会同意你的建议编写一个小脚本生产SQL有查询每一个柱。
事实上你的剧本可能已经完成的时间,你已经花了思考有关这个神奇的查询(即你将只使用一个时间,然后扔掉,所以有什么用它的所有magicy和完美)
对不起,但是血腥的张贴分析器拆除的空白和格式在我的职务。它使一个记录难以阅读。
@跺:
上述的框类型的回答,有几个按钮。一个是101010是一种代码样本。你选择了你所有的案文是代码,然后点击按钮。然后它没有得到搞砸了多。
cout>>"I don't know C"
cout>>"Hello World"
我会用一个联合查询,很大致为:
Dim db As Database
Dim tdf As TableDef
Set db = CurrentDb
Set tdf = db.TableDefs("SO")
strSQL = "SELECT ID, Species, """ & tdf.Fields(2).Name _
& """ AS Country, [" & tdf.Fields(2).Name & "] AS CountryValue FROM SO "
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
db.CreateQueryDef "UnionSO", strSQL
你会再有一个认为,可以附加于你新的设计。
当我阅读标题'的坏坏的数据库的设计,我很好奇,想找出这是多么糟糕。你没有让我失望:)
正如其他人所述,一个脚本将是最简单的方法。这可以通过编写大约15行代码在PHP.
SELECT * FROM ugly_table;
while(row)
foreach(row as field => value)
if(value == 1)
SELECT country_id from country_table WHERE country_name = field;
if(field == 'Species')
SELECT species_id from species_table WHERE species_name = value;
INSERT INTO better_table (...)
很明显,这是伪代码并不会的工作,因为它是。你也可以填充的国家和种表在飞行中加入语句插在这里。
对不起,我已经做了很少的访问的编程,但我可以提供一些指导,它应该有所帮助。
首先让我们步行通过的问题。假设你会通常需要产生多行SpeciesFoundInCountry中的每一行的原始表格。换句话说物种往往是在一个以上的国家。这实际上是很容易做到的有笛卡尔的产品,与没有加入标准。
做一个笛卡尔的产品则需要创建国家表。该表应有country_id从1到N(N号的独特的国家、200或使)和国家的名称。让生活轻松只是使用数字1到N列顺序。这将使阿富汗1和阿尔巴尼亚的2...N.津巴布韦你应该能够使用的系统表做到这一点。
接下来,创建一个表格或图从原来的表,其中包含的种类和刺用0或1对每个国家。你会需要转换null,"not null"一文本0或1和连接所有的价值观纳入一个单一串。一个说明表和一个文本编辑器与常规的表情应该让这个容易的。实验的第一个单列和一旦工作编辑创建图/插入与所有列。
下一个加入两个表一起,与没有加入标准。这会给你一个记录每个物种在每一个国家,你们几乎没有。
现在,你所需要做的就是过滤出来的记录是无效的,他们将有一个零,在相应的位置。由于该国表的country_code列有substring位置的所有您需要做的只是筛选出记录,它是0.
where substring(new_column,country_code) = '1'
你将仍然需要创建一种表和加入,
where a.species_name = b.species_name
a和b都表的别名。
希望这个帮助
OBTW,
如果你有的查询已经运行针对老表,你将需要创建一个图,这次重复老表,使用新表格。你需要做一组由非规范化的表格。
告诉你的用户,老表/图将不支持在未来的所有新的查询或更新旧的查询将使用新表格。
如果我必须创建一卡车的类似SQL的发言和执行所有这些,我经常发现Excel是非常方便。把你原来的查询。如果你有一个国家名单列一个和你SQL声明中列B、格式化作为文本(报价)的细胞引用插那里的国家出现在sql
例如="INSERT INTO new_table选择...(种类。" &A1&")=...));"
然后只是复制式下创建200种不同的SQL声明、复制、粘贴列为你的编辑打F5。你当然可以这样做因为许多变量,如要你想。
当我一直面临着类似的问题,我已经找到了方便产生一个脚本生成SQL脚本。这样你给抽象使用%PAR1%,在地方的阿富汗。
SELECT Species.ID, Country.CountryID
FROM Country, Species
WHERE (((Species.%PAR1%)=1)) AND (((Country.Country)="%PAR1%"))
UNION
还关键词盟已经加入作为一种方式,结合所有的选择。
接下来,你需要一个名单的国家,从产生的现有数据:
阿富汗 阿尔巴尼亚 ., .
下次你需要一个剧本,可以通过循环访问的国家名单,并且为每次迭代, 产生输出,替代阿富汗%PAR1%上第一次迭代,阿尔巴尼亚的第二次迭代。的算法是,就像邮件合并在文字处理器。这是一个小的工作编写的这个剧本。但是,一旦你有了它,你可以用它在几十个像这样的项目之一。
最后,你需要手动改变的最后的"联盟"回到一分号。
如果你可以获得执行这个巨大的联盟,可以得到你想要的数据的形式要你想,并插入新的表格。
我会让它三个步骤的过程,与一个微小的临时修改,以你的SpeciesFoundInCountry表。我想添加一栏,表以储存的国家的名称。然后的步骤如下。
1)创建/运行一个脚本走列在来源表,并创建一个记录在SpeciesFoundInCountry每一列有一个真正的价值。这一记录将包含国家的名称。2)Run a SQL的发言,更新的SpeciesFoundInCountry.了场通过加入该国表国家的名称。3)清理SpeciesFoundInCountry表通过删除CountryName列。
这里是一个小MS Access VB/VBA伪码给你的要点
Public Sub CreateRelationshipRecords()
Dim rstSource as DAO.Recordset
Dim rstDestination as DAO.Recordset
Dim fld as DAO.Field
dim strSQL as String
Dim lngSpeciesID as Long
strSQL = "SELECT * FROM [ORIGINALTABLE]"
Set rstSource = CurrentDB.OpenRecordset(strSQL)
set rstDestination = CurrentDB.OpenRecordset("SpeciesFoundInCountry")
rstSource.MoveFirst
' 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
' Clean up
rstSource.Close
Set rstSource = nothing
....
End Sub
在此之后,你可以跑一个简单的SQL声明更新了值SpeciesFoundInCountry表。
更新SpeciesFoundInCountry内加入的国家SpeciesFoundInCountry.CountryName=的国家。CountryName组SpeciesFoundInCountry.了=的国家。了;
最后,你所要做的就是清理SpeciesFoundInCountry表通过删除CountryName列。
****侧注意:我已经找到了有用的,有国家表,也包括ISO缩写(国家代码)。有时它们被用来作为外键的其他表中,以便加入到国家表没有被包括在查询。
这是(希望)的一个运动,因此,一个不优雅的解决方案可能不是那么糟糕,因为它的声音。
问题(如,我敢肯定你只是太知道!) 是的,在某些时候在查询你要列出所有这些列。:(现在的问题是,什么是最优雅的方式做到这一点?下面是我的尝试。它看上去笨拙,因为有这么多柱,但它可能是什么你之后,或至少它可能意你在正确的方向。
可能SQL解决方案:
/* if you have N countries */
CREATE TABLE Country
(id int,
name varchar(50))
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',
CREATE TABLE #tmp
(key varchar(N),
country_id int)
/* "key" field needs to be as long as N */
INSERT #tmp
SELECT '1________ ... _', 'Afghanistan'
/* '1' followed by underscores to make the length = N */
UNION SELECT '_1_______ ... ___', 'Albania'
UNION SELECT '__1______ ... ___', 'Algeria'
...
UNION SELECT '________ ... _1_', 'Zambia'
UNION SELECT '________ ... __1', 'Zimbabwe'
CREATE TABLE new_table
(country_id int,
species_id int)
INSERT new_table
SELECT species.id, country_id
FROM species s ,
#tmp t
WHERE isnull( s.Afghanistan, ' ' ) +
isnull( s.Albania, ' ' ) +
... +
isnull( s.Zambia, ' ' ) +
isnull( s.Zimbabwe, ' ' ) like t.key
我的建议
就个人而言,我不会这么做。我会做一个快速解决方案一样,其暗示,除了我的硬代码的国家标识(因为你只要做到这一次,对吗?你可以做到这一点之后,你创建的国家表,所以你知道什么所有的Id):
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