【问题标题】:SQL stored procedures design issueSQL存储过程设计问题
【发布时间】:2009-06-09 14:48:21
【问题描述】:

我最近看到一个数据库,其中有一个表 Types,列有 IdKeyName

Id 只是类型的 Id,Key 是类型的短键名称,例如“啤酒”,Name 是可以为用户显示的文本(例如, “我们最棒的啤酒”)。 Id 当然是唯一的,并且是该表的主键。 Key 也是独一无二的。其他表始终使用其 Id 列与表类型链接,但存储过程始终使用 Key 进行过滤(例如 "X inner join Types on X.type_Id = Types.Id where Types.Key = 'beer' " 而不是 "X.type_Id = 3")。

我认为这是一种不好的方法。我会使用Id 而不是Key,即使我知道Key 是独一无二的。我认为 Key 可能(并且可以)更改,但 Id 不应该更改,因为它在另一个表中用于链接。有什么不这样做的规则吗?我的意思是如果我们将Key“beer”更改为“beers”,一些存储过程将停止正常工作(实际上确实存在这种情况)。对我来说,如果Id 标识表中的行,我们应该始终使用 id,因为如果需要其他属性可能会更改并且不会导致问题,这对我来说是非常直观的。我说的对吗?

【问题讨论】:

  • 面临同样的问题,但阅读这些答案后,我更加困惑。

标签: sql database-design primary-key


【解决方案1】:

Key 是访问表中数据的一种更有意义且更易于理解的方式。让我这样说:你宁愿调试这个

SELECT ColumnA, ColumnB
FROM Table T
INNER JOIN Keys K
ON T.KeyId = K.KeyId
WHERE K.Key = 'Beer'

或者

SELECT ColumnA, ColumnB
FROM Table T
WHERE T.KeyId = 103461

当您不知道“103461”代表什么时?

存储过程和其他参数化查询也是如此。你想看看

EXEC get_items_by_category 'Beer'

或者

EXEC get_items_by_category 103461

?答案应该很明显。好的、可维护的代码是自我记录的,任意的 ID 不能给你。

【讨论】:

  • cmets 在 sql 中非常有用... "T.KeyID = 103461 --Beer"
  • 但是当我们因为拼写错误而更改了某个键时,所有存储过程都停止了工作!您是否认为在存储过程中使用人们的姓氏(知道他们必须是唯一的)进行操作,只是为了使其更具可读性?如果因为拼写错误而更改某人的姓氏会发生什么?这是一个类似的情况
  • 但是自然键会随着时间的推移而变化,对维护数据完整性非常不利。我几乎不会选择自然键而不是代理加入,但是 30 多年来我使用了数百个数据库,我确切地知道自然键会造成什么混乱。
  • 我没有加入任何自然键,如您所见。从维护的角度来看,使用自然键比使用任意键更有意义。如果您有一个已知的唯一约束,并且您知道您的自然键不会经常更改,请使用自然键进行查找。如果你的自然键是善变的,那么我认为它们根本就不是键。
【解决方案2】:

只有一个(单字段或多字段)主键,在进行 JOINS 时必须始终使用它。无论如何,在特定域(您没有告诉我们)中,在特定查询中搜索另一个字段可能是有意义的。如果如您所说,这些字段可以更改,那么硬编码此查询是不好的做法。

【讨论】:

  • +1! tekBlues 识别出该方法的“坏处”,将值硬编码到存储过程中,无论是 id=3 还是 key='beer',当这些值可以更改时。 (主键应该是不可变的,即一旦分配,就不能改变。)
【解决方案3】:

将 ID 值视为保存某个变量值的内存地址。您希望通过变量名还是内存地址来引用值?

就我个人而言,如果它是自动递增的,我永远不会依赖特定的 ID。特别是如果可能会删除一行。如果有一天您转储数据并再次导入它会怎样,可能是因为您想重新安装。 SQL 服务器将重新枚举 ID,您的所有查询都会中断。

编辑:(回答您的评论)

所以您的主要论点是,在您的场景中,ID 永远不会改变,而密钥可以改变?我会说这是糟糕的设计:)。如果你不得不忍受它,使用 ID 似乎很明显,即使它是非描述性的。恕我直言,在这种情况下,如果允许更改键并破坏大量查询,则该键没有任何价值。

【讨论】:

  • 好的,但问题是更经常发生的情况 - 清除数据库并再次插入数据或拼写错误和数据更改(“beer”到“beers”)?
  • 您从哪里获得价值“啤酒”?如果您在数据库上有一个闭环,那应该不是问题。将“beer”更新为“beers”,传递给存储过程的可用键列表将发生变化。
  • 你没有仔细阅读。存储过程中有“啤酒”硬编码:“其中 Types.Key = 'beer'”。所以如果“啤酒”发生变化,存储过程就会停止工作
【解决方案4】:

我总体上同意你的观点,但不同意 darasd。我假设 id 列是代理键 - 换句话说,它们仅在数据库内部,从不呈现给用户。我认为这(使用代理键作为绝对唯一标识符)是大多数情况下的首选方法。正如您所说,它允许您更好地处理现实世界的描述性密钥(例如“蜜蜂”)应该是唯一的但实际上事实并非如此的情况。典型的例子是 SSN,它原本是作为唯一标识,但实际上并非如此。

【讨论】:

    【解决方案5】:

    听起来是个糟糕的设计。我不认为这有什么坏处,毕竟一个表可以有多个唯一索引。

    我可以看到 id 是 IDENTITY 代理项,你不能指望某个值存在,所以使用自然键代替,但就像你说的,不能保证它存在,所以你的逻辑取决于它可能会破裂。

    但是,在这种情况下,还有其他设计可能会起作用,例如包含“Beers”的所有 id 的 IsBeer 表或具有 IsBeer 列(和 IsFood 列)的 Flags 表,或类似的东西.同样,您根本不能依赖它们存在,但您至少不必担心列更改会破坏逻辑,因为您将拥有 FK 关系。

    进一步以 welbog 为例,您是否宁愿将应用程序逻辑中的信息结构化地包含在数据库中,例如:

    SELECT ColumnA, ColumnB
    FROM Table T
    INNER JOIN Keys K
        ON T.KeyId = K.KeyId
        WHERE K.IsBeer = 1
    

    【讨论】:

      猜你喜欢
      • 2011-11-07
      • 1970-01-01
      • 2012-04-18
      • 2013-08-14
      • 1970-01-01
      • 2021-05-22
      • 2011-03-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多