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

Mig*_*uel 8 sql-server sql-server-2012

我有一个问题,因为我一直在使用这个查询没有问题......直到现在:

UPDATE T1
SET ORDINAL = DATEDIFF(DAY, T2.Opening_Date, T1.Date)
FROM FactTransactions T1
INNER JOIN DimStore T2 ON T1.cod_store = T2.cod_storeKey
Run Code Online (Sandbox Code Playgroud)

但现在它给了我一个错误:

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

我不知道是怎么回事。以下是列:

Ordinal(numeric,null)
Opening_date(varchar, not null)
Date(varchar, not null)
cod_store(int,not null)
cod_storekey(PK,int, not null)
Run Code Online (Sandbox Code Playgroud)

Aar*_*and 7

您将日期存储为字符串 - 为什么?Opening_Date并且Date应该是dateor datetime,不是varchar。但在解决此问题之前,您需要确定导致转换问题的行:

SELECT cod_store, [Date]
FROM dbo.FactTransactions
WHERE ISDATE([Date]) = 0;

SELECT cod_storekey, Opening_Date
FROM dbo.DimStore
WHERE ISDATE(Opening_Date) = 0;
Run Code Online (Sandbox Code Playgroud)

现在您已经解决了这个问题,并且我知道您使用的是 2012 而不是 2008 R2,使用它可能更干净TRY_CONVERT(),特别是因为它可以让您识别月和日转置不正确的任何行。例如,假设您希望将日期存储为mm/dd/yyyy字符串:

SELECT cod_store, [Date]
FROM dbo.FactTransactions
WHERE TRY_CONVERT(datetime, [Date], 101) = 0;

SELECT cod_storekey, Opening_Date
FROM dbo.DimStore
WHERE TRY_CONVERT(datetime, Opening_Date, 101) = 0;
Run Code Online (Sandbox Code Playgroud)

除了识别用户在何处存储废话像垃圾一样行floob9992-13-36作为“日期”,这也将确定用户在何处存储行13/07/1999代替07/13/1999(但没有办法知道是否05/06/2000意味着是5月6日或6月5日)。

现在,您需要先修复这些行,然后才能更正表。

ALTER TABLE dbo.DimStore ALTER COLUMN Opening_Date date; -- or datetime;
ALTER TABLE dbo.FactTransactions ALTER COLUMN [Date] date; -- or datetime;
Run Code Online (Sandbox Code Playgroud)

您还可以考虑将Date列重命名为 (a) 不那么模糊和 (b) 不是保留字。

如果无法修复表,则需要更改查询:

UPDATE T1
SET ORDINAL = DATEDIFF(DAY, 
  CASE WHEN ISDATE(T2.Opening_Date) = 1 THEN T2.OpeningDate END,
  CASE WHEN ISDATE(T1.[Date]) = 1 THEN T1.Date END)
FROM dbo.FactTransactions AS T1
INNER JOIN dbo.DimStore AS T2 
  ON T1.cod_store = T2.cod_storeKey
WHERE ISDATE(T2.Opening_Date) = 1
  AND ISDATE(T1.[Date]) = 1;
Run Code Online (Sandbox Code Playgroud)

@RLF 也提出了一个很好的观点;如果您无法修复表格,则日期列可能包含表示特定日期(例如 9 月 7 日)但以错误格式输入的数据(例如,在美国英语系统中,以英国格式输入为字符串, 7/9/2015)。所以真的,你需要修复表格并停止将这些东西存储为字符串。

其他一些有用的材料:


Jul*_*eur 6

您可以使用具有正确样式的Convert以将varchar(x)本地格式转换为正确的日期:

DATEDIFF(DAY, convert(date, T2.Opening_Date, 104), convert(date, T1.Date, 104))
Run Code Online (Sandbox Code Playgroud)

在此示例中,我假设您使用的是德国风格 (dd.mm.yyyy)。德国风格是104

Select convert(date, '01.02.2015', 104)
Run Code Online (Sandbox Code Playgroud)

输出: 2015-02-01

您必须使其适应您的设置和本地格式,并用您使用的任何内容替换 104。主要款式有:

Style   Standard        Input/Ouput
1       U.S.            1 = mm/dd/yy
101     U.S.            101 = mm/dd/yyyy
2       ANSI            2 = yy.mm.dd
102     ANSI            102 = yyyy.mm.dd
3       British/French  3 = dd/mm/yy
103     British/French  103 = dd/mm/yyyy
4       German          4 = dd.mm.yy
104     German          104 = dd.mm.yyyy
5       Italian         5 = dd-mm-yy
105     Italian         105 = dd-mm-yyyy
Run Code Online (Sandbox Code Playgroud)

看看CAST 和 CONVERT (Transact-SQL)

如果您的日期格式错误或格式混合,您还可以使用以下查询查看TRY_CONVERT

Select ... 
FROM FactTransactions T1
INNER JOIN DimStore T2 ON T1.cod_store = T2.cod_storeKey
Where try_convert(date, T2.Opening_Date, 104) is null 
or try_convert(date, T1.Date, 104) is null
Run Code Online (Sandbox Code Playgroud)

它将为您提供样式 104 格式不正确的日期列表。同样,根据您的设置调整它并替换 104。您可以在修复错误格式和日期之前使用它。

但主要的问题和问题是:你为什么使用 varchar(x)。如果您可以更新您的表格,您应该使用 Date 或 Datetime 2。

  • 是的,TRY_CONVERT() 比 ISDATE() 更干净、更可靠。我使用后者是因为问题中的原始标题 (2012R2) - 我不确定它们是否真的意味着 2008 R2,其中 TRY_CONVERT() 不可用。 (2认同)