转换日期时间在存储过程中不起作用

CONVERT datetime not working in a stored procedure

我有一个在 Report Builder 中使用的存储过程,在查询中我有两列是我将它们设置为的日期; CONVERT(datetime, CONVERT(CHAR(8), EZ.ACDT), 101) as 'ACCOUNTING DATE', CONVERT(datetime, CONVERT(CHAR(8), EZ.TRDT), 101) AS'交易日期'

并且存储过程执行无误,但是,当我在 Report Builder 中 运行 设计模式下的报表时,我收到以下错误消息;

“从字符串转换日期 and/or 时间时转换失败。”

我已经搜索了关于 SOF 的其他问题,并尝试实施在其他情况下有效的建议,但 none 对我有效。

不同寻常的是,我在使用相同 table 构建的其他存储过程和工作正常的列上使用了相同的 CONVERT 设置,但出于某种原因,我不是收到此错误消息;

“从字符串转换日期 and/or 时间时转换失败。”

    ALTER PROCEDURE [dbo].[RPT_CAS300_REPORT]
(
--The below variables are used as parameters to search in Report Builder.  The date variables are range dates
@DIVISION NVARCHAR(50)
,@FROMACCDATE date
,@TOACCDATE date
,@FROMTRANSDATE date
,@TOTRANSDATE date
,@ERRORCODE NVARCHAR(50)

)

AS

BEGIN


select distinct
EZ.DIVI AS 'DIVISION',
EZ.ANBR AS 'ACCOUNT NUMBER',
EZ.SENO AS 'SEQ NO',
EZ.EVEN AS 'EVENT',
CONVERT(datetime, CONVERT(CHAR(8), EZ.ACDT), 101) as 'ACCOUNTING DATE',
CONVERT(datetime, CONVERT(CHAR(8), EZ.TRDT), 101) AS'TRANSACTION DATE',
EZ.ACTY AS 'ACCOUNT TYPE',
EZ.ITNO AS 'ITEM NUMBER',
MM.ITDS AS 'STYLE',
MM.BUAR AS 'BRAND',
MM.ITGR AS 'CLASS',
EZ.ACQT AS 'QUANTITY',
EZ.ACAM AS 'RECORDED AMOUNT',
EZ.AIT1 AS 'DIM 1: GL',
EA.TX40 AS 'GL DESCRIPTION',
EZ.AIT2 AS 'DIM 2: COST CENTRE',
EZ.AIT3 AS 'DIM 3: BRAND',
EZ.AIT4 AS 'DIM 4: CHANNEL',
EZ.AIT5 AS 'DIM 5: WAREHOUSE',
EZ.AIT6 AS 'DIM 6: ORDER',
EZ.AIT7 AS 'DIM 7: ACCOUNTING RULE',
EZ.RGDT AS 'END DATE',
EZ.ERCD AS 'ERROR CODE'
from CINACC EZ
left join MITMAS MM on MM.CONO = EZ.CONO
    and MM.ITNO = EZ.ITNO
    and EZ.deleted = 'N'
left join FCHACC EA on EA.CONO = EZ.CONO
    and EA.AITM = EZ.AIT1
    and EA.deleted = 'N'
where EZ.CONO = '100'
    AND (EZ.DIVI IN ( SELECT Value FROM dbo.FnSplit( @DIVISION,',') ) OR @DIVISION IS NULL ) 
    AND (EZ.ERCD IN ( SELECT Value FROM dbo.FnSplit( @ERRORCODE,',') ) OR @ERRORCODE IS NULL ) 
    AND CONVERT(datetime, CONVERT(CHAR(8), EZ.ACDT), 101) BETWEEN @FROMACCDATE AND @TOACCDATE
    AND CONVERT(datetime, CONVERT(CHAR(8), EZ.TRDT), 101) BETWEEN @FROMTRANSDATE AND @TOTRANSDATE
    AND EZ.deleted = 'N'


END

错误信息很清楚,你有一个空字符串,不能转换为日期时间,因为它不对应任何。

将那些空白字符串转换为空值,您将不再有这个问题。

CONVERT(datetime, nullif(ltrim(CONVERT(CHAR(8), EZ.ACDT)) = ''), 101) as 'ACCOUNTING DATE', 
CONVERT(datetime, nullif(ltrim(CONVERT(CHAR(8), EZ.TRDT)) = ''), 101) AS'TRANSACTION DATE'

尽管不建议将字符串转换为日期,因为格式错误的日期或与您的区域设置不同的日期都会导致查询失败。如果你不能帮助它,但必须从字符串转换为日期,那么使用 try_convert 而不是 convert,因为格式错误的字符串(如你的空白字符串)将作为空值返回。

TRY_CONVERT(datetime, CONVERT(VARCHAR(10), EZ.ACDT), 101) as 'ACCOUNTING DATE', 
TRY_CONVERT(datetime, CONVERT(VARCHAR(10), EZ.TRDT), 101) AS 'TRANSACTION DATE'

PD:我已将 char(8) 更改为 varchar(10),因为正如 Larnu 所说,您的结果应该截断他们的年份。

警告:此答案假设您的日期(和时间)列是强类型日期和时间数据类型,因为为什么不呢? 如果这不是真的,问题出在你的设计上;停止在 varchar 中存储日期(和时间)数据。修复设计,修复问题。


