我道歉,但这是一个两部分问题。
我对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结构或语句。”然而,接下来是一行已经受到影响的消息,当我查看我的Clock表时,它看起来就像我期望的那样。什么是最好的方法来解释这个?
这个问题的第二部分在我的WHERE语句中。是否有函数可以在clockDate列中获取今天的日期而不必填充今天的日期?只是想着提前构建前端应用程序。
IF NOT EXISTS(SELECT * FROM Clock WHERE clockDate = { fn CURRENT_DATE() }) AND userName = 'test')
再一次,这给了我想要的结果,但是在得到错误之后“在'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
我的表结果是:
clockDate userName clockIn breakOut breakIn clockOut
08/10/2012 test NULL 11:24:38 NULL NULL
这是我想要的结果,但这个错误让我感到困惑。这是Visual Studio错误还是SQL错误?我将阅读合并声明,感谢您的链接。
答案 0 :(得分:60)
乍一看,你最初的尝试似乎非常接近。我假设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不是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
正如其他人所指出的那样,合并声明是解决这一逻辑的另一种方式。但是,在某些情况下,特别是对于大型数据集,merge语句可能会非常慢,从而导致大量的tran日志活动。因此,如上所示知道如何逻辑它仍然是一种有效的技术。
答案 1 :(得分:33)
正如其他人建议您应该查看MERGE语句但没有人提供使用它的解决方案我正在使用这个特定的TSQL构造添加我自己的答案。我打赌你会喜欢它。
您的代码在if
部分的not exists(select...)
声明中有拼写错误。内部select
语句只有一个where
条件,而由于括号完成无效,UserName条件被排除在not exists
之外。在任何情况下,你都会关闭太多关闭括号。
我假设这是基于您在代码中稍后在where
语句中使用两个update
条件的事实。
让我们继续回答......
MERGE statement是一个漂亮的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());
答案 2 :(得分:4)
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:使用merge声明:)
答案 3 :(得分:2)
您需要将其替换为WHERE clockDate = { fn CURRENT_DATE() } AND userName = 'test'
。
请从")"
{ fn CURRENT_DATE() })