如何防止多个客户端从SQL数据库重复下载
本文关键字:数据库 下载 SQL 何防止 客户端 | 更新日期: 2023-09-27 17:49:49
我正在使用c#开发一个应用程序,其中一部分将从我的SQL数据库下载数据,并发送到打印机。当打印完成时,它会将数据标记为已经打印,所以我不会多次打印相同的数据。
它在一台计算机/打印机上工作得很好,但是如果我在多台客户端计算机上运行该应用程序呢?似乎在一个客户端下载了数据,但在数据被标记为完成之前,另一个客户端会认为它没有下载并下载相同的数据。
有办法防止这种情况吗?我想也许标签的数据被下载时,或确保只有一个查询可以在同一时间执行?
添加一些形式的" PrintStatus "列
- 当添加数据时,设置为(例如)1,"未打印"。
- 客户端查询要打印的数据时,只返回PrintStatus = 1的数据
- 当客户端拿起要打印的数据时,将其设置为(例如)2,"正在打印"
- 客户端打印完成后,将状态设为3,"已打印"
- 如果由于某种原因客户端无法打印数据,将状态设置回1(并将PrintedAt设置回null)
这就留下了看门人问题,也就是如果打印失败而没有报告这个事实(因为看门人拔掉了服务器上的插头)会发生什么。别笑,这事就发生在这里,不过是一个技术维修人员干的,他真应该更清楚。)如果你担心这些事情:
- 添加一些形式的时间记录,例如列"PrintedAt",当PrintStatus设置为1时,将其设置为null,当PrintStatus设置为2时,将其设置为getdate()。附带的好处是:现在您知道何时(或者可能是"哪种打印")打印了数据!
- PrintStatus在打印完成时设置为3。因此,如果PrintStatus = 2,打印(到目前为止)已经占用了getdate() - PrintedAt时间来运行。
- 定期运行一个定期调度的进程(SQL Agent作业适合于此)。它检查表,如果它发现PrintStatus = 2的任何行,其中PrintedAt列大于您的容忍度(1天?2小时?)通过将PrintStatus设置为1并将PrintedAt设置为null,可以将任何此类行"回滚"。
这看起来有点过分,但是你告诉我一致性数据对你有多重要。
补充道:
是的,并发性和一致性是个问题。虽然您可以随意使用BEGIN TRANSACTION ... COMMIT/ROLLBACK
,但我更喜欢利用隐式事务的功能。有一种方法:
下面设置一个包含一些数据的测试表:
-- Set up test data
CREATE TABLE Test
(
Data int not null identity(1,1)
,PrintStatus tinyint not null
,PrintedAt datetime null
)
INSERT Test (PrintStatus) values
(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
SELECT *
from Test
GO
这个位包含一个UPDATE语句,更新所有要打印的行,并使用OUTPUT子句将这些项存储到一个临时表中。然后将该表的内容返回给打印例程。
-- First pass: Get everything to pring
DECLARE @ToBePrinted table
(Data int not null)
UPDATE Test
set
PrintStatus = 2
,PrintedAt = getdate()
output inserted.Data into @ToBePrinted (Data)
where PrintStatus = 1
SELECT Data PrintThese
from @ToBePrinted
SELECT * from Test
GO
在这里,我们添加了三行数据,然后再次运行例程。旧项不被选中,新项被选中
-- Second pass: Add three new items, they (and only they) get selected
INSERT Test (PrintStatus) values
(1),(1),(1)
SELECT *
from Test
DECLARE @ToBePrinted table
(Data int not null)
UPDATE Test
set
PrintStatus = 2
,PrintedAt = getdate()
output inserted.Data into @ToBePrinted (Data)
where PrintStatus = 1
SELECT Data PrintThese
from @ToBePrinted
SELECT * from Test
这是因为SQL Server的ACID属性——特别是I)隔离。一旦一个语句开始修改表的内容,在该语句执行完成之前,其他语句都不能访问被修改的数据。(除非你蠢到使用NOLOCK。)
我认为你的思路是对的。要么将布尔/位IsComplete字段更改为状态字段(值如"New","InProgress"answers"Complete"),要么添加一个名为IsInProgress的新位字段。然后只下载那些尚未完成或未完成的。
如果下载或打印可能会失败,请确保您有逻辑将行设置回New状态,以便稍后将其捡起并再次尝试。或者,您可能希望将其标记为不同的错误状态,而不是作为异常过程的一部分进行处理。