数据库查找/字典表 - 设计问题 - 硬编码GUID

时间:2011-04-05 18:37:57

标签: database database-design

在重构我的数据库中的瓶颈的复杂存储过程时,我的脑海中出现了以下问题......让我来介绍一下这个主题。假设我们有查找/字典表(它包含GUID作为其他表的外键和人类可读的名称):

CREATE TABLE [dbo].[PlayerStatus](
    [PlayerStatusId] [uniqueidentifier] NOT NULL,
    [PlayerStatusName] [nvarchar](50) NOT NULL,
 CONSTRAINT [PK_PlayerStatus] PRIMARY KEY CLUSTERED 
(
    [PlayerStatusId] ASC
))

并且有一个播放器表:

    CREATE TABLE [dbo].[Player](
[PlayerId] [uniqueidentifier] NOT NULL,
[PlayerStatusId] [uniqueidentifier] NOT NULL,
[PlayerName] [nchar](10) NOT NULL, 
[PlayerSurname] [nchar](10) NOT NULL, 
CONSTRAINT [PK_Player] PRIMARY KEY CLUSTERED  ( [PlayerId] ASC )) ON [PRIMARY]

非常明智。

让我们说在代码中的某个地方有一个巨大的查询来访问大量的表:

SELECT ...
FROM Player JOIN PlayerStatus ON Player.PlayerStatusId = PlayerStatus.PlayerStatusId
.....
WHERE PlayerStatus.PlayerStatusName = 'Active' ....

现在,在我的存储过程中,根据执行计划玩家表在结果集中包含在开头。假设这是一个包含数百万行的非常大的表,播放器 PlayerStatus 之间的散列连接可能非常耗时。通过优化此查询,我可以将其重写为像这样的

SELECT ...
FROM Player .....
WHERE PlayerStatus.PlayerStatusId = '46bb6a12-4cd9-4b6c-84c2-7444f5f45eb6' ....

这正是我在瓶颈程序中所做的。这样,我删除了4个包含不同类型状态的类似查找/字典表。令我惊讶的是,我设法将性能提高了50%,尽管我认为这些表格根本不会影响性能。但那是侧面的情节。我的问题是:你对硬编码的指导有什么看法?

修改

  • 我在PlayerStatus.PlayerStatusId和Player.PlayerId上有PK索引
  • 我对Player.PlayerStatusId:
  • 有FK约束
  

ALTER TABLE [dbo]。[Player] WITH CHECK   添加约束   [FK_Player_PlayerStatus]外国人   KEY([PlayerStatusId])参考   [DBO]。[PlayerStatus]   ([PlayerStatusId])

  • 播放器表约占2mln记录,PlayerStatus表包含约25条记录

3 个答案:

答案 0 :(得分:1)

答案 - 不要硬编码GUID。这导致标准在哪里?在状态表中。怎么指定呢?如果string是不可变的,那么,如果你愿意,可以使用它,我更喜欢逻辑IsActive标志。如果性能不可接受,请重新访问 - 使用我们讨论过的信息

你有外键约束吗?

如果您正在进行内连接并且没有外键约束,则无论列是否被使用,每行都必须匹配(以满足内连接的逻辑)。

如果你对一个唯一的列(显然是PK)有一个外键约束,优化器就知道可以只有一个,它可以消除匹配的需要,因为它知道它会被满足。

Constraints are your friend.

正如另一个答案所示,你还需要一个关于你的状态外键的索引,我还会检查执行计划,看看到底发生了什么。

就GUID硬编码而言,这是不寻常的,因为GUID通常是非常匿名的。

此外,我通常会有一个逻辑列,如状态中的IsActive,因为在某些情况下,您可能有几个逻辑等效的“状态”,例如Status IN('Closed','Locked','Suspended' ,'')=> IsInactive = 1,而only('Locked')=> IsLocked = 1. FWIW,我倾向于不使用单个状态字符串,而是对帐户上的各个状态使用物理标志,然后将这些状态的逻辑组合作为查询条件的逻辑标志。

我重新阅读了您发布的内容,就您的执行计划而言,这将根据表格中的统计信息进行更改。我非常认为100个玩家的计划与100万玩家的计划相同 - 在尝试进行任何过早优化之前一定要检查一下。此外,在测试中,请确保统计信息已更新 - 有时,对于一行有效的计划将会出现一行。

答案 1 :(得分:1)

请记住,GUID列上不建议使用聚簇索引。将聚簇索引转换为常规PK索引并再次运行查询。您可能会注意到差异。

答案 2 :(得分:1)

硬编码GUID或其他数字ID可能看起来不太优雅,但根据我的经验,就性能而言,有时它被证明是非常有益的。

您的示例非常简单,但如果您有一个包含许多联接的更复杂查询,则删除一个联接可以加快查询速度。您的代码中的一个示例是删除与PlayerStatus的连接,并使用Player表中的PlayerStatusID进行过滤,而不是使用PlayerStatus中的PlayerStatusName。


在硬编码GUID / ID时还有两件事需要考虑:

     
  1. GUID / ID通常是表中的PK并由FK引用,因此更改GUID / ID比更改状态名称更难。将示例中的PlayerStatusName从“Active”更改为“In action”将使您使用PlayerStatusName的查询无效。因此,使用GUID / ID可确保查询基于实心列(PK,FK)  
  2. 如果您有多个环境,则在查询中使用GUID / ID几乎不需要纪律。您需要确保所有数据库实例中包含字典的表(即PlayerStatus)中的相关ID是相同的。