[英]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.