查找表有多重要?

时间:2011-01-28 01:46:55

标签: sql mysql database-design

我编写的很多应用程序都使用了查找表,因为这就是我教授的方式(规范化等)。问题是由于这个原因,我所做的查询通常更复杂。它们通常看起来像这样

获取所有仍处于打开状态的帖子

"SELECT * FROM posts WHERE status_id = (SELECT id FROM statuses WHERE name = 'open')"

通常,查找表本身非常短。例如,可能只有3种左右的不同状态。在这种情况下,是否可以通过在应用程序中使用常量来搜索某种类型?像

这样的东西

获取所有仍处于打开状态的帖子

"SELECT * FROM posts WHERE status_id = ".Status::OPEN

或者,如果不是使用外来ID,我将其设置为枚举并查询它?

感谢。

7 个答案:

答案 0 :(得分:29)

如果您只限于MyNonSQL中的小型文件系统,或者您正在考虑SQL和大型数据库,那么答案有所不同。

在实际数据库中,有许多应用程序使用一个数据库,许多用户使用不同的报告工具(不仅仅是应用程序)来访问数据,标准,规范化和开放式体系结构要求非常重要。

尽管有人试图改变“规范化”等的定义以达到目的,但规范化并没有改变。

  • 如果在数据表中重复“打开”和“关闭”,则这是一个简单的规范化错误。如果更改这些值,则可能需要更新数百万行,这是非常有限的设计。这些值通常归一化为Reference或Lookup表。它还节省了空间。 “打开”,“已关闭”等值不再重复。

  • 第二点是易于更改,如果“已关闭”更改为“已过期”,则需要更改一行,这将反映在整个数据库中;而在非标准化文件中,需要更改数百万行。

  • 添加新值只需插入一行即可。

  • 在Open Architecture术语中,Lookup表是一个普通的表。它存在于(标准SQL)目录中;任何报告工具都可以找到它,只要定义了PK :: FK关系,报告工具也可以找到它。

  • Enum仅适用于非SQLS。在SQL中,Enum是一个Lookup表。

  • 下一点涉及密钥的意义。如果密钥对用户毫无意义,那么使用INT或TINYINT或任何合适的东西;逐渐编号;允许“差距”。但是如果密钥对用户有意义,请不要使用无意义的数字,请使用有意义的密钥。男性和女性等的“M”和“F”

    • 现在有些人会进入切线,这是PK的永久性。这是一个单独的观点。是的,当然,总是使用稳定的PK值。 “M”和“F”不太可能改变;如果您使用过{0,1,2,4,5,6},请不要更改它,为什么要这样做。这些价值观被认为是毫无意义的,只有有意义的关键需要改变 。
  • 如果您确实使用了有意义的密钥,请使用简短的字母代码,用户和开发人员都可以轻松理解(并推断出长篇说明)。

  • 由于PK是稳定的,特别是在查找表中,您可以安全地编码:

    WHERE status_id = 'O'

    您不必加入查找表并检查值“打开”。这会丢失代码段中Lookup表的值。

SQL是一种繁琐的语言,尤其是在连接时。但这就是我们所拥有的,所以我们需要接受障碍并处理它。你的示例代码很好。但更简单的形式可以做同样的事情。报告工具会生成:

SELECT  p.*,
         s.name
    FROM posts p, 
         status s
    WHERE p.status_id = s.status_id 
    AND   p.status_id = 'O'

  • 对于银行系统,我们使用有意义的短代码(因为它们是有意义的,我们不会随着季节改变它们,我们只是添加它们),给定一个Lookup表,如(精心挑选,类似于ISO国家代码):

    Eq   Equity
    EqCS Equity/Common Share
    O    Over The Counter
    OF   OTC/Future

    这样的代码很常见:

    WHERE InstrumentTypeCode LIKE "Eq%"

并且用户将从显示“打开”,“已关闭”等的下拉列表中选择值,而不是{0,1,2,4,5,6},而不是{M,F,U }。在应用程序和报表工具中都有。如果没有查找表,则无法执行此操作。

最后,如果数据库很大,并支持BI或DSS或OLAP函数(高度规范化的数据库),那么Lookup表实际上是Dimension-Fact分析中的Dimension或Vector。如果它不存在,则必须添加,以满足该软件的要求,然后才能进行此类分析。

答案 1 :(得分:1)

