简体   繁体   English

SQL-日期时间转换不一致

[英]SQL - Inconsistent datetime conversion issue

I am hoping someone can give me an explanation the the following issue involving converting a varchar to datetime. 我希望有人可以给我解释以下涉及将varchar转换为datetime的问题。

The code below works: 以下代码有效:

SELECT
    mt.matter_code,
    ud.uds_type,
    ud.group_no,
    ud.ud_field##2,
    convert(datetime,ud.ud_field##2,103) hearingDate
FROM dbo.matdb mt
    INNER JOIN dbo.matdb_add_in ad
            ON mt.mt_int_code  = ad.mt_int_code
            AND ad.add_in_code = 'OUTAA'
    INNER JOIN dbo.uddetail ud
            ON convert(varchar,ad.mt_add_in_int_code) = ud.owner_code
            AND ud.parent_code = ad.add_in_code
            AND ud.po_type_char = 'A'
            AND ud.uds_type = 'LPR'
            --AND ud.uds_type IN (SELECT s FROM dbo.split(',','LPR'))
WHERE mt.mt_type   = 'MATA'
AND mt.matter_code = '118-1'
ORDER BY ud.ud_field##2 ASC;

However if we substitute the 但是,如果我们用

'AND ud.uds_type = 'LPR'

FOR 对于

'AND ud.uds_type IN (SELECT s FROM dbo.split(',','LPR'))'

I get the following conversion error message: 'Conversion failed when converting datetime from character string.' 我收到以下转换错误消息:“从字符串转换日期时间时转换失败。”

Both lines of code return the EXACT same results when not using a conversion. 不使用转换时,两行代码都返回完全相同的结果。 However as soon as the conversion is introduced when using dbo.split() I get the error message mentioned above. 但是,使用dbo.split()引入转换后,我立即收到上述错误消息。

Am I missing some kind of reference or is this a bug of sorts? 我是否缺少某种参考,或者这是某种错误?

EDIT: dbo.split function 编辑:dbo.split函数

USE [Vfile_Dev]
GO

/****** Object:  UserDefinedFunction [dbo].[Split]    Script Date: 02/18/2016 13:28:12 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE FUNCTION [dbo].[Split] (@sep char(1), @s varchar(512))
RETURNS table
AS
RETURN (
    WITH Pieces(pn, start, stop) AS (
      SELECT 1, 1, CHARINDEX(@sep, @s)
      UNION ALL
      SELECT pn + 1, stop + 1, CHARINDEX(@sep, @s, stop + 1)
      FROM Pieces
      WHERE stop > 0
    )
    SELECT pn,
      SUBSTRING(@s, start, CASE WHEN stop > 0 THEN stop-start ELSE 512 END) AS s
    FROM Pieces
  )
GO

EDIT: If anyone is interested, I have provided a solution below which was made possible thanks to Ivan. 编辑:如果有人感兴趣,我提供了一个解决方案,在下面,感谢伊凡(Ivan)使之成为可能。 Additional checks were made on ud.ud_field##2 to ensure that there were no blanks and the dbo.split results are now inserted into a table variable. 对ud.ud_field ## 2进行了其他检查,以确保没有空格,并且dbo.split结果现在插入到表变量中。

INSERT INTO @screenTable
SELECT s FROM dbo.split(',','LPR,COU');

SELECT
    mt.matter_code,
    ud.uds_type,
    ud.group_no,
    isnull(nullif(ud.ud_field##2,''),'01/01/1990') as hdNorm,
    convert(datetime, isnull(nullif(ud.ud_field##2,''),'1990-01-01 00:00:00.000')) AS hdConverted
FROM dbo.matdb mt
    INNER JOIN dbo.matdb_add_in ad
            ON mt.mt_int_code  = ad.mt_int_code
            AND ad.add_in_code = 'OUTAA'
    INNER JOIN dbo.uddetail ud
            ON convert(varchar, ad.mt_add_in_int_code) = ud.owner_code
            AND ud.parent_code = ad.add_in_code
            AND ud.uds_type IN (select * from @screenTable)
WHERE mt.mt_type   = 'MATA'
AND mt.matter_code = '118-1'
ORDER BY group_no ASC;

My guess is that ud.ud_field##2 contains more than just datetime data. 我的猜测是ud.ud_field##2包含datetime数据。 ud.uds_type and one of the reasons you need the convert(datetime,ud.ud_field##2,103) . ud.uds_type以及您需要convert(datetime,ud.ud_field##2,103)的原因之一。 When you use a WHERE clause directly, the predicate is applied before the CONVERT function is called. 直接使用WHERE子句时,将调用CONVERT函数之前应用谓词。 This means SQL Server is only converting data that can be converted. 这意味着SQL Server仅转换可以转换的数据。 When you use your split function, SQL Server is no longer able to do that. 当您使用split功能时,SQL Server将不再能够执行此操作。

If ud.ud_field##2 is being used to store multiple datatypes, then you have a design problem. 如果将ud.ud_field##2用于存储多个数据类型,则您会遇到设计问题。 There are a few different solutions (eg create multiple uddetail tables - one for each datatype, add columns to uddetail - one for each datatype, etc.). 有几种不同的解决方案(例如,创建多个uddetail表-每个数据类型一个,向uddetail添加列-每个数据类型一个,等等)。

Here's my example: 这是我的示例:

CREATE TABLE #DifferentData1
  (
     [DataType]           VARCHAR(20),-- This is our code for the type of data we have in Sometimes_datetime
     [Sometimes_datetime] VARCHAR(20)-- This is datetime or something else
  )

CREATE TABLE #DifferentData2
  (
     [DataType]        VARCHAR(20),-- This is our code for the type of data we have
     [Always_datetime] DATETIME,-- This is ALWAYS datetime
     [Other_data]      VARCHAR(50)
  )

INSERT INTO #DifferentData1
            ([DataType],
             [Sometimes_datetime])
VALUES      ( 'LPR',CONVERT(VARCHAR, GETDATE(), 103)),
            ( 'Almost LPR',CONVERT(VARCHAR, GETDATE(), 120)),
            ('Not LPR','Some other data type')

INSERT INTO #DifferentData2
            ([DataType],
             [Always_datetime],
             [Other_data])
VALUES      ( 'LPR',CONVERT(DATETIME, GETDATE(), 103),NULL),
            ( 'Almost LPR',NULL,CONVERT(VARCHAR, GETDATE(), 120)),
            ('Not LPR',NULL,'Some other data type')

-- Let's see what we have
SELECT [DataType],
       [Sometimes_datetime]
FROM   #DifferentData1

SELECT [DataType],
       [Always_datetime],
       [Other_data]
FROM   #DifferentData2

Now, we can filter with a simple WHERE clause and everything is fine for either table: 现在,我们可以使用简单的WHERE子句进行过滤,任何一张表都可以正常运行:

SELECT CONVERT(DATETIME, [Sometimes_datetime], 103)
FROM   #DifferentData1
WHERE  [DataType] = 'LPR'

SELECT CONVERT(DATETIME, [Always_datetime], 103)
FROM   #DifferentData2
WHERE  [DataType] = 'LPR'

This works for #DifferentData1 because the predicate (WHERE clause) is applied before the CONVERT . 这适用于#DifferentData1因为谓词(WHERE子句)在CONVERT 之前应用。 However, when using the split function, SQL Sever is attempting to CONVERT after the predicate is applied. 但是,当使用split函数时,SQL Sever会应用谓词尝试进行CONVERT This causes it to fail. 这导致它失败。 #DifferentData2 will still work as intended: #DifferentData2仍将按预期工作:

-- This fails
SELECT CONVERT(DATETIME, [Sometimes_datetime], 103)
FROM   #DifferentData1
WHERE  [DataType] IN
       (SELECT [SplitData]
        FROM   [dbo].[SplitString]('LPR', ','))
OPTION (MERGE JOIN) 

-- This works!
SELECT CONVERT(DATETIME, [Always_datetime], 103)
FROM   #DifferentData2
WHERE  [DataType] IN
       (SELECT [SplitData]
        FROM   [dbo].[SplitString]('LPR', ','))
OPTION (MERGE JOIN) 

Note: I had to force a MERGE JOIN, because the predicate might be applied before the CONVERT if a LOOP JOIN is used. 注意:我必须强制执行MERGE JOIN,因为如果使用LOOP JOIN,则该谓词可能会在CONVERT之前应用。 If your data set is small, it may work to simply force a LOOP JOIN, but this is NOT recommended as a general solution: 如果您的数据集很小,则可以简单地强制使用LOOP JOIN,但这不建议作为一般解决方案:

SELECT CONVERT(DATETIME, [Sometimes_datetime], 103)
FROM   #DifferentData1
WHERE  [DataType] IN
       (SELECT [SplitData]
        FROM   [dbo].[SplitString]('LPR', ','))
OPTION (LOOP JOIN) 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM