4

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

目前我有一个表 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(例如),因为我不想让开发人员记住这些神奇的数字。id 是任意的,应该保持不变。

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

编辑我的解决方案必须支持的语言

  • .NET 2.0 + 3(对于它的价值 asp.net、vb.net 和 c#)
  • vba(访问)
  • 数据库 (MSSQL 2000)

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

4

7 回答 7

3

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

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

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

你的代码会有一个

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

如果这给您带来的麻烦比它所修复的更多,那么删除一些过度的规范化是可以的。但是,请记住,如果您使用 VARCHAR 字段(与幻数相反),您必须保持一致性,如果您需要多种语言或文化,以后可能很难本地化。

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

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

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 号,Bar 为 1 号。您只需更改 Enum(但确保表值保持一致)。

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

希望这可以帮助。

于 2008-09-17T09:50:57.753 回答
2

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

于 2008-09-17T12:34:54.783 回答
1

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

于 2008-09-17T09:47:57.883 回答
0

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

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

如果您想在不更改应用程序中当前查询的情况下更改架构,那么包含此连接的视图是可行的方法。

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

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

但请记住在 LeadSource.LeadSource 上保留索引 - 至少如果您有很多它们存储在表中。

于 2008-09-17T09:51:17.890 回答
0

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

在之前的解决方案中,您可以在适当的 SalesLead 行中将 LeadSource 名称更新为您想要的任何名称。如果您更新新结构中的名称,您将对所有 SalesLead 行执行此操作。

没有办法处理这些不同的语义。你只需要这样做。为了使表格更易于查询,您可以使用已经建议的视图,但我希望它们主要用于报告目的或向后兼容性,前提是它们不可更新,因为更新此视图的每个人都不会意识到更改的语义.

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

于 2008-09-17T09:58:49.560 回答
0

在典型的应用程序中,用户将看到一个潜在客户来源列表(通过查询 LeadSource 表返回),随后的 SalesLead 查询将由应用程序根据用户的选择动态创建。

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

这将魔术的负担从数据库生成的魔术数字(可能因安装而异)转移到系统定义的魔术名称(由设计固定)。

于 2008-09-17T10:01:22.173 回答
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.

于 2008-09-17T11:29:35.800 回答