SQLServer2008-IF 不存在

抱歉,但这个问题有两部分。

我对 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。我得到一个错误说“不支持复合语句 SQL 结构或语句”然而,接下来的消息是1行受到影响,当我查看我的时钟表时,它看起来就像我期望的那样。实现这一目标的最佳方法是什么?

这个问题的第二部分在 WHERE 语句中。是否有一个函数来获取今天的日期,而不是填充今天的日期?只是试着提前考虑构建前端应用程序。

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

同样,这会给我我想要的结果,但不是直到得到一个错误“ Error in WHERE 子句靠近‘ CURRENT _ DATE’。无法解析查询文本。”

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

编辑:

@ RThomas @ w00te

好的,那么使用 lockDate 作为日期字段,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

我的表格结果是:

clockDate  userName  clockIn  breakOut  breakIn  clockOut


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

这是我想要的结果,但是这个错误使我困惑。这是 VisualStudio 错误还是 SQL 错误?我会仔细阅读合并声明,谢谢你们的链接。

491376 次浏览
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: 使用 合并语句:)

你需要把它替换成 WHERE clockDate = { fn CURRENT_DATE() } AND userName = 'test'。 请从 { fn CURRENT_DATE() })移除额外的 ")"

乍一看,你最初的尝试似乎相当接近。我假设时钟日期是一个日期时间字段,所以试试这个:

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 给出了当前日期。如果您试图比较一个日期(没有时间) ,您需要强制转换,否则 time 元素将导致比较失败。


如果 lockDate 不是 datetime 字段(只是 date) ,那么 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 日志活动。因此,如上所示,知道如何将其逻辑化仍然是一种有效的技术。

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

重要提示

您的代码在 not exists(select...)部分的 if语句中有一个输入错误。内部 select语句只有一个 where条件,而 UserName 条件由于无效的大括号完成被排除在 not exists之外。在任何情况下,你都会屈服于太多的闭括号。

我假设这是基于这样一个事实,即稍后在代码中的 update语句中将使用两个 where条件。

让我们继续回答我的问题。

SQLServer2008 + 支持 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());