对于查找表,我使用一个敏感的主键 - 通常只是一个CHAR(1),在域中有一个额外的Title(VARCHAR)字段。这可以保持关系执行,同时“保持SQL简单”。要记住的关键是查找表不是“包含数据”。它包含身份。其他一些身份可能是时区名称或已分配IOC country codes

例如性别:

ID Label
M  Male
F  Female
N  Neutral
select * from people where gender = 'M'

或者,可以使用ORM并且可能永远不必执行手动SQL生成 - 在这种情况下,标准的“int”代理键方法很好,因为其他事情处理它: - )

快乐的编码。

答案 2 :(得分:1)

为每个查找创建一个函数。 没有简单的方法。您希望性能和查询简单。确保维护以下内容。您可以创建一个SP_TestAppEnums来比较现有的查找值和函数,并查找不同步/返回的零。

CREATE FUNCTION [Enum_Post](@postname varchar(10))
RETURNS int
AS
BEGIN
DECLARE @postId int
SET @postId =
CASE @postname
WHEN 'Open' THEN 1
WHEN 'Closed' THEN 2
END

RETURN @postId
END
GO

/* Calling the function */
SELECT dbo.Enum_Post('Open')
SELECT dbo.Enum_Post('Closed')

答案 3 :(得分:1)

问题是:您是否需要在查询中包含查找表(域名表,在我的脖子上)?据推测,这些类型的表通常是

  • 非常静态 - 域可能会被扩展,但它可能不会缩短。
  • 他们的主键值也不太可能发生变化(例如,状态为“打开”的status_id不太可能突然变为不同于其创建的状态)。

如果上述假设是正确的,那么就没有必要将所有这些额外的表添加到您的联接中,因此您的where子句可以使用朋友名称而不是id值。只需在您需要的地方直接过滤status_id。我怀疑where子句中的非键属性(上面示例中的名称')比键属性('名称')更容易获得更改上面的示例):您通过引用联接中域表的所需键值来更加受到保护。

域表服务

  • 通过外键关系限制变量的域,
  • 允许通过向域表添加数据来扩展域,
  • 使用用户友好的信息填充UI控件等

当然,您需要将域表吮吸到您的查询中,您实际需要域表中的非键属性(例如,值的描述性名称)。

YMMV:很大程度上取决于背景和问题空间的性质。

答案 4 :(得分:0)

评论者让我相信我的方式错误。然而,这个答案以及随之而来的讨论仍然可供参考。

我认为常量是合适的,而数据库表则不合适。在设计应用程序时,您希望状态表永远不会发生变化,因为您的应用程序已经硬编码了这些状态的含义。数据库的重点是中的数据会发生变化。有些情况下线条模糊(例如“这个数据可能每隔几个月左右就会发生变化......”),但这不是模糊情况之一。

状态是应用程序逻辑的一部分;使用常量在应用程序中定义它们。它不仅以更严格的方式组织,而且还可以使您的数据库交互更加快速。

答案 5 :(得分:0)

在可能的情况下(并且总是......),我使用这个经验法则:如果我需要将值硬编码到我的应用程序中(相比之下,它仍然是数据库中的记录),还有将vlue存储在我的数据库中,然后我的设计出了问题。它并非总是如此,但基本上,无论问题的价值是什么,它要么代表一块数据,要么代表一个程序逻辑。这是一种罕见的情况。

并不是说你不会发现自己在项目的中途发现了哪一个。但正如其他人所说,无论如何都可以进行权衡。就像我们并不总是在数据库设计中实现“完美”的规范化一样(出于性能原因,或者仅仅因为你可以在追求完美的时候采取太多措施......),我们可能会对我们的位置做出一些有意义的选择。找到我们的“查找”值。

但就个人而言,我试图遵守上面的规则。它既可以是DATA,也可以是PROGRAM LOGIC,很少都是。如果它最终作为(或IN)数据库中的记录,我会尝试将其保留在应用程序代码之外(当然,除了从数据库中检索它...)。如果它在我的应用程序中是硬编码的,我会尝试将其保留在我的数据库之外。

在我无法遵守这条规则的情况下,我用我的推理来记录代码,所以三年后,如果发生这种情况,一些可怜的灵魂将能够修复它的破坏方式。

答案 6 :(得分:-1)

答案是“无论有意义”。

查找表涉及并不总是有效的连接或子查询。我很多地使用枚举来完成这项工作。高效快捷