【问题标题】:Cannot convert Varchar to DateTime - Conversion failed when converting date and/or time from character string无法将 Varchar 转换为 DateTime - 从字符串转换日期和/或时间时转换失败
【发布时间】:2020-06-25 22:21:12
【问题描述】:

我正在使用的源数据,这些都在一个名为 body 的列中。遗憾的是,该数据无法更改。数据通过来自我们网络外部的电子邮件进入,然后进入 Email2DB。 Email2DB 是一个将电子邮件数据放入表格的应用程序。

这不是数据通常存储在我们的表中的方式,这是一个临时的东西,用于在用户电脑离线时记录问题,以便跟踪问题的数量。

身体

21/05/2020| 16:01| Pathe 2.0 Delay| Pathe| LRW10| terence         

出于报告目的,我将数据拆分为单独的列。

我需要将单独的日期和时间作为 DateTime 格式放在一列中。 例如:2020-05-21 16:01

我正在尝试将数据输入到以下临时表中,但对于我的一生,我无法将日期和时间转换为 DateTime。

DECLARE @QuickData TABLE (TicketDate datetime, Issue varchar(50), Category varchar(50), ComputerName varchar(30), UserName varchar(50));

如果我将临时表 TicketDate 更改为 TicketDate varchar(25),则数据正常。但是最终目标是能够传入 @DateFrom 和 @DateTo 并能够在 @DateFrom 和 @DateTo 之间执行 where TicketDate ,除非 TicketDate 是日期时间格式,否则我不能这样做。

我无法理解的是,即使我将数据插入到带有 Ticket 为 Varchar 的临时表中,结果如下所示

Result if insert TicketDate as Varchar

一个数据在临时表中,我尝试使用 cast as datetime 和 cast as datetime2 运行 select 语句,还尝试转换为 datetime,但没有任何效果,我总是遇到同样的错误

从字符串转换日期和/或时间时转换失败。

如果有人能解决,我将在下面添加我尝试过的尝试之一,我将不胜感激。

请知道我已经用谷歌搜索了无数的想法并尝试了多种日期时间格式。

DECLARE @dateFrom DateTime = '2020-05-21 08:00:00'
DECLARE @dateTo DateTime = '2020-05-25 17:30:00'


    DECLARE @QuickData TABLE (TicketDate varchar(25), Issue varchar(50), Category varchar(50), ComputerName varchar(30), UserName varchar(50));

        
    INSERT INTO @QuickData  
        SELECT  CONVERT(VARCHAR(25), (TRIM(CONVERT(VARCHAR(10), CONVERT(date, dbo.f_SplitString(body,1,'|'), 105), 23))  + dbo.f_SplitString(body,2,'|') + ':00'),120),
    dbo.f_SplitString(body,3,'|') as 'Issue', dbo.f_SplitString(body,4,'|') as 'Category',
    dbo.f_SplitString(body,5,'|') as 'PCName', dbo.f_SplitString(body,6,'|') as 'User' FROM t_Quick;

    
    SELECT convert(datetime,TicketDate) as 'TicketDate' , Issue, Category, ComputerName, UserName 
    FROM @QuickData
    WHERE TicketDate between @dateFrom and @dateTo
    ORDER BY TicketDate DESC;

使用的拆分字符串函数如下。

