2017-10-16 47 views
0

我試圖在我的查詢中將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 protected];', 
    @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'', @[email protected] 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', @[email protected] OUTPUT 
END 

GO 

典型數據在f.created柱

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預期結果。創建了列

05-11-2002 
15-12-2003 
05-11-2002 
05-11-2002 
06-11-2002 

我將很高興與DD-MM-YY/YYYY的任何變化

+0

是什麼類型'f.created'?你爲什麼要把它轉換兩次?你能提供一些樣本數據和預期結果嗎? –

+0

DATETIME。我已更新我的帖子。 –

回答

1

的錯誤,然後你試圖比較與其他兩個日期(BETWEEN)

該字符串只動子查詢中這種情況下,用在何處,並在SELECT轉換的原始DATETIME列...。

其實爲什麼你甚至可以使用子查詢:

@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()' 

或有兩列 - 一個原始的DATETIME在何處,其他轉換爲VARCHAR展現給客戶使用。

+0

我有一個子查詢,因爲有一個函數返回的兩個別名列(不包括在我的文章中,爲了避免混淆),它不能在'where子句中使用,但可以在子查詢中 –

+0

@MrJF OK,如果需要的話保留子查詢,但邏輯保持不變,在'datetime'列上使用'where'條件..你能從這裏得到你的解決方案嗎? –

+0

是的,這實際上工作。謝謝! –

0

正確的方法是簡單地:

CONVERT(VARCHAR(255), f.created, 105) as [Created] 

你應該總是包括長度與SQL字符串類型服務器。 但是,這不會解決您的問題。一個問題可能是命名約定。我不知道你的存儲過程是什麼樣子,但你可以嘗試:

CONVERT(VARCHAR(255), f.created, 105) as Created_ddmmyyyy 

這樣一來,兩者不會混淆。如果Created_ddmmyyyy然後被用於某種目的,您的查詢可能會嘗試將其轉換回日期/時間 - 使用不同的格式。

您不應該得到將日期/時間轉換爲字符串的超範圍錯誤。

+0

我已經嘗試過兩個例子,但都沒有成功。我犯了同樣的錯誤。我會更新我的帖子的所有細節 –

+0

@MrJF。 。 。你錯過了這一點。使用'created'來比較日期/時間值。這是一個日期/時間。用它! –

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個月返回直到今天,而不是「現在」減2個月直到「現在」(即包括時間)。如果是這樣的話,你會被互聯網上發現的許多不正確的例子之一所誤導。忘掉轉換成字符串,只需使用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) 

還請注意使用> =和<代替間,在我的例子。之間既包括起始值和結束值,因此你可以不正確地包括具有00的時間成分行:00:00.000,如果你代替=>和<之間使用。正確

select convert(varchar(10), getdate(), 120) 

至於另外再加上,它甚至各種各樣:

使用GET格式,使得它不能被錯誤地解釋日期字符串,格式爲YYYY-MM-DD格式!在轉換和格式化參數的全部信息可以在SQL Server的書籍在網上找到:在第一查詢條件

WHERE 
x.Created_ddmmyyyy Between DATEADD(m, -2, GETDATE()) and GETDATE() 

,因爲你已經將您DATETIME字符串出現Books online

+0

感謝您的詳細解答。我會閱讀你提到的問題,因爲這不是我第一次遇到日期問題!我轉換'f.created'字段的原因是以更具可讀性的格式顯示它,因爲查詢結果將通過電子郵件發送,而SQL格式似乎沒有爲人類設計。當我運行你的版本時,電子郵件完全空白。 –

+0

:-)我剛剛意識到這一點,並已在我的原始答案中添加了一條簡短評論。 – rrozema