我正在建立一个投票系统,每张投票都会在投票表格中获得,其中UserID
和DateTime
投票以及int
1 或 -1 。
我还在表中保留了TotalVotes
的总计,其中包含用户实际投票的项目。这样我就不会经常向SUM
投票表运行查询。
在更新TotalVotes字段时,我的问题是一个利弊问题。关于代码可管理性,在应用程序中添加其他更新方法可以轻松进行故障排除并发现任何潜在问题。但是,如果此应用程序在其用户群中显着增长,则可能会导致从应用程序到数据库的大量额外SQL调用。使用触发器可以保持“在sql系列中的所有内容”,并且应该添加一点性能提升,以及保持代码库中的平凡活动。
我知道在这个具体问题中可以调用过早优化,但由于我还没有构建它,我不妨试着找出更好的方法。
我个人倾向于触发器。请告诉我你的想法/推理。
答案 0 :(得分:3)
另一种选择是在投票表上创建聚合投票的视图作为TotalVotes。 然后索引视图。
SQL Server优化器(我认为只是企业版)的神奇之处在于,当它看到sum(voteColumn)的查询时,它会从相同数据视图的索引中选择该值,这在您考虑时是惊人的您没有直接在查询中引用该视图!
如果您没有企业版,可以查询视图而不是表格的总投票数,然后利用索引。
索引本质上是优化程序知道的数据的非规范化。您可以根据需要创建或删除它们,让优化器弄明白(不需要更改代码)一旦开始自己手工制作的非规范化的路径,您将在未来几年内将其融入您的代码中。
查看Improving performance with indexed views
要使索引视图正常工作,必须满足一些特定条件。以下是基于对数据模型的猜测的示例:
create database indexdemo
go
create table votes(id int identity primary key, ItemToVoteOn int, vote int not null)
go
CREATE VIEW dbo.VoteCount WITH SCHEMABINDING AS
select ItemToVoteOn, SUM(vote) as TotalVotes, COUNT_BIG(*) as CountOfVotes from dbo.votes group by ItemToVoteOn
go
CREATE UNIQUE CLUSTERED INDEX VoteCount_IndexedView ON dbo.VoteCount(itemtovoteon)
go
insert into votes values(1,1)
insert into votes values(1,1)
insert into votes values(2,1)
insert into votes values(2,1)
insert into votes values(2,1)
go
select ItemToVoteOn, SUM(vote) as TotalVotes from dbo.votes group by ItemToVoteOn
此查询(不引用视图或扩展名为索引)会产生此执行计划。请注意,使用了索引。当然会丢弃索引,(并获得插入性能)
最后一句话。在您启动并运行之前,确实知道您是否知道任何类型的非规范化实际上是否有助于整体吞吐量。通过使用索引,您可以创建它们,测量它是否有帮助或受伤,然后根据需要保留或删除它们。这是唯一一种可以安全执行的性能非规范化。
答案 1 :(得分:2)
我建议您构建一个存储过程,同时执行投票插入和总投票更新。然后你的应用程序只需要知道如何记录一个投票,但是当你调用它时,究竟发生了什么的逻辑仍然包含在一个地方(存储过程,而不是一个临时更新查询和一个单独的触发器)
这也意味着如果您想要删除总票数的更新,稍后您需要更改的是通过注释掉更新部分的过程。
答案 2 :(得分:2)
过早的过早优化是将总数保存在表格中,而不是仅根据需要对数据求和。你真的需要对数据进行非规范化以提高性能吗?
如果您不需要对数据进行非规范化,则无需编写触发器。
答案 3 :(得分:1)
我的第一个直觉就是编写一个UDF来执行SUM操作,并使TotalVotes
成为基于该UDF的计算列。
答案 4 :(得分:1)
我已经完成了多年的触发方法,并且总是更开心。因此,正如他们所说,“来吧,水很好。”但是,我通常会在涉及许多表时执行此操作,而不仅仅是一个。
利弊是众所周知的。实现价值是“立即付钱”的决定,您需要在插入上多付一点才能获得更快的读取。当且仅当您想要在5毫秒而不是500毫秒内读取时,这是可行的方法。
PRO:只需一次阅读即可立即获得TotalVotes。
PRO:您不必担心代码路径,使插入的代码更简单。在较大的应用程序上乘以许多表格,这对可维护性来说是一个大问题。
CON:对于每个INSERT,您还需要支付额外的更新费用。在您注意到这一点之前,它需要比大多数人想象的更多插入/秒。
CON:对于许多表,手动编码触发器可能会变得棘手。我推荐一个代码生成器,但正如我写的那个我所知道的那个,这会让我进入自我推销的领域。如果您只有一个表,只需手动编码即可。
CON:为了确保完全正确,不应该从控制台或代码发出UPDATE来修改TotalVotes。这意味着它更复杂。触发器应作为通常不使用的特殊超级用户执行。父表上的第二个触发器在UPDATE上触发并阻止对TotalVotes的更改,除非进行更新的用户是特殊的超级用户。
希望这足以让你做出决定。