New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Selected datetime value doesn't match the original value on SQL Server 2016 #680

Open
hito4t opened this Issue Apr 18, 2018 · 3 comments

Comments

3 participants
@hito4t
Copy link

hito4t commented Apr 18, 2018

Driver version or jar name

mssql-jdbc-6.1.6.jre7.jar

SQL Server version

SQL Server 2016

Client operating system

Windows 7 Enterprise (64bit)

Java/JVM version

java version "1.7.0_79"

Table schema

CREATE TABLE DATETIMETEST (
Id int,
UpdateDate datetime,
PRIMARY KEY (Id)
);

INSERT INTO DATETIMETEST VALUES(1, '2018-04-01 12:00:00.000');
INSERT INTO DATETIMETEST VALUES(2, '2018-04-01 12:00:01.003');

Problem description

On SQL Server 2016, selected datetime value doesn't match the original value.
It must be caused by the following change.
https://support.microsoft.com/en-us/help/4010261/sql-server-and-azure-sql-database-improvements-in-handling-some-data-t

But the problem doesn't occur in .NET.
Is it a problem of the JDBC driver?

Expected behavior and actual behavior

Expected: it matches.
Actual: it doesn't match.

Repro code

Connection conn = ...;

for (int i = 1; i <= 2; i++) {
    PreparedStatement statement1 = conn.prepareStatement("SELECT UpdateDate FROM DATETIMETEST WHERE ID=?");
    statement1.setInt(1, i);
    ResultSet resultSet1 = statement1.executeQuery();
    resultSet1.next();
    Timestamp date1 = resultSet1.getTimestamp(1);
    resultSet1.close();
    statement1.close();

    PreparedStatement statement2 = conn.prepareStatement("SELECT COUNT(*) FROM DATETIMETEST WHERE ID=? AND UpdateDate=?");
    statement2.setInt(1, i);
    statement2.setTimestamp(2, date1);
    ResultSet resultSet2 = statement2.executeQuery();
    resultSet2.next();
    // When Id=1, count=1
    // When Id=2, count=1 (SQL Server 2012) / count=0 (SQL Server 2016)
    System.out.println("Id=" + i + ", UpdateDate=" + date1 + ", count=" + resultSet2.getInt(1));
    resultSet2.close();
    statement2.close();
}
@David-Engel

This comment has been minimized.

Copy link
Member

David-Engel commented Apr 18, 2018

You are correct that the issue is caused by the server-side change you noted. The reason the change breaks things is because the Timestamp data type in JDBC maps to the datetime2 data type in SQL Server. So in the first query, when i = 2, the database returns 2018-04-01 12:00:01.003 which is stored into date1 which becomes the Timestamp 2018-04-01 12:00:01.003000. When you send that Timestamp as a parameter in query 2, the 2016 database sees a comparison between the datetime2 parameter value of 2018-04-01 12:00:01.003000 and the database datetime value of 2018-04-01 12:00:01.003333 which it equates to not equal. (Due to the data handling accuracy "improvements" in 2016, datetime values are made more "accurate" when converting to datetime2 since datetime is actually granular to 1/300 of a second.)

You can see this in practice on the server side by simply running:
select 'equal' where cast('2018-04-01 12:00:01.003' as datetime) = cast ('2018-04-01 12:00:01.003' as datetime2)
It returns 'equal' on SQL Server 2014 and below but no rows on SQL Server 2016 and up.
This query shows how the database converts the datetime to a "more accurate" datetime2 in SQL Server 2016 and up:
select cast(cast('2018-04-01 12:00:01.003' as datetime) as datetime2), cast ('2018-04-01 12:00:01.003' as datetime2)

The reason this is not an issue in .NET is .NET has native DateTime and DateTime2 data types whereas JDBC only has Timestamp.

The most reasonable workarounds are to run the database in SQL Server 2014 compatibility mode or convert all datetime columns to datetime2(3).

@ulvii

This comment has been minimized.

Copy link
Member

ulvii commented Apr 18, 2018

Thank you for detailed explanation, @David-Engel!

The issue seems to be related to #443, we will try to add a fix in one of the upcoming releases.

@cheenamalhotra cheenamalhotra added this to In progress in MSSQL JDBC Apr 19, 2018

@hito4t

This comment has been minimized.

Copy link

hito4t commented Apr 19, 2018

@David-Engel @ulvii
Thank you!
I look forward to the release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment