SQL Server 2008-如果不存在则插入其他更新


71

我很抱歉,但这是一个分为两个部分的问题。

我对SQL非常陌生,正在尝试为我工作的小型办公室开发一个时钟应用程序。我现在正在使用SQL后端,并对复合语句有疑问。

我遇到的问题是,如果用户尝试暂停休息,但从未在上班开始前一直停下来,则SQL需要创建新行而不是更新现有行。

这是我尝试过的:

    IF NOT EXISTS(SELECT * FROM Clock WHERE clockDate = '08/10/2012') AND userName = 'test')
    BEGIN
        INSERT INTO Clock(clockDate, userName, breakOut)
        VALUES({ fn NOW() }, 'test', { fn NOW() })
    END
    ELSE
    BEGIN
        UPDATE Clock
        SET breakOut = { fn NOW() }
        WHERE (clockDate = '08/10/2012') AND (userName = 'test')
    END

我正在使用Visual Studio 2010将其连接到本地计算机上的SQL Server Express 2008。我收到一条错误消息:“不支持Composite语句SQL构造或语句。” 但是,这之后会出现一条消息,指出1行已受到影响,当我查看Clock表时,它看起来就像我期望的样子。做到这一点的最佳方法是什么?

我的第二部分问题在我的WHERE语句中。是否有一个函数可以在clockDate列中获取今天的日期,而不必填充今天的日期?只是尝试为构建前端应用程序考虑一下。

    IF NOT EXISTS(SELECT * FROM Clock WHERE clockDate = { fn CURRENT_DATE() }) AND userName = 'test')

再一次,这给了我想要的结果,但是直到出现错误“ CURRENT_DATE附近的WHERE子句中的错误。无法解析查询文本”之后,我才得到结果。

我希望我已经正确解释了这一点,并感谢您的帮助!

编辑:

@RThomas @ w00te

好的,因此使用clockDate作为日期字段,将breakOut作为time(0)字段,是否可以正常工作?原因我仍然收到“不支持复合语句SQL构造或语句”。语法错误,即使它似乎正在工作。

    IF NOT EXISTS (SELECT * FROM Clock WHERE (clockDate = GETDATE()) AND (userName = 'test'))
    BEGIN
        INSERT INTO Clock(clockDate, userName, breakOut)
        Values(GETDATE(), 'test', GETDATE())
    END
    ELSE
    BEGIN
        UPDATE Clock
        SET breakOut = GETDATE()
        WHERE (clockDate = GETDATE()) AND (userName = 'test')
    END

我的表结果是:

clockDate  userName  clockIn  breakOut  breakIn  clockOut

08/10/2012  test      NULL    11:24:38   NULL     NULL

这是我想要的结果,但是此错误使我感到困惑。这是Visual Studio错误还是SQL错误?我将阅读合并声明,谢谢你们的链接。


12
查看合并语句。
Mike Christensen 2012年

2
尽管下面有我的回答,但我与其他人一样,关于合并语句是一种不确定插入/更新方案的好方法,当您不确定该值是否已存在时。这是我不久前发布的merge语句的演练。 stackoverflow.com/questions/10218101/…如果您想了解更多有关其工作原理的信息。
RThomas 2012年


请注意,在某些情况下,尤其是对于大型数据集,merge语句可能会过慢,从而导致大量的tran日志活动。因此,知道如何对其进行逻辑运算仍然有效。
RThomas

Answers:


63

乍一看,您最初的尝试似乎非常接近。我假设ClockDate是一个DateTime字段,所以请尝试以下操作:

IF (NOT EXISTS(SELECT * FROM Clock WHERE cast(clockDate as date) = '08/10/2012') 
    AND userName = 'test') 
BEGIN 
    INSERT INTO Clock(clockDate, userName, breakOut) 
    VALUES(GetDate(), 'test', GetDate()) 
END 
ELSE 
BEGIN 
    UPDATE Clock 
    SET breakOut = GetDate()
    WHERE Cast(clockDate AS Date) = '08/10/2012' AND userName = 'test'
END 

