我希望有人可以解释以下涉及将varchar转换为datetime的问题。
以下代码有效:
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;
但是,如果我们替换
'AND ud.uds_type = 'LPR'
FOR
'AND ud.uds_type IN (SELECT s FROM dbo.split(',','LPR'))'
我收到以下转换错误消息: '从字符串转换日期时转换失败。'
两行代码在不使用转换时返回完全相同的结果。但是,只要在使用dbo.split()时引入转换,我就会收到上面提到的错误消息。
我错过了某种参考,还是这种错误?
编辑: 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
编辑:如果有人感兴趣,我已经提供了一个解决方案,这要归功于Ivan。在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;
答案 0 :(得分:1)
我的猜测是ud.ud_field##2
不仅包含datetime
个数据。 ud.uds_type
以及您需要convert(datetime,ud.ud_field##2,103)
的原因之一。直接使用WHERE子句时,在调用CONVERT
函数之前应用谓词。这意味着SQL Server仅转换可转换的数据。当您使用split
函数时,SQL Server将无法再执行此操作。
如果ud.ud_field##2
用于存储多种数据类型,那么您就遇到了设计问题。有一些不同的解决方案(例如,创建多个uddetail
表 - 每个数据类型一个,向uddetail
添加列 - 每个数据类型一个,等等。
这是我的榜样:
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
现在,我们可以使用一个简单的WHERE子句进行过滤,并且对于任何一个表都可以。
SELECT CONVERT(DATETIME, [Sometimes_datetime], 103)
FROM #DifferentData1
WHERE [DataType] = 'LPR'
SELECT CONVERT(DATETIME, [Always_datetime], 103)
FROM #DifferentData2
WHERE [DataType] = 'LPR'
这适用于#DifferentData1
,因为谓词(WHERE子句)在 CONVERT
之前应用。但是,在使用split
函数时,SQL Sever会在应用谓词后尝试CONVERT
。这导致它失败。 #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)
注意:我必须强制使用MERGE JOIN,因为如果使用LOOP JOIN,则可以在CONVERT
之前应用谓词 。如果您的数据集很小,可能只是强制执行LOOP JOIN,但不建议将其作为一般解决方案:
SELECT CONVERT(DATETIME, [Sometimes_datetime], 103)
FROM #DifferentData1
WHERE [DataType] IN
(SELECT [SplitData]
FROM [dbo].[SplitString]('LPR', ','))
OPTION (LOOP JOIN)