SQL Server 2008 - 如果不是EXISTS INSERT ELSE UPDATE

tmh*_*ert 67 sql sql-server-2008

我道歉,但这是一个两部分问题.

我对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
Run Code Online (Sandbox Code Playgroud)

我正在使用Visual Studio 2010在本地计算机上连接到SQL Server Express 2008.我收到一条错误消息,指出"不支持复合语句SQL结构或语句." 然而,接下来是一行已经受到影响的消息,当我查看我的Clock表时,它看起来就像我期望的那样.如何解释这个问题最好的方法是什么?

我在这个问题的第二部分是在我的WHERE语句中.是否有函数可以在clockDate列中获取今天的日期而不必填充今天的日期?只是想提前构建前端应用程序.

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

再一次,这给了我想要的结果,但直到收到错误"错误在'CURRENT_DATE'附近的WHERE子句中.无法解析查询文本."

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

编辑:

@RThomas @ w00te

好的,所以将clockDate作为日期字段,将breakOut作为时间(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
Run Code Online (Sandbox Code Playgroud)

我的表结果是:

clockDate  userName  clockIn  breakOut  breakIn  clockOut

08/10/2012  test      NULL    11:24:38   NULL     NULL
Run Code Online (Sandbox Code Playgroud)

这是我想要的结果,但这个错误让我困惑.这是Visual Studio错误还是SQL错误?我将阅读合并声明,感谢您的链接.

RTh*_*mas 62

乍一看,你原来的尝试似乎非常接近.我假设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 
Run Code Online (Sandbox Code Playgroud)

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


如果clockDate不是datetime字段(只是日期),那么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 
Run Code Online (Sandbox Code Playgroud)

正如其他人所指出的那样,合并声明是解决这一逻辑的另一种方式.但是,在某些情况下,特别是对于大型数据集,merge语句可能会非常慢,从而导致大量的tran日志活动.因此,如上所示知道如何逻辑它仍然是一种有效的技术.


Rob*_*nik 34

正如其他人建议您应该查看MERGE语句但没有人提供使用它的解决方案我正在使用这个特定的TSQL构造添加我自己的答案.我打赌你会喜欢它.

重要的提示

您的代码在您的if陈述中有not exists(select...)部分拼写错误.内部select语句只有一个where条件,而UserName条件not exists由于无效的大括号完成而被排除.在任何情况下,你都会躲过太多关闭括号.

我假设这是基于您在代码中稍后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());
Run Code Online (Sandbox Code Playgroud)

  • 我有一些保留找到美丽的MERGE. (2认同)

Joh*_*0te 5

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

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

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

此外, GETDATE() 会将当前日期放在列中,但如果您不想要时间,则必须稍微玩一下。我认为 CONVERT(varchar(8), GETDATE(), 112) 只会给你日期(不是时间)部分。

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

应该这样做。

PS:使用合并语句:)