投票系统:我应该使用 SQL 触发器还是更多代码?

发布于 2024-10-13 05:17:11 字数 530 浏览 3 评论 0原文

我正在构建一个投票系统,其中每张投票都被捕获在投票表中,其中包含投票的 UserIDDateTime 以及 int 1-1

我还在表中保留了 TotalVotes 的运行总数,其中包含用户实际投票的项目。这样我就不会不断地运行对 Vote 表进行 SUM 的查询。

我的问题是更新 TotalVotes 字段时的优点/缺点问题。就代码可管理性而言,在应用程序中添加额外的更新方法可以轻松排除故障并发现任何潜在问题。但是,如果该应用程序的用户群显着增长,则可能会导致从应用程序到数据库的大量额外 SQL 调用。可以这么说,使用触发器将其“全部纳入 sql 系列”,并且应该会增加一点性能提升,并将日常活动排除在代码库之外。

我知道在这个特定问题中可以调用过早优化,但由于我还没有构建它,我不妨尝试立即找出更好的方法。

就我个人而言,我倾向于触发器。请告诉我你的想法/推理。

I'm building a voting system whereby each vote is captured in a votes table with the UserID and the DateTime of the vote along with an int of either 1 or -1.

I'm also keeping a running total of TotalVotes in the table that contains the item that the user actually voted on. This way I'm not constantly running a query to SUM the Vote table.

My Question is kind of a pros/cons question when it comes to updating the TotalVotes field. With regards to code manageability, putting an additional update method in the application makes it easy to troubleshoot and find any potential problems. But if this application grows significantly in it's user base, this could potentially cause a lot of additional SQL calls from the app to the DB. Using a trigger keeps it "all in the sql family" so to speak, and should add a little performance boost, as well as keep a mundane activity out of the code base.

I understand that premature optimization could be called in this specific question, but since I haven't built it yet, I might as well try and figure out the better approach right out of the gate.

Personally I'm leaning towards a trigger. Please give me your thoughts/reasoning.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

反话 2024-10-20 05:17:11

另一种选择是在投票表上创建视图,将投票聚合为 TotalVotes。
然后索引视图。

SQL Server 优化器(我认为仅限企业版)的神奇之处在于,当它看到 sum(voteColumn) 的查询时,它会从相同数据视图的索引中选取该值,当您认为自己是不要在查询中直接引用视图!

如果您没有企业版,您可以查询视图而不是表上的总票数,然后利用索引。

索引本质上是优化器所知道的数据的非规范化。您根据需要创建或删除它们,然后让优化器弄清楚(不需要更改代码)一旦您开始走上自己手工制作的非规范化之路,您将在未来几年将其融入到您的代码中。

查看通过索引视图提高性能

有一些特定的标准必须满足满足使索引视图正常工作的要求。以下是基于对数据模型的猜测的示例:

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

此查询(不引用视图或扩展其索引)会产生此执行计划。请注意使用了索引。当然,删除索引(并获得插入性能)

alt text

最后一句话。在您启动并运行之前,您将知道任何类型的非规范化实际上是否有助于整体吞吐量。通过使用索引,您可以创建它们,衡量它是否有帮助或有害,然后根据需要保留或删除它们。这是唯一一种可以安全执行的非规范化性能。

Another option is to create view on the votes table aggregating the votes as TotalVotes.
Then index the view.

The magic of SQL Server optimizer (enterprise edition only i think) is that when it sees queries of sum(voteColumn) it will pick that value up from the index on the view of the same data, which is amazing when you consider you're not referencing the view directly in your query!

If you don't have the enterprise edition you could query for the total votes on the view rather than the table, and then take advantage of the index.

Indexes are essentially denormalization of your data that the optimizer is aware of. You create or drop them as required, and let the optimizer figure it out (no code changes required) Once you start down the path of your own hand crafted denormalization you'll have that baked into your code for years to come.

Check out Improving performance with indexed views

There are some specific criteria that must be met to get indexed views working. Here's a sample based on a guess of your data model:

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

And this query (which doesn't reference the view or by extension it's index) results in this execution plan. Notice the index is used. Of course drop the index, (and gain insert performance)

alt text

