如何防止多个客户端从SQL数据库重复下载

本文关键字:数据库 下载 SQL 何防止 客户端 | 更新日期: 2023-09-27 17:49:49

我正在使用c#开发一个应用程序,其中一部分将从我的SQL数据库下载数据,并发送到打印机。当打印完成时,它会将数据标记为已经打印,所以我不会多次打印相同的数据。

它在一台计算机/打印机上工作得很好,但是如果我在多台客户端计算机上运行该应用程序呢?似乎在一个客户端下载了数据,但在数据被标记为完成之前,另一个客户端会认为它没有下载并下载相同的数据。

有办法防止这种情况吗?我想也许标签的数据被下载时,或确保只有一个查询可以在同一时间执行?

如何防止多个客户端从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状态,以便稍后将其捡起并再次尝试。或者,您可能希望将其标记为不同的错误状态,而不是作为异常过程的一部分进行处理。