数据库设计查找表

时间:2008-09-17 09:42:58

标签: database-design magic-numbers

我目前正在尝试改进遗留数据库的设计,我有以下情况

目前我有一个表SalesLead,我们在其中存储LeadSource。

Create Table SalesLead(
    ....
    LeadSource varchar(20)
    ....
)

主要来源有助于存储在表格中。

Create Table LeadSource (
    LeadSourceId int,   /*the PK*/
    LeadSource varchar(20)
)

所以我只是想从一个到另一个创建一个外键并删除非规范化的列。

所有标准的东西,我希望。

这是我的问题。我似乎无法摆脱这个问题而不是写作

 SELECT * FROM SalesLead Where LeadSource = 'foo'

这完全是明确的我现在必须写

SELECT * FROM SalesLead where FK_LeadSourceID = 1

SELECT * FROM SalesLead 
INNER JOIN LeadSource ON SalesLead.FK_LeadSourceID = LeadSource.LeadSourceId 
where LeadSource.LeadSource = "foo"

如果我们改变了LeadSource字段的内容,会中断。

在我的应用程序中,当我想要改变SalesLead的LeadSource的值时,我不想从1更新到2(例如),因为我不希望开发人员必须记住这些幻数即可。 ids是任意的,应该保持这样。

如何在我的应用代码中删除或否定对它们的依赖?

修改我的解决方案必须支持的语言

  • .NET 2.0 + 3(值得asp.net,vb.net和c#)
  • vba(访问)
  • db(MSSQL 2000)

编辑2.0 加入很好只是'foo'可能会根据请求更改为'foobar'而且我不想通过查询。

7 个答案:

答案 0 :(得分:3)

如果要对表进行反规范化,只需将LeadSource(Varchar)列添加到SalesLead表,而不是使用FK或ID。

另一方面,如果您的语言支持ENUM结构,则应将“幻数”安全地存储在枚举中,以便您可以:

SELECT * FROM SALESLEAD WHERE LeadSouce = (int) EnmLeadSource.Foo; //pseudocode

你的代码将有一个

public enum EnmLeadSource 
{
   Foo = 1,
   Bar = 2
}

如果这会导致您比修复它更麻烦,可以删除一些过度的规范化。但是,请记住,如果您使用VARCHAR字段(因为选择了Magic Number),您必须保持一致性,如果您需要多种语言或文化,以后可能很难本地化。

规范化后的最佳方法似乎是使用Enum结构。它保持代码清洁,您始终可以跨方法和函数传递枚举。 (我在这里假设.NET,但也使用其他语言)

更新:由于您使用的是.NET,如果您通过代码构建查询,则数据库后端“无关紧要”。想象一下这个功能:

public void GiveMeSalesLeadGiven( EnmLeadSource thisLeadSource )
{
  // Construct your string using the value of thisLeadSource 
}

在表格中,您将拥有LeadSource(INT)列。但它有1,2或N的事实对你来说无关紧要。如果您以后需要将foo更改为foobar,则可能意味着:

1)所有“数字1”必须是数字“2”。你必须更新表格。 2)或者您需要Foo现在为数字2和条形码1.您只需更改枚举(但请确保表值保持一致)。

如果使用得当,Enum是一个非常有用的结构。

希望这有帮助。

答案 1 :(得分:2)

您是否考虑过不在LeadSource表中使用人工密钥?然后,您可以在LeadSource中使用SalesLead作为FK,这样可以简化查询,同时保留使用规范值集(LeadSource中的行)的好处。

答案 2 :(得分:1)

您是否考虑过可更新的视图?根据您的数据库服务器和数据库设计的完整性,您将能够创建一个视图,当其值发生更改时,它将更新组成表。

答案 3 :(得分:0)

我真的没有看到你加入背后的问题。

当然,直接通过FK_LeadSourceID询问是错误的,但是使用JOIN似乎是正确的方法,因为我屏蔽了更改ID非常好。例如,如果“foo”在某一天变为3(并且您更新了外键字段),那么您显示的最后一个查询仍将完全相同。

如果要在不更改应用程序中的当前查询的情况下对架构进行更改,则可以使用包含此联接的视图。

或者,如果您担心连接语法不直观,那么总会有子选择......

SELECT * FROM SalesLead where FK_LeadSourceID = 
         (SELECT LeadSourceID from LeadSource WHERE LeadSource = 'foo')

但请记住在LeadSource.LeadSource上保留一个索引 - 至少如果你有很多这些索引存储在表中。

答案 4 :(得分:0)

如果您通过引入新的关系/表来“改进设计”,那么您肯定需要不同的实体。如果是这样,你需要处理它们的语义。

在上一个解决方案中,您只需将LeadSource名称更新为相应SalesLead行中您想要的任何名称即可。如果更新新结构中的名称,则对所有SalesLead行执行此操作。

无法处理这些不同的语义。你必须这样做。为了使表更容易查询,您可以使用已建议的视图,但我希望它们主要用于报告目的或向后兼容性,前提是它们不可更新,因为更新此视图的每个人都不会知道更改的语义

如果您不喜欢加入尝试 SELECT * FROM SalesLead,其中LeadSourceId IN(SELECT Id FROM LeadSource WHERE LeadSource ='foo')

答案 5 :(得分:0)

在典型的应用程序中,将向用户显示一个Lead Sources列表(通​​过查询LeadSource表返回),后续的SalesLead查询将由应用程序根据用户的选择动态创建。

您的应用程序似乎有一些“众所周知”的潜在客户来源,您需要为其编写特定查询。如果是这种情况,则在LeadSource表中添加第三个(唯一)字段,其中包含可用作应用程序查询基础的不变“名称”。

这会将魔法负担从DB生成的幻数(可能因安装而异)转移到系统定义的魔术名称(由设计修复)。

答案 6 :(得分:0)

这里有一个错误的二分法。

SELECT * FROM SalesLead 
INNER JOIN LeadSource ON SalesLead.FK_LeadSourceID = LeadSource.LeadSourceId 
where LeadSource.LeadSource = "foo"

不会破坏原来的

SELECT * FROM SalesLead Where LeadSource = 'foo'

foo更改为foobar时。此外,如果您正在使用参数化查询(并且您确实应该这样做),则当foo更改为foobar时,您不必更改任何内容。