And one last word. Until you're up and running there is really know way that you'll know if any sort of denormalization will in fact help overall throughput. By using indexes you can create them, measure if it helps or hurts, and then keep or drop them as required. It's the only kind of denormalization for performance that is safe to do.

穿越时光隧道 2024-10-20 05:17:11

我建议您构建一个存储过程来插入投票并更新总票数。然后您的应用程序只需要知道如何记录投票,但是当您调用时到底发生了什么的逻辑仍然包含在一个地方(存储过程,而不是临时更新查询和单独的触发器) 。

这也意味着以后如果您想删除对总票数的更新,您只需通过注释掉更新部分来更改程序即可。

I'd suggest you build a Stored Procedure that does both the vote insert and the update on the total votes. Then your application only has to know how to record a vote, but the logic on exactly what is going on when you call that is still contained in one place (the stored procedure, rather then an ad-hoc update query and a seperate trigger).

It also means that later on if you want to remove the update to total votes, all you have to change is the procedure by commenting out the update part.

粉红×色少女 2024-10-20 05:17:11

过早的过早优化是将总计保存在表中,而不是仅根据需要对数据求和。您真的需要对数据进行非规范化以提高性能吗?

如果您不需要对数据进行非规范化,则无需编写触发器。

The premature premature optimization is saving the total in a table instead of just summing the data as needed. Do you really need to denormalize the data for performance?

If you do not need to denormalize the data then you do not need to write a trigger.

写下不归期 2024-10-20 05:17:11

我已经使用触发方法很多年了,并且总是为此感到高兴。所以,正如他们所说,“进来吧,水很好。”然而,当涉及很多表而不仅仅是一张表时,我通常会这样做。

优点/缺点是众所周知的。实现价值是一个“现在付钱给我”的决定,你在插入内容上多付一点钱以获得更快的读取速度。当且仅当您希望在 5 毫秒而不是 500 毫秒内读取时,才采用这种方法。

PRO:只需阅读一次即可立即获得 TotalVotes。

PRO:您不必担心代码路径,进行插入的代码要简单得多。乘以大型应用程序上的许多表,这对于可维护性来说是一件大事。

缺点:对于每个插入,您还需要支付额外的更新费用。在您注意到这一点之前,每秒执行的插入次数比大多数人想象的要多得多。

缺点:对于许多表,手动编码触发器可能会很棘手。我推荐一个代码生成器,但由于我编写了我所知道的唯一一个代码生成器,这会让我进入自我推销的领域。如果您只有一张表,只需手动编码即可。

缺点:为了确保完全正确,不应从控制台或代码发出更新来修改 TotalVotes。这意味着它更加复杂。触发器应作为通常不使用的特殊超级用户执行。父表上的第二个触发器在 UPDATE 时触发,并阻止对 TotalVotes 进行更改,除非进行更新的用户是该特殊的超级用户。

希望这足以让您做出决定。

I've done the trigger method for years and was always happier for it. So, as they say, "come on in, the water's fine." However, I usually do it when there are many many tables involved, not just one.

The pros/cons are well known. Materializing the value is a "pay me now" decision, you pay a little more on the insert to get faster reads. This is the way to go if and only if you want a read in 5 milliseconds instead of 500 milliseconds.

PRO: The TotalVotes will always be instantly available with one read.

PRO: You don't have to worry about code path, the code that makes the insert is much simpler. Multiplied over many tables on larger apps this is a big deal for maintainability.

CON: For each INSERT you also pay with an additional UPDATE. It takes a lot more inserts/second than most people think before you ever notice this.

CON: For many tables, manually coding triggers can get tricky. I recommend a code generator, but as I wrote the only one I know about, that would get me into self-promotion territory. If you have only one table, just code it manually.

CON: To ensure complete correctness, it should not be possible to issue an UPDATE from a console or code to modify TotalVotes. This means it is more complicated. The trigger should execute as a special superuser that is not normally used. A second trigger on the parent table fires on UPDATE and prevents changes to TotalVotes unless the user making the update is that special super-user.

Hope this gives you enough to decide.

水中月 2024-10-20 05:17:11

我的第一直觉是编写一个 UDF 来执行 SUM 操作,并使 TotalVotes 成为基于该 UDF 的计算列。

My first gut instinct would be to write a UDF to perform the SUM operation and make TotalVotes a computed column based on that UDF.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文