SQL Server:如果列中不存在,则更新值

时间:2015-11-13 10:59:56

标签: sql sql-server sql-server-2008

我正在使用SQL Server 2008并尝试创建一个语句,如果满足某个参数,它将更新来自另一个表的行中的单个值。我需要让我的团队成员尽可能简单地使用它。

所以在这种情况下我想存储2个值,即销售订单和参考。不幸的是,销售订单具有我需要记录并输入作业表的唯一标识符,而不是实际的销售订单参考。

需要满足的参数是,销售订单唯一标识符不能存在于jobs表中的销售订单列中的任何位置。我可以在while值设置为1时将其设置为工作,但是当它设置为0并且在我的头脑中它应该设置为0.任何人都有任何想法为什么这不起作用?< / p>

/****** Attach an SO to a WO  ******/

/****** ONLY EDIT THE VALUES BETWEEN '' ******/
Declare @Reference nvarchar(30);
Set @Reference = 'WO16119';

Declare @SO nvarchar(30);
Set @SO = '0000016205';

/****** DO NOT ALTER THE CODE BEYOND THIS POINT!!!!!!!!!!!!! ******/

/* store more values */
Declare @SOID nvarchar(30);
Set @SOID = (Select SOPOrderReturnID
        FROM Test_DB.dbo.SOTable
        Where DocumentNo = @SO);

/*  check if update should run */                           
Declare @Check nvarchar (30);
Set @Check = (Select case when exists   (select * 
                                from Test_DB.dbo.Jobs 
                                where SalesOrderNumber != @SOID)
                                then CAST(1 AS BIT)
                                ELSE CAST(0 AS BIT) End)
While (@Check = 0)

/* if check is true run code below */
Begin
Update Test_DB.dbo.jobs
SET SalesOrderNumber = (Select SOPOrderReturnID
                    FROM Test_DB.dbo.SOPOrderReturn
                    Where DocumentNo = @SO)
Where Reference = @Reference
END;

1 个答案:

答案 0 :(得分:2)

一些评论。首先,为了避免进入永无止境的循环,您可能希望更改IF语句的时间。您没有更改@check值以便永远运行:

IF (@Check = 0)
BEGIN
   /* if check is true run code below */
   Update Test_DB.dbo.jobs
   SET SalesOrderNumber = (Select SOPOrderReturnID
                FROM Test_DB.dbo.SOPOrderReturn
                Where DocumentNo = @SO)
   Where Reference = @Reference
END

然后,在不知道您的应用程序的情况下,我会说您进行检查的方式是要求您锁定表格以避免其他用户使SELECT的结果无效。

我会转而在你想要独特的列上创建一个UNIQUE约束,并优雅地处理错误。这样您就不需要在表上创建大锁了

CREATE UNIQUE INDEX IX_UniqueIndex ON Test_DB.dbo.Jobs(SalesOrderNumber)

根据您的评论,如果您无法创建唯一索引,您可能需要尝试以下SQL,尽管它可能导致过多锁定并受到竞争条件的影响:

IF NOT EXISTS (SELECT 1 FROM Test_DB.dbo.Jobs j INNER JOIN Test_DB.dbo.SOTable so ON j.SalesOrderNumber = so.SPOrderReturnId)
BEGIN
    UPDATE Test_DB.dbo.jobs
    SET SalesOrderNumber =  so.SOPOrderReturnID
    FROM
        Test_DB.dbo.Jobs j 
        INNER JOIN Test_DB.dbo.SOTable so ON j.SalesOrderNumber = so.SPOrderReturnId
    Where 
        Reference = @Reference
END

这样做的风险在于您正在运行单独的查询(选择和更新),因此在它们之间可能会更改数据库的状态。因此,第一个查询可能返回该Id的任何内容,但在更新时,其他用户已插入/更新该数据,因此之前的结果不再为真。

您可以尝试通过使用在读取时锁定表的隔离级别(如Serializable)来避免此问题,但这可能会导致数据库中的锁甚至死锁。

这里最好的解决方案是唯一索引。如果某个列在表中必须是唯一的,那么最好的控制器系统就是db本身,它通过定义约束。