在重构我的数据库中的瓶颈的复杂存储过程时,我的脑海中出现了以下问题......让我来介绍一下这个主题。假设我们有查找/字典表(它包含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%,尽管我认为这些表格根本不会影响性能。但那是侧面的情节。我的问题是:你对硬编码的指导有什么看法?
修改
ALTER TABLE [dbo]。[Player] WITH CHECK 添加约束 [FK_Player_PlayerStatus]外国人 KEY([PlayerStatusId])参考 [DBO]。[PlayerStatus] ([PlayerStatusId])
答案 0 :(得分:1)
答案 - 不要硬编码GUID。这导致标准在哪里?在状态表中。怎么指定呢?如果string是不可变的,那么,如果你愿意,可以使用它,我更喜欢逻辑IsActive标志。如果性能不可接受,请重新访问 - 使用我们讨论过的信息
你有外键约束吗?
如果您正在进行内连接并且没有外键约束,则无论列是否被使用,每行都必须匹配(以满足内连接的逻辑)。
如果你对一个唯一的列(显然是PK)有一个外键约束,优化器就知道可以只有一个,它可以消除匹配的需要,因为它知道它会被满足。
正如另一个答案所示,你还需要一个关于你的状态外键的索引,我还会检查执行计划,看看到底发生了什么。
就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时还有两件事需要考虑: