2

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

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

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');

如果我运行 query : 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 查询和 sql 准备语句返回的结果不同。下面是分析:

  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

我的理解是否正确,是否有任何解决方法来解决准备好的声明中的差异?

4

1 回答 1

0

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

而不是提供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'

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

于 2018-08-07T15:16:46.380 回答