我使用存储过程来管理仓库。 PDA扫描仪扫描添加的库存并将其批量(插回时)发送到SQL数据库(SQL Server 2016)。
SQL数据库相当偏远(另一个国家/地区),因此某些查询有时会延迟,但是这一特定查询是有问题的:即使库存表很好,我在更新的占用率时也遇到了一些问题仓库地点。 PDA以SMALLINT的形式跟踪每个位置中添加的项目,然后将该值发送回下面的存储过程。
PDA“ send_spots”查询:
SELECT spot, added_items FROM spots WHERE change=1
存储过程:
CREATE PROCEDURE [dbo].[update_spots]
@spot VARCHAR(10),
@added_items SMALLINT
AS
BEGIN
BEGIN TRAN
UPDATE storage_spots
SET remaining_capacity = remaining_capacity - @added_items
WHERE storage_spot=@spot
IF @@ROWCOUNT <> 1
BEGIN
ROLLBACK TRAN
RETURN - 1
END
ELSE
BEGIN
COMMIT TRAN
RETURN 0
END
END
GO
如果remaining_capacity
的值为0,则PDA在下一轮不能向其添加更多项目。但是在此过程中,我有负值,因为据称该查询运行了两次(因此两次减去@added_items
)。
有没有办法做到这一点?我该如何解决?据我了解,如果受影响的行是!= 1,则应该取消该事务(ROLLBACK),但这也许是其他原因。
编辑:借助于@Zero的当前解决方案:
CREATE PROCEDURE [dbo].[update_spots]
@spot VARCHAR(10),
@added_racks SMALLINT
AS
BEGIN
-- Recover current capacity of the spot
DECLARE @orig_capacity SMALLINT
SELECT TOP 1
@orig_capacity = remaining_capacity
FROM storage_spots
WHERE storage_spot=@spot
-- Test if double is present in logs by comparing dates (last 10 seconds)
DECLARE @is_double BIT = 0
SELECT @is_double = CASE WHEN EXISTS(SELECT *
FROM spot_logs
WHERE log_timestamp >= dateadd(second, -10, getdate()) AND storage_spot=@spot AND delta=@added_racks)
THEN 1 ELSE 0 END
BEGIN
BEGIN TRAN
UPDATE storage_spots
SET remaining_capacity= @orig_capacity - @added_racks
WHERE storage_spot=@spot
IF @@ROWCOUNT <> 1 OR @is_double <> 0
-- If double, rollback UPDATE
ROLLBACK TRAN
ELSE
-- If no double, commit UPDATE
COMMIT TRAN
-- write log
INSERT INTO spot_logs
(storage_spot, former_capacity, new_capacity, delta, log_timestamp, double_detected)
VALUES
(@spot, @orig_capacity, @orig_capacity-@added_racks, @added_racks, getdate(), @is_double)
END
END
GO
答案 0 :(得分:0)
我找到了here的替代SQL查询,该查询按照我需要的方式进行更新,但是使用DECLARE临时值。对于我来说,它会更好吗?还是我的初始查询正确?
初始查询:
UPDATE storage_spots
SET remaining_capacity = remaining_capacity - @added_items
WHERE storage_spot=@spot
替代查询:
DECLARE @orig_capacity SMALLINT
SELECT TOP 1 @orig_capacity = remaining_capacity
FROM storage_spots
WHERE spot=@spot
UPDATE Products
SET remaining_capacity = @orig_capacity - @added_items
WHERE spot=@spot
此外,我应该摆脱ROLLBACK / COMMIT指令吗?
答案 1 :(得分:0)
我正在考虑可能的原因(以及一种追踪原因的方法),然后它就打了我-您没有价值验证!
这是一个简单的例子来说明问题:
Spot | capacity
---------------
x1 | 1
Update spots set capacity = capacity - 2 where spot = 'X1'
您的扫描仪很可能为您提供的数量超出了您的承受能力。 我不确定您的业务逻辑如何发展,但是您需要执行
行Update spots set capacity = capacity - @added_items where spot = 'X1' and capacity >= @added_items
if @@rowcount <> 1;
rollback;
编辑:不执行验证即可跟踪问题的几种方法:
创建一个日志记录表(使用timestamp
,user id
(连接到数据库的用户),session id
,old value
,new value
,delta value
(添加的项目)。
选项1: 记录所有将值从正更改为负的更新(至少直到您找出问题为止)。 此选项的缺点是它不会注册不会导致容量减负的重复调用。
选项2:(记录相同的更新): 创建脚本,该脚本创建一个全局临时表并从该表中删除早于...的时间戳(假设每分钟大约10分钟(使用数字))。
此临时表应保存传递给您的更新语句的数据,以便“ spot”,“ added_items” +“ timestamp”(用于跟踪)。
现在至关重要的部分:当您调用更新语句时,请检查临时表中是否存在类似的记录(相同的位置和add_items,以及当前时间戳为between timestamp and [timestamp + 1 second]
的位置-再次使用数字)。如果存在这样的记录,则该记录会更新,如果没有,则将其添加到临时表中。
这将注册彼此之间在一秒钟内(或您选择的任何时间范围内)的相同更新。