让我们看看你的一个表情:

CONVERT(datetime, CONVERT(CHAR(8), EZ.ACDT), 101) BETWEEN @FROMACCDATE AND @TOACCDATE

那么,这是做什么的?首先,让我们从 CONVERT(CHAR(8), EZ.ACDT) 开始,它将 EZ.ACDT 的值(记住我们假设是强类型日期和时间数据类型)并将其转换为 char(8)。没有注明样式,因此 SQL 服务器可以自由使用其默认选项,并且会因语言而异。

对于我自己(英语),这意味着 GETDATE() 返回的值将是 'Sep 2 2';一个完全无意义的值。如果我是美国人,我会得到同样的价值。如果我是法国人或日本人,我会分别得到值 'sept 2 ''09 2 20'。你在这里看到问题了吗?希望如此。

接下来我们有 CONVERT(datetime,{prior expression}, 101)。样式编号 101 适用于 MM/dd/yyyy。首先这是 10 个字符,我们已经将值截断为 8;一个问题。接下来,这些先验值中的 none 看起来很像那种格式。对于 BRITISHFRENCHJAPANESE,我们有格式 MMM d [y]MMMM d MM d [yy](括号中的字符表示 start 的值;例如 [yy] for 2021 将是 20 not 21)。毫不奇怪,这就是您的错误产生的地方。

最后我们有 {prior expression} BETWEEN @FROMACCDATE AND @TOACCDATE 它会(如果它被成功转换)检查值是否在 2 个参数之间。

据推测 您实际想要查看的是 datetime 值是否介于 2 个 date 值之间,但忽略 date 的时间部分=36=]。为此,您需要使用 >=< 日期界限。例如,对于上面的内容,那将是:

EZ.ACDT >= @FROMACCDATE AND EX.ACDT < DATEADD(DAY,1,@TOACCDATE)

对于SELECT,只是CONVERT你的值到date,不要格式化它。不要在 WHERE 中这样做,因为它会影响性能


应用所有这些更改后,您将得到以下结果:

ALTER PROCEDURE [dbo].[RPT_CAS300_REPORT] (
    --The below variables are used as parameters to search in Report Builder.  The date variables are range dates
    @DIVISION nvarchar(50),
    @FROMACCDATE date,
    @TOACCDATE date,
    @FROMTRANSDATE date,
    @TOTRANSDATE date,
    @ERRORCODE nvarchar(50))
AS
BEGIN


    SELECT DISTINCT --Is this really needed?
           EZ.DIVI AS DIVISION,
           EZ.ANBR AS [ACCOUNT NUMBER],
           EZ.SENO AS [SEQ NO],
           EZ.EVEN AS EVENT,
           CONVERT(date,EZ.ACDT) AS [ACCOUNTING DATE],
           CONVERT(date,EZ.TRDT) AS [TRANSACTION DATE],
           EZ.ACTY AS [ACCOUNT TYPE],
           EZ.ITNO AS [ITEM NUMBER],
           MM.ITDS AS STYLE,
           MM.BUAR AS BRAND,
           MM.ITGR AS CLASS,
           EZ.ACQT AS QUANTITY,
           EZ.ACAM AS [RECORDED AMOUNT],
           EZ.AIT1 AS [DIM 1: GL],
           EA.TX40 AS [GL DESCRIPTION],
           EZ.AIT2 AS [DIM 2: COST CENTRE],
           EZ.AIT3 AS [DIM 3: BRAND],
           EZ.AIT4 AS [DIM 4: CHANNEL],
           EZ.AIT5 AS [DIM 5: WAREHOUSE],
           EZ.AIT6 AS [DIM 6: ORDER],
           EZ.AIT7 AS [DIM 7: ACCOUNTING RULE],
           EZ.RGDT AS [END DATE],
           EZ.ERCD AS [ERROR CODE]
    FROM CINACC EZ
         LEFT JOIN MITMAS MM ON MM.CONO = EZ.CONO
                            AND MM.ITNO = EZ.ITNO
                            AND EZ.deleted = 'N'
         LEFT JOIN FCHACC EA ON EA.CONO = EZ.CONO
                            AND EA.AITM = EZ.AIT1
                            AND EA.deleted = 'N'
    WHERE EZ.CONO = '100'
      AND (EZ.DIVI IN (SELECT Value FROM dbo.FnSplit(@DIVISION, ',') )
        OR @DIVISION IS NULL)
      AND (EZ.ERCD IN (SELECT Value FROM dbo.FnSplit(@ERRORCODE, ',') )
        OR @ERRORCODE IS NULL)
      AND EZ.ACDT >= @FROMACCDATE AND EX.ACDT < DATEADD(DAY,1,@TOACCDATE)
      AND EZ.TRDT >= @FROMTRANSDATE AND EX.TRDT < DATEADD(DAY,1,@TOTRANSDATE)
      AND EZ.deleted = 'N';


END;

我也把分隔符改成了T-SQL分隔符,括号([])。不要使用文字字符串作为别名;这让新用户感到困惑,这是 唯一 可以用文字字符串定义对象名称的地方。

后期编辑:您可能还想将 RECOMPILE 添加到查询中的 OPTION 子句。您允许 @DIVISION@ERRORCODENULL 并返回所有行的处理可能会导致 非常 不同的查询计划。这将停止重复使用不适当的缓存计划。