【问题标题】:MSSQL PreparedStatement for smalldatetime returns wrong resultsmalldatetime 的 MSSQL PreparedStatement 返回错误结果
【发布时间】:2019-01-14 02:03:25
【问题描述】:

我的 mssql 数据库中有一个表,其中一列是 smalldatetime 数据类型。

smalldatetime 数据类型没有秒精度。根据其documentation

ss 是两位数,范围从 00 到 59,表示秒。小于等于 29.998 秒的值向下舍入到最接近的分钟,大于等于 29.999 秒的值向上舍入到最接近的分钟。"

我有一个包含列 description(varchar) 和 startTime(smalldatetime) 的表 em_test。

I inserted following entries in table :
    insert into em_test values('e1-2018-08-01 23:59:59', '2018-08-01 23:59:59');
    insert into em_test values('e2-2018-08-02 00:00:00', '2018-08-02 00:00:00');
    insert into em_test values('e3-2018-08-02 01:00:00', '2018-08-02 01:00:00');
    insert into em_test values('e4-2018-08-02 23:59:59', '2018-08-02 23:59:59');
    insert into em_test values('e5-2018-08-03 00:00:00', '2018-08-03 00:00:00');

如果我运行查询:select * from em_test where startTime between '2018-08-02 00:00:00' and '2018-08-02 11:59:59',将获得以下结果:

name                    startTime
e1-2018-08-01 23:59:59  2018-08-02 00:00:00 (Rounded up)
e2-2018-08-02 00:00:00  2018-08-02 00:00:00
e3-2018-08-02 01:00:00  2018-08-02 01:00:00
e4-2018-08-02 23:59:59  2018-08-03 00:00:00 (Rounded up)
e5-2018-08-03 00:00:00  2018-08-03 00:00:00

使用预处理语句的相同查询返回不同的结果

name                    startTime
e1-2018-08-01 23:59:59  2018-08-02 00:00:00 (Rounded up)
e2-2018-08-02 00:00:00  2018-08-02 00:00:00
e3-2018-08-02 01:00:00  2018-08-02 01:00:00

我调试了mssql-jdbc-6.4.0-jre7.jar源代码,了解为什么普通sql查询和sqlprepared statement返回的结果不同。 下面是分析:

  1. 每当必须执行准备好的语句时,都会在内部创建一个运行时存储过程来包装查询。查询输入参数变为存储过程输入参数。
  2. 在形成准备好的语句时,我们将 smalldatetime 值设置为时间戳为 ps.setTimeStamp('2018-08-01 23:59:59')。
  3. 在形成运行时存储过程时,输入参数数据类型已确定。 TimeStamp 映射到 mssql datetime2
  4. 因此在 datetime2 的情况下,args '2018-08-02 00:00:00' and '2018-08-02 11:59:59' 不会四舍五入,并且不会返回最后两个结果。
  5. 尝试用少量日志​​模拟运行时过程:
    创建过程 smalldatetimetest @startDate datetime2, @endDate datetime2
    作为
        print 'Datetime2 格式的开始日期:' + Convert(varchar(50), @startDate);
        print 'Datetime2 格式的结束日期:' + Convert(varchar(50), @endDate);
        print 'smalldatetime 格式的开始日期:' + Convert(varchar(50), cast(@startDate as smalldatetime));
        print 'smalldatetime 格式的结束日期:' + Convert(varchar(50), cast(@endDate as smalldatetime));
        select * from em_test where datetime1 在@startDate 和@endDate 之间;
    走
    

执行exec smalldatetimetest '2018-08-02 00:00:00','2018-08-02 23:59:59' 打印以下日志:

Start Date in Datetime2 format : 2018-08-02 00:00:00.0000000
End Date in Datetime2 format: 2018-08-02 23:59:59.0000000
Start Date in smalldatetime format: Aug  2 2018 12:00AM
End Date in smalldatetime format: Aug  3 2018 12:00AM

我的理解正确吗?在准备好的语句的情况下,是否有任何解决方法来解决差异?

【问题讨论】:

  • 如果您想将此报告为 JDBC 驱动程序的“问题”(可能的错误),您可以这样做here
  • 谢谢!我提出来了。
  • GitHub问题here

标签: sql-server jdbc mssql-jdbc smalldatetime


【解决方案1】:

以下解决方法似乎对我有用:

而不是提供java.sql.Timestamp 参数值

ps.setTimestamp(1, ts);

以字符串形式提供

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
ps.setString(1, sdf.format(ts));

这会导致 JDBC 生成的查询使用 nvarchar(4000) 参数而不是 datetime2 参数

exec sp_executesql N'SELECT COUNT(*) AS n FROM #tmp WHERE foo<=@P0        ',N'@P0 nvarchar(4000)',N'2018-08-11 02:59:59.001'

结果似乎与带有字符串文字日期/时间值的纯文本查询的结果相匹配,就像您的问题中的那样。

【讨论】:

    猜你喜欢
    • 2020-03-02
    • 1970-01-01
    • 1970-01-01
    • 2015-05-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多