USE [Central]
GO
/****** Object:  UserDefinedFunction [dbo].[f_SplitString]    Script Date: 26/06/2020 00:07:31 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author:      <Author,,Name>
-- Create date: <Create Date, ,>
-- Description: <Description, ,>
-- =============================================
ALTER FUNCTION [dbo].[f_SplitString]
(
  @multiwordstring VARCHAR(255),
  @wordnumber      integer,
  @delimiter char(1)
)
returns VARCHAR(255)
AS
  BEGIN
      DECLARE @remainingstring VARCHAR(255)
      SET @remainingstring=@multiwordstring

      DECLARE @numberofwords NUMERIC
      SET @numberofwords=(LEN(@remainingstring) - LEN(REPLACE(@remainingstring, @delimiter, '')) + 1)

      DECLARE @word VARCHAR(50)
      DECLARE @parsedwords TABLE
      (
         line NUMERIC IDENTITY(1, 1),
         word VARCHAR(255)
      )

      WHILE @numberofwords > 1
        BEGIN
            SET @word=LEFT(@remainingstring, CHARINDEX(@delimiter, @remainingstring) - 1)

            INSERT INTO @parsedwords(word)
            SELECT @word

            SET @remainingstring= REPLACE(@remainingstring, @word + @delimiter, '')
            SET @numberofwords=(LEN(@remainingstring) - LEN(REPLACE(@remainingstring, @delimiter, '')) + 1)

            IF @numberofwords = 1
              BREAK

            ELSE
              CONTINUE
        END

      IF @numberofwords = 1
        SELECT @word = @remainingstring
      INSERT INTO @parsedwords(word)
      SELECT @word

      RETURN
        (SELECT RTrim(LTrim(word))
         FROM   @parsedwords
         WHERE  line = @wordnumber)

  END

【问题讨论】:

  • 所以这个21/05/2020| 16:01| Pathe 2.0 Delay| Pathe| LRW10| terence一栏?我认为您需要以更好、更规范的格式将数据推回并获取到您的数据库中。现在它不是数据库,而是文本文件。
  • 您是否真的尝试将查询解构为单个块以查看失败的位置?在我的简单测试中(我没有你对f_SplitString 函数的实现)它可以工作。运行这个:SELECT CONVERT(date, dbo.f_SplitString(body,1,'|'), 105), CONVERT(date, dbo.f_SplitString(body,1,'|'), 105), 23)), CONVERT(date, dbo.f_SplitString(body,1,'|'), 105), 23)) + dbo.f_SplitString(body,2,'|')。如果失败,请继续从最后删除代码块,直到找出问题所在。
  • @AaronBertrand 数据通过电子邮件从不在我们网络中的外部来源传入,然后通过 Email2DB。此应用程序将其放入表中,但无法将其分成不同的列,因此为什么我要处理这么糟糕的数据。
  • This 20-year old thing 除了将整个数据集转储到单个列中之外,什么也做不了?也许是时候研究不同的软件了。
  • 基于您的文本字符串中有空格的事实,我怀疑您的字符串拆分功能正在修剪,因此您在日期和小时组件之间失去了一个空格,因此我要求您 de - 构建您的查询以找到问题。

标签: sql-server datetime


【解决方案1】:

另一种方法是创建一个像这样的表值函数,它返回一个按顺序拆分的字符串:

CREATE FUNCTION dbo.fnOrderedStringSplit
(
    @List       varchar(4000),
    @Delimiter  char(1)
)
RETURNS TABLE
AS
    RETURN
    (
        SELECT Element = ROW_NUMBER() OVER (ORDER BY [Number]),
               [Value] = LTRIM(RTRIM(SUBSTRING(@List, [Number],
                 CHARINDEX(@Delimiter, @List + @Delimiter, [Number]
                 ) - [Number])))
        FROM (SELECT TOP (4000) ROW_NUMBER() OVER (ORDER BY [object_id]) 
            FROM sys.all_columns) AS x ([Number])
        WHERE SUBSTRING(@Delimiter + @List, [Number], 1) = @Delimiter
          AND Number <= LEN(@List)
    );

然后您可以旋转它以提取特定元素:

-- INSERT @QuickData(TicketDate, Issue, Category, ComputerName, UserName)
SELECT TicketDate = TRY_CONVERT(datetime, [1] + ' ' + [2], 103),
       Issue      = [3],
       Category   = [4],
       PCName     = [5],
       UserName   = [6]
  FROM dbo.t_QuickData AS t 
  CROSS APPLY dbo.fnOrderedStringSplit(t.body, '|') AS f
  PIVOT (MAX([Value]) FOR Element IN ([1],[2],[3],[4],[5],[6])) AS p;

【讨论】:

  • 使用字符串 concat 获得更好的日期时间
【解决方案2】:

一种选择是通过 XML 解析字符串(如果是 2016 年,JSON 是另一种选择)

示例

Declare @YourTable Table ([ID] varchar(50),[body] varchar(150))  Insert Into @YourTable Values 
 (1,'21/05/2020| 16:01| Pathe 2.0 Delay| Pathe| LRW10| terence')
 

Select A.ID
      ,DateCol  = try_convert(datetime,Pos1+' '+Pos2,103)
      ,Issue    = Pos3
      ,Category = Pos4
      ,PCName   = Pos5
      ,[User]   = Pos6
 From  @YourTable A
 Cross Apply (
                Select Pos1 = ltrim(rtrim(xDim.value('/x[1]','varchar(max)')))
                      ,Pos2 = ltrim(rtrim(xDim.value('/x[2]','varchar(max)')))
                      ,Pos3 = ltrim(rtrim(xDim.value('/x[3]','varchar(max)')))
                      ,Pos4 = ltrim(rtrim(xDim.value('/x[4]','varchar(max)')))
                      ,Pos5 = ltrim(rtrim(xDim.value('/x[5]','varchar(max)')))
                      ,Pos6 = ltrim(rtrim(xDim.value('/x[6]','varchar(max)')))
                From  ( values (cast('<x>' + replace((Select replace([body],'|','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml))) as A(xDim)
             ) B

退货

【讨论】:

    【解决方案3】:

    解决方案

    您的字符串拆分功能会修剪字符串,因此您会丢失日期和时间组件之间的空间。

    更改您的代码

    SELECT  CONVERT(VARCHAR(25), (TRIM(CONVERT(VARCHAR(10), CONVERT(date, dbo.f_SplitString(body,1,'|'), 105), 23))
        + dbo.f_SplitString(body,2,'|') + ':00'),120),
    

    SELECT  CONVERT(VARCHAR(25), (TRIM(CONVERT(VARCHAR(10), CONVERT(date, dbo.f_SplitString(body,1,'|'), 105), 23))
       + ' ' + dbo.f_SplitString(body,2,'|') + ':00'),120),
    

    注意dbo.f_SplitString(body,2,'|')前面的空格

    “授人以鱼,养其一日。授人以渔,养其一生”

    您可以通过如下方式解构查询来轻松地自行调试:

    SELECT CONVERT(date, dbo.f_SplitString(body,1,'|'), 105) FROM t_Quick
    SELECT CONVERT(date, dbo.f_SplitString(body,1,'|'), 105), 23)) FROM t_Quick
    SELECT CONVERT(date, dbo.f_SplitString(body,1,'|'), 105), 23))  + dbo.f_SplitString(body,2,'|') FROM t_Quick
    

    您可以查看每个阶段的输出以了解问题所在。

    【讨论】:

      【解决方案4】:

      感谢大家的建议。不过,我终于弄清楚了问题所在。转换时间时,有一个“不普通”的空间,TRIM 无法删除空间,因此解决方案是使用子字符串,效果很好。

          DECLARE @QuickData TABLE (TicketDate datetime, Issue varchar(50), Category varchar(50), ComputerName varchar(30), UserName varchar(50));
      
      
          INSERT INTO @QuickData  
              SELECT convert(datetime, try_CONVERT(VARCHAR(25), (TRIM(CONVERT(VARCHAR(10), CONVERT(date, dbo.f_SplitString(body,1,'|'), 105), 23))  + ' ' + substring(dbo.f_SplitString(body,2,'|'),2,5) + ':00'),120)), 
          dbo.f_SplitString(body,3,'|') as 'Issue', dbo.f_SplitString(body,4,'|') as 'Category',
          dbo.f_SplitString(body,5,'|') as 'PCName', dbo.f_SplitString(body,6,'|') as 'User' FROM t_Quick;
      
      
          Select * from @QuickData
          where TicketDate between @dateFrom and @dateTo
          order by 1 desc;
      

      【讨论】:

        猜你喜欢
        • 2013-12-05
        • 1970-01-01
        • 2012-04-17
        • 2017-12-07
        • 1970-01-01
        相关资源
        最近更新 更多