我正在尝试在查询中将DATETIME
字段转换为dd-mm-yyyy
格式。
当我在SSMS中运行以下任一行时,查询会成功执行,我会以正确的格式获取日期。
CONVERT(VARCHAR(30), CONVERT(DATETIME, f.Created, 101), 103) as [Created]
CONVERT(VARCHAR, f.created, 105) as [Created]
f.Created
是DATETIME列
但是,如果我尝试在存储过程中将其作为查询的一部分运行,我会收到错误:
The conversion of a varchar data type to a datetime data type resulted in an out-of-range value.
在存储过程中,我尝试将语言设置为British
,并将日期格式设置为dmy
但我仍然会出现超出范围的错误。如果我删除这些行或只是正常选择f.Created
字段,它就可以正常工作。
出了什么问题?
编辑:
查询以运行SP
DECLARE @html nvarchar(MAX);
EXEC spQueryToHtmlTable @html = @html OUTPUT, @query = N'
SELECT top 100 * from
(
select
c.clno + ''.'' + f.fileno as [Number]
,c.clName as [Client Name]
,f.fileDesc as [File name]
,CONVERT(VARCHAR(255), f.created, 105) as Created_ddmmyyyy
--Or either of these:
--CONVERT(VARCHAR(30), CONVERT(DATETIME, f.Created, 101), 103) as [Created]
--CONVERT(VARCHAR, f.created, 105) as [Created]
from config.dbfile f
join config.dbclient c on c.clid = f.clid
) x
where x.Department = ''Import'' and Type = ''Import''
and
x.Created_ddmmyyyy Between DATEADD(m, -2, GETDATE()) and GETDATE()
', @orderBy = '';
EXEC msdb.dbo.sp_send_dbmail
@profile_name = 'Outlook',
@recipients = 'email@email.com;',
@subject = 'subject of email',
@body = @html,
@body_format = 'HTML',
@query_no_truncate = 1,
@attach_query_result_as_file = 0;
SP:
/****** Object: StoredProcedure [dbo].[spQueryToHtmlTable] Script Date: 10/16/2017 11:47:40 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- Description: Turns a query into a formatted HTML table. Useful for emails.
-- Any ORDER BY clause needs to be passed in the separate ORDER BY parameter.
-- =============================================
CREATE PROC [dbo].[spQueryToHtmlTable]
(
@query nvarchar(MAX), --A query to turn into HTML format. It should not include an ORDER BY clause.
@orderBy nvarchar(MAX) = NULL, --An optional ORDER BY clause. It should contain the words 'ORDER BY'.
@html nvarchar(MAX) = NULL OUTPUT --The HTML output of the procedure.
)
AS
BEGIN
SET NOCOUNT ON;
IF @orderBy IS NULL BEGIN
SET @orderBy = ''
END
SET @orderBy = REPLACE(@orderBy, '''', '''''');
DECLARE @realQuery nvarchar(MAX) = '
DECLARE @headerRow nvarchar(MAX);
DECLARE @cols nvarchar(MAX);
SELECT * INTO #dynSql FROM (' + @query + ') sub;
SELECT @cols = COALESCE(@cols + '', '''''''', '', '''') + ''['' + name + ''] AS ''''td''''''
FROM tempdb.sys.columns
WHERE object_id = object_id(''tempdb..#dynSql'')
ORDER BY column_id;
SET @cols = ''SET @html = CAST(( SELECT '' + @cols + '' FROM #dynSql ' + @orderBy + ' FOR XML PATH(''''tr''''), ELEMENTS XSINIL) AS nvarchar(max))''
EXEC sys.sp_executesql @cols, N''@html nvarchar(MAX) OUTPUT'', @html=@html OUTPUT
SELECT @headerRow = COALESCE(@headerRow + '''', '''') + ''<th>'' + name + ''</th>''
FROM tempdb.sys.columns
WHERE object_id = object_id(''tempdb..#dynSql'')
ORDER BY column_id;
SET @headerRow = ''<tr>'' + @headerRow + ''</tr>'';
SET @html = ''<table border="1">'' + @headerRow + @html + ''</table>'';
';
EXEC sys.sp_executesql @realQuery, N'@html nvarchar(MAX) OUTPUT', @html=@html OUTPUT
END
GO
f.created column中的典型数据
2002-11-05 00:00:00.000
2003-12-15 00:00:00.000
2002-11-05 00:00:00.000
2002-11-05 00:00:00.000
2002-11-06 00:00:00.000
f.Created列的预期结果
05-11-2002
15-12-2003
05-11-2002
05-11-2002
06-11-2002
我会对dd-mm-yy / yyyy
的任何变化感到满意答案 0 :(得分:1)
第一个查询的WHERE条件中出现错误
WHERE
x.Created_ddmmyyyy Between DATEADD(m, -2, GETDATE()) and GETDATE()
因为您已将DATETIME转换为字符串,然后尝试将该字符串与其他两个日期进行比较(BETWEEN)
只需在子查询中移动此条件,并在WHERE中使用原始DATETIME列,并在SELECT ..中转换。
实际上为什么你甚至使用子查询:
@query = N'
select top 100
c.clno + ''.'' + f.fileno as [Number]
,c.clName as [Client Name]
,f.fileDesc as [File name]
,CONVERT(VARCHAR(255), f.created, 105) as Created
from config.dbfile f
join config.dbclient c on c.clid = f.clid
where Department = ''Import''
and Type = ''Import''
and f.Created Between DATEADD(m, -2, GETDATE()) and GETDATE()'
或者有两列 - 一个在WHERE中使用的原始DATETIME,另一个转换为VARCHAR以显示给客户端。
答案 1 :(得分:0)
正确的方法就是:
CONVERT(VARCHAR(255), f.created, 105) as [Created]
您应该始终在SQL Server中包含字符串类型的长度。 但是,这不会解决您的问题。一个问题可能是命名约定。我不知道您的存储过程是什么样的,但您可以尝试:
CONVERT(VARCHAR(255), f.created, 105) as Created_ddmmyyyy
这样,两人不会混淆。如果Created_ddmmyyyy
被用于某种目的,您的查询可能会尝试将其转换回日期/时间 - 使用不同的格式。
您不应该在将日期/时间转换为字符串时出现超出范围的错误。
答案 2 :(得分:0)
如果已经采用日期时间格式,则根本不要转换f.created列。 dateadd()返回一个日期时间,与列的类型完全匹配。
SELECT top 100 * from
(
select
c.clno + '.' + f.fileno as [Number]
,c.clName as [Client Name]
,f.fileDesc as [File name]
,f.created
--,CONVERT(VARCHAR(255), f.created, 105) as Created_ddmmyyyy
from config.dbfile f
join config.dbclient c on c.clid = f.clid
) x
where x.Department = 'Import' and Type = 'Import'
and x.Created >= DATEADD(m, -2, GETDATE())
and x.Created < GETDATE()
我能想到你为什么添加转换的唯一原因是因为你已经读过getdate()包含时间组件的某个地方,你希望昨天返回的结果减去2个月直到今天,而不是&#34;现在&#34;减去2个月直到&#34;现在&#34; (即包括时间)。如果是这种情况,您可能会被互联网上发现的许多不正确的例子之一误入歧途。忘记转换成字符串,只需使用dateadd()和datediff()的智能组合,如果没有您现在体验的国际日期格式噩梦,并且作为额外的奖励,您将得到您所需要的内容:它更快。如果您想充分了解情况,请阅读:Dwain Camps' blog post called "Manipulating Dates and Times in T-SQL"。但这是一个快速示例,它实现了日期截断,以消除getdate()返回的时间组件。
SELECT top 100 * from
(
select
c.clno + '.' + f.fileno as [Number]
,c.clName as [Client Name]
,f.fileDesc as [File name]
,f.created
--,CONVERT(VARCHAR(255), f.created, 105) as Created_ddmmyyyy
from config.dbfile f
join config.dbclient c on c.clid = f.clid
) x
where x.Department = 'Import' and Type = 'Import'
and x.Created >= DATEADD(m, -2, dateadd(d, datediff(d, 0, GETDATE()), 0))
and x.Created < dateadd(d, datediff(d, 0, GETDATE()), 0)
请注意使用&gt; =和&lt;而不是在我的例子之间。 Between包括开始值和结束值,因此如果使用between而不是=&gt;,则可能错误地包含时间分量为00:00:00.000的行。和&lt;。
要获取格式化的日期字符串,使其永远不会被错误解释,请使用以下格式转换为yyyy-mm-dd格式:
select convert(varchar(10), getdate(), 120)
另外,它甚至可以正确排序!有关转换及其格式参数的完整信息,请参见在线SQL服务器书籍:Books online