请注意,getdate为您提供当前日期。如果尝试与日期(无时间)进行比较,则需要强制转换,否则时间元素将导致比较失败。


如果clockDate不是日期时间字段(仅日期),则SQL引擎将为您执行此操作-无需强制转换为set / insert语句。

IF (NOT EXISTS(SELECT * FROM Clock WHERE clockDate = '08/10/2012') 
    AND userName = 'test') 
BEGIN 
    INSERT INTO Clock(clockDate, userName, breakOut) 
    VALUES(GetDate(), 'test', GetDate()) 
END 
ELSE 
BEGIN 
    UPDATE Clock 
    SET breakOut = GetDate()
    WHERE clockDate = '08/10/2012' AND userName = 'test'
END 

正如其他人指出的那样,merge语句是解决此逻辑的另一种方法。但是,在某些情况下,尤其是对于大型数据集,merge语句可能会非常慢,从而导致大量的tran日志活动。因此,知道如何如上所示进行逻辑运算仍然是有效的技术。


clockDate是“日期”数据类型,而不是“日期时间”。抱歉,您不清楚。
tmhalbert

您可以删除演员表声明,然后...我将添加到我的帖子中。
RThomas 2012年

哦,突破是一个“时间(0)”字段
tmhalbert

没关系。 SET breakOut = GetDate()仍将更新为当前时间。请注意,如果要比较值(而不是设置值),则需要像这样首先进行转换where cast(getdate() as time(0)) = breatOut
RThomas 2012年

换句话说。您可以将日期,日期时间和时间设置为getdate()。sql引擎足够聪明,可以为您完成转换工作。只是在比较值时才需要更具体。
RThomas 2012年

37

正如其他人所建议的那样,您应该研究MERGE语句,但是没有人提供使用它的解决方案,我为此特定的TSQL构造添加了自己的答案。我敢打赌你会喜欢的。

重要的提示

您的代码ifnot exists(select...)部分语句中有错别字。内部select语句只有一个where条件,而not exists由于无效括号完成而将UserName条件排除在外。无论如何,您会使太多的右括号掉落。

我基于您whereupdate稍后在代码语句中。

让我们继续我的答案...

SQL Server 2008+支持MERGE语句

MERGE语句是一个非常漂亮的TSQL gem,非常适合“插入或更新”情况。在您的情况下,它看起来类似于以下代码。考虑到我在声明变量,可能是什么存储过程参数(我怀疑)。

declare @clockDate date = '08/10/2012';
declare @userName = 'test';

merge Clock as target
using (select @clockDate, @userName) as source (ClockDate, UserName)
on (target.ClockDate = source.ClockDate and target.UserName = source.UserName)
when matched then
    update
    set BreakOut = getdate()
when not matched then
    insert (ClockDate, UserName, BreakOut)
    values (getdate(), source.UserName, getdate());

4
我有一些储备可以找到美丽的MERGE。
史蒂夫,

除非您尝试在远程表上使用MERGE,否则一切都很好。<face palm>为什么!?!?
广告

5
IF NOT EXISTS(SELECT * FROM Clock
WHERE clockDate = '08/10/2012') AND userName = 'test')

有一个额外的括号。我认为如果删除它就可以了:

IF NOT EXISTS(SELECT * FROM Clock WHERE
clockDate = '08/10/2012' AND userName = 'test')

此外,GETDATE()会将当前日期放在该列中,尽管如果您不希望花费时间,则必须花一点时间。我认为CONVERT(varchar(8),GETDATE(),112)只会给您日期(而不是时间)部分。

IF NOT EXISTS(SELECT * FROM Clock WHERE
clockDate = CONVERT(varchar(8), GETDATE(), 112)
AND userName = 'test')

应该可以做到。

PS:使用合并语句:)


2

您需要将其替换为WHERE clockDate = { fn CURRENT_DATE() } AND userName = 'test'。请")"从中删除其他内容 { fn CURRENT_DATE() })


抱歉,这只是我的错字。我在我的SQL查询窗口中确实有此问题。
tmhalbert
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.