2017-10-16 2 views
0

J'essaie de convertir un champ DATETIME au format dd-mm-yyyy dans ma requête.Date hors de portée, mais seulement dans la procédure stockée appelez

Lorsque j'exécute l'une des lignes suivantes dans SSMS, la requête s'exécute correctement et ma date est au format correct.

CONVERT(VARCHAR(30), CONVERT(DATETIME, f.Created, 101), 103) as [Created] 
CONVERT(VARCHAR, f.created, 105) as [Created] 

f.Created est une colonne de DATETIME

Cependant, si je tente de l'exécuter dans le cadre d'une requête dans une procédure stockée je reçois une erreur:

The conversion of a varchar data type to a datetime data type resulted in an out-of-range value.

Dans la procédure stockée , J'ai essayé de définir la langue à British et aussi le format de la date à dmy mais je reçois toujours une erreur hors plage. Si je supprime ces lignes ou sélectionne simplement le champ f.Created normalement, cela fonctionne.

Qu'est-ce qui ne va pas?

Edit:

requête pour exécuter 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 

données typiques dans la colonne 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 

résultat escompté pour le f. Colonne créée

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

Je serais heureux avec une variation de/jj-mm-aa aaaa

+0

Quel type est 'f.created'? Pourquoi convertissez-vous deux fois? Pouvez-vous fournir des exemples de données et les résultats attendus? –

+0

DATETIME. J'ai mis à jour mon message. –

Répondre

1

L'erreur se produit dans la condition WHERE de la première requête

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

parce que vous avez converti votre DATETIME à chaîne et vous essayer de comparer cette chaîne avec deux autres dates (BETWEEN)

Il suffit de déplacer cette condition dans votre sous-requête et d'utiliser la colonne DATETIME d'origine dans WHERE, et converti en SELECT ...

en fait pourquoi utilisez-vous même sous-requête:

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

ou ont deux colonnes - une DATETIME originale à utiliser dans OU, autre converti en VARCHAR pour montrer au client.

+0

J'ai une sous-requête car il y a deux alias retournés par une fonction (non inclus dans mon message, pour éviter toute confusion) qui ne peut pas être utilisée dans une clause where mais peut être dans une sous-requête –

+0

@MrJF OK, garder la sous-requête si nécessaire, mais la logique reste la même, utilisez la condition 'where' sur la colonne' datetime'. Pouvez-vous obtenir votre solution d'ici? –

+0

Oui, cela fonctionne réellement. Merci! –

0

La manière correcte serait tout simplement:

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

Vous devez toujours inclure la longueur avec des types de chaîne dans SQL Serveur. Cependant, cela ne va pas résoudre votre problème. Un problème peut être la convention de nommage. Je ne sais pas ce que votre procédure stockée ressemble, mais vous pouvez essayer:

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

De cette façon, les deux ne se confondre. Si Created_ddmmyyyy est alors utilisé dans un but, votre requête peut essayer de le convertir en date/heure - en utilisant un format différent.

Vous ne devriez jamais avoir une erreur hors plage en convertissant une date/heure en chaîne.

+0

J'ai essayé les deux exemples mais ne fonctionne pas. J'ai la même erreur. Je vais mettre à jour mon poste avec tous les détails –

+0

@MrJF. . . Vous manquez le point. Utilisez 'created' pour les comparaisons avec les valeurs date/heure. C'est une date/heure. Utilise le! –

0

Ne convertissez pas la colonne f.created du tout si elle est déjà au format datetime. dateadd() renvoie un datetime, correspondant exactement au type de votre colonne.

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

La seule raison pour laquelle je peux penser pourquoi vous avez ajouté la conversion, parce que vous avez lu quelque part que getdate() comprend le composant de temps et que vous voulez obtenir des résultats retournés pour hier moins de 2 mois jusqu'à aujourd'hui , et non pour "maintenant" moins 2 mois jusqu'à "maintenant" (c'est-à-dire y compris le temps).Si tel est le cas, vous avez été égaré par l'un des nombreux exemples incorrects trouvés sur Internet. Oubliez la conversion en chaînes, utilisez juste une combinaison intelligente de dateadd() et de datediff() et vous obtiendrez exactement ce dont vous avez besoin sans le cauchemar international de format de date que vous rencontrez actuellement et en bonus: c'est plus rapide. Si vous voulez être pleinement informé, veuillez lire: Dwain Camps' blog post called "Manipulating Dates and Times in T-SQL". Mais voici un exemple rapide qui implémente la troncature de date pour se débarrasser du composant time renvoyé par 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) 

S'il vous plaît noter également l'utilisation de> = et < au lieu de entre et dans mes exemples. Between inclut à la fois les valeurs de début et de fin, de sorte que vous pourriez incorrectement inclure des lignes qui ont un composant time de 00: 00: 00.000 si vous utilisez entre au lieu de => et <.

Pour obtenir une chaîne de date formatée de telle sorte qu'il ne peut jamais être interprété de manière incorrecte, le format dans le format aaaa-mm-jj en utilisant:

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

Comme un autre plus, il a même correctement sortes! Des informations complètes sur convert et son paramètre de mise en forme peut être trouvé dans les livres de serveur SQL en ligne: Books online

+0

Merci pour la réponse détaillée. Je vais lire sur les problèmes que vous avez mentionnés car ce n'est pas la première fois que je rencontre des problèmes avec datetime! La raison pour laquelle je convertis le champ 'f.created' est de l'afficher dans un format plus lisible, car les résultats de la requête doivent être envoyés par e-mail et le format SQL ne semble pas avoir été conçu pour être lu par les humains. Lorsque j'ai exécuté votre version, l'e-mail est complètement vide. –

+0

:-) Je viens de le réaliser et j'ai déjà ajouté un petit commentaire dans ma réponse originale. – rrozema