在 SO 上参考这个问题,因为它很相似。
我正在研究一种从数据库中具有日期值的所有表中删除一系列日期的方法(用于构建迷你开发者)。我构建了一个脚本,允许用户传入开始、结束和数据库值,但我遇到了动态 sql 的问题。例如,在删除某些日期之间的记录时,我会遇到操作数冲突。下面显示了一个过度简化的例子:
DECLARE @b DATE, @e DATE, @cmd NVARCHAR(MAX)
SET @b = '2012-07-01'
SET @e = '2014-01-25'
SET @cmd = 'DELETE FROM DateTable
WHERE ValueDate BETWEEN ' + CONVERT(VARCHAR,@b,121) + ' AND ' + CONVERT(VARCHAR,@E,121)
EXEC sp_executesql @cmd
Run Code Online (Sandbox Code Playgroud)
错误是Operand type clash: date is incompatible with int
。我尝试CAST
在动态 sql 中做一个内部,因为错误表明ValueDate
变成和INT
:
-- Only the WHERE clause changed:
WHERE ValueDate BETWEEN CAST(' + CONVERT(VARCHAR,@b,121) + ' AS DATE) AND CAST(' + CONVERT(VARCHAR,@E,121) + ' AS DATE)'
Run Code Online (Sandbox Code Playgroud)
这次的错误是Explicit conversion from data type int to date is not allowed
。最后,我尝试使用其他一些格式(来自 MSDN)。简而言之,我想完全按照 SO 用户尝试完成的操作,但能够跨多个表完成(我什至搜索了一个脚本,因为我怀疑我是第一个想要这个的人)。
Aar*_*and 11
您应该尝试在出现错误时打印您的命令。如果你发出这个而不是执行它:
PRINT @cmd;
Run Code Online (Sandbox Code Playgroud)
你会看到:
WHERE ValueDate BETWEEN ' + CONVERT(VARCHAR,@b,121) + ' AND ' + CONVERT(VARCHAR,@E,121)
Run Code Online (Sandbox Code Playgroud)
产生这个:
WHERE ValueDate BETWEEN 2012-07-01 AND 2014-01-25
Run Code Online (Sandbox Code Playgroud)
其中,由于那些在 SQL Server 中看起来像带有几个减法运算符的三个整数,变成:
WHERE ValueDate BETWEEN 2004 AND 1988
Run Code Online (Sandbox Code Playgroud)
这甚至不是有效的BETWEEN
操作,即使它可以将这些转换为日期。
你应该做的是将这些作为适当的参数传递,例如
SET @cmd = 'DELETE FROM dbo.DateTable -- always use SCHEMA prefix
WHERE ValueDate BETWEEN @b AND @e;';
EXEC sp_executesql @cmd, N'@b DATE, @e DATE', @b, @e;
Run Code Online (Sandbox Code Playgroud)
如果这些列中的任何一个是DATETIME
,不是DATE
,那么您根本不应该使用BETWEEN
。反而:
WHERE ValueDate >= @b AND ValueDate < DATEADD(DAY, 1, @e);
Run Code Online (Sandbox Code Playgroud)
请阅读:
至于针对数据库中的所有日期列重复此查询的总体要求,这是我将如何执行(我已经查看了您引用的 SO 问题,它仅显示了如何对一个表执行此操作):
DECLARE @b DATE = '2012-07-01', @e DATE = '2014-01-25',
@sql NVARCHAR(MAX) = N'';
SELECT @sql += N'
DELETE ' + QUOTENAME(s.name)
+ '.' + QUOTENAME(t.name) + '
WHERE ' + QUOTENAME(c.name) + ' >= @b
AND ' + QUOTENAME(c.name) + ' < DATEADD(DAY, 1, @e);'
FROM sys.tables AS t
INNER JOIN sys.schemas AS s
ON t.[schema_id] = s.[schema_id]
INNER JOIN sys.columns AS c
ON t.[object_id] = c.[object_id]
WHERE c.system_type_id IN (40,42,43,58,61);
-- or just = 40 if you're only interested in DATE columns
PRINT @sql;
-- EXEC sp_executesql @sql, N'@b DATE, @e DATE', @b, @e;
Run Code Online (Sandbox Code Playgroud)