[转帖]SQL Server JDBC – Set sendStringParametersAsUnicode to false

sql,server,jdbc,set,sendstringparametersasunicode,to,false · 浏览次数 : 0

小编点评

Select   | 0       |             | SELECT  | 0       |             | PLAN_ROW | 0        | 1.0               | So, you can run the test for yourself and see that it works as explained in the article.

正文

https://vladmihalcea.com/sql-server-jdbc-sendstringparametersasunicode/
https://learn.microsoft.com/en-us/sql/connect/jdbc/setting-the-connection-properties?view=sql-server-ver16

If the sendStringParametersAsUnicode property is set to "true", String parameters are sent to the server in Unicode format.

If the sendStringParametersAsUnicode property is set to "false", String parameters are sent to the server in non-Unicode format such as ASCII/MBCS instead of Unicode.

The default value for the sendStringParametersAsUnicode property is "true".

Note: The sendStringParametersAsUnicode property is only checked to send a parameter value with CHAR, VARCHAR, or LONGVARCHAR JDBC types. The new JDBC 4.0 national character methods, such as the setNString, setNCharacterStream, and setNClob methods of SQLServerPreparedStatement and SQLServerCallableStatement classes, always send their parameter values to the server in Unicode whatever the setting of this property.

For optimal performance with the CHAR, VARCHAR, and LONGVARCHAR JDBC data types, an application should set the sendStringParametersAsUnicode property to "false" and use the setString, setCharacterStream, and setClob non-national character methods of the SQLServerPreparedStatement and SQLServerCallableStatement classes.

When the application sets the sendStringParametersAsUnicode property to "false" and uses a non-national character method to access Unicode data types on the server side (such as nchar, nvarchar and ntext), some data might be lost if the database collation doesn't support the characters in the String parameters passed by the non-national character method.

An application should use the setNString, setNCharacterStream, and setNClob national character methods of the SQLServerPreparedStatement and SQLServerCallableStatement classes for the NCHAR, NVARCHAR, and LONGNVARCHAR JDBC data types.

  

Last modified: Apr 17, 2021

Imagine having a tool that can automatically detect JPA and Hibernate performance issues. Wouldn’t that be just awesome?

Well, Hypersistence Optimizer is that tool! And it works with Spring Boot, Spring Framework, Jakarta EE, Java EE, Quarkus, or Play Framework.

So, enjoy spending your time on the things you love rather than fixing performance issues in your production system on a Saturday night!

Introduction

In this article, I’m going to explain why you should always disable the sendStringParametersAsUnicode default JDBC Driver setting when using SQL Server.

Database table

Let’s assume we have the following database table:

SQL Server Post table

The PostID column is the Primary Key, and the Title column is of the VARCHAR type and has a secondary index as well:

1
CREATE INDEX IDX_Post_Title ON Post (Title)

The Post table contains the following records:

| PostID | Title                                       |
|--------|---------------------------------------------|
| 1      | High-Performance Java Persistence, part 1   |
| 2      | High-Performance Java Persistence, part 2   |
| 3      | High-Performance Java Persistence, part 3   |
| 4      | High-Performance Java Persistence, part 4   |
| ..     | ..                                          |
| 249    | High-Performance Java Persistence, part 249 |
| 250    | High-Performance Java Persistence, part 250 |

As you can see, the Title column is highly selective since every record has a different title value.

Unexpected CONVERT_IMPLICIT and Clustered Index Scan

When finding a Post row by its associated Title column value, we expect an Index Seek operation against the IDX_Post_Title index, but this is not what we get when using the default SQL Server JDBC settings.

For instance, if we enable the runtime query statistics to retrieve the associated execution plan of the SQL query that filters by the Title column:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
executeStatement(entityManager, "SET STATISTICS IO, TIME, PROFILE ON");
 
try (PreparedStatement statement = connection.prepareStatement("""
    SELECT PostId, Title
    FROM Post
    WHERE Title = ?
    """
)) {
 
    statement.setString(1, title);
 
    if (statement.execute() && statement.getMoreResults()) {
        LOGGER.info("Execution plan: {}{}",
            System.lineSeparator(),
            resultSetToString(statement.getResultSet())
        );
    }
}

We get the following SQL execution plan:

1
2
3
4
5
|StmtText                                                                                            |
|----------------------------------------------------------------------------------------------------|
|SELECT PostId, Title FROM Post WHERE Title = @P0                                                    |
|  |--Clustered Index Scan(OBJECT:([high_performance_sql].[dbo].[Post].[PK__Post__AA12603828AEBF55]),|
|     WHERE:(CONVERT_IMPLICIT(nvarchar(255),[high_performance_sql].[dbo].[Post].[Title],0)=[@P0]))   |

The Clustered Index Scan operation tells us that SQL Server has used the PostId Clustered Index to scan the leaf pages in search of the Title value we provided.

The reason why the IDX_Post_Title index was not used is because of the implicit conversion that was done between the provided NVARCHAR value and the VARCHAR value of the Title column.

Even if we provided the Title bind parameter value as a VARCHAR using the setString method:

1
statement.setString(1, title);

The SQL Server JDBC Driver behaved as if we used setNString method instead.

SQL Server JDBC sendStringParametersAsUnicode configuration

By default, SQL Server sends all String parameter values as NVARCHAR since the sendStringParametersAsUnicode configuration is set to true.

So, if we set the sendStringParametersAsUnicode configuration value to false

1
jdbc:sqlserver://localhost;instance=SQLEXPRESS;databaseName=high_performance_sql;sendStringParametersAsUnicode=false;

And, rerun the previous SQL query, we will get the following execution plan:

1
2
3
4
5
|StmtText                                                                        |
|--------------------------------------------------------------------------------|
|SELECT PostId, Title FROM Post WHERE Title = @P0                                |
|  |--Index Seek(OBJECT:([high_performance_sql].[dbo].[Post].[IDX_Post_Title]),  |
|       SEEK:([high_performance_sql].[dbo].[Post].[Title]=[@P0]) ORDERED FORWARD)|

That’s exactly what we were expecting from the start. There’s an Index Seek on the IDX_Post_Title index, and there’s no implicit conversion happening anymore.

Handing Unicode characters

Now, even if you disable the sendStringParametersAsUnicode setting, you can still persist Unicode data in NHARNVARCHAR or NLONGVARCHAR column.

So, if the Title column is of the NVARCHAR type:

1
2
3
4
5
CREATE TABLE Post (
    PostID BIGINT NOT NULL,
    Title NVARCHAR(255),
    PRIMARY KEY (PostID)
)

We can set the Title column using the setNString PreparedStatement method:

1
2
3
4
5
6
7
8
9
10
11
try (PreparedStatement statement = connection.prepareStatement("""
    INSERT INTO Post (Title, PostID)
    VALUES (?, ?)
    """
)) {
 
    statement.setNString(1"România");
    statement.setLong(2, 1L);
 
    assertEquals(1, statement.executeUpdate());
}

And, we can read the Title column using the getNString ResultSet method:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
try (PreparedStatement statement = connection.prepareStatement("""
    SELECT Title, PostId
    FROM Post
    WHERE Title = ?
    """
)) {
 
    statement.setNString(1"România");
 
    try(ResultSet resultSet = statement.executeQuery()) {
        if (resultSet.next()) {
            assertEquals("România", resultSet.getNString(1));
            assertEquals(1L, resultSet.getLong(2));
        }
    }
}

If you’re using JPA and Hibernate, the NVARCHAR column needs to be annotated with the @Nationalized Hibernate annotation to instruct Hibernate that the underlying String attribute needs to be handled by the StringNVarcharType, as opposed to the default StringType:

1
2
3
4
5
6
7
8
9
10
11
12
@Entity(name = "Post")
public class Post {
 
    @Id
    @Column(name = "PostID")
    private Long id;
 
    @Column(name = "Title")
    @Nationalized
    private String title;
     
}

Awesome, right?

If you enjoyed this article, I bet you are going to love my Book and Video Courses as well.

 

2 Comments on “SQL Server JDBC – Set sendStringParametersAsUnicode to false”

  1. Pavel Rund
    March 3, 2023

    Thank you for inspiring article, but are you sure about your results? I tried the scenario you described and SQL server have chosen index seek operation even in case of sendStringParametersAsUnicode=true.
    May be it is dependent of SQL server version. I used MSSQL 2017.

    Regards
    Pavel Rund

    • You’re welcome.

      This test provides the proof.

      Here are the results:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      Test with sendStringParametersAsUnicode=true
       
      | Rows | Executes | StmtText                                                                                                                                                                                                                    | StmtId | NodeId | Parent | PhysicalOp           | LogicalOp            | Argument                                                                                                                                                                                         | DefinedValues                                                                                                       | EstimateRows | EstimateIO   | EstimateCPU | AvgRowSize | TotalSubtreeCost | OutputList                                                                                                          | Warnings | Type     | Parallel | EstimateExecutions |
      | ---- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | ------ | ------ | -------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------- | ------------ | ------------ | ----------- | ---------- | ---------------- | ------------------------------------------------------------------------------------------------------------------- | -------- | -------- | -------- | ------------------ |
      | 1    | 1        | SELECT PostId, Title FROM Post WHERE Title = @P0                                                                                                                                                                            | 1      | 1      | 0      |                      |                      |                                                                                                                                                                                                  |                                                                                                                     | 2.0          |              |             |            | 0.0050384817     |                                                                                                                     |          | SELECT   | 0        |                    |
      | 1    | 1        |   |--Clustered Index Scan(OBJECT:([high_performance_java_persistence].[dbo].[Post].[PK__Post__AA12603836E8D7BA]), WHERE:(CONVERT_IMPLICIT(nvarchar(255),[high_performance_java_persistence].[dbo].[Post].[Title],0)=[@P0])) | 1      | 2      | 1      | Clustered Index Scan | Clustered Index Scan | OBJECT:([high_performance_java_persistence].[dbo].[Post].[PK__Post__AA12603836E8D7BA]), WHERE:(CONVERT_IMPLICIT(nvarchar(255),[high_performance_java_persistence].[dbo].[Post].[Title],0)=[@P0]) | [high_performance_java_persistence].[dbo].[Post].[PostID], [high_performance_java_persistence].[dbo].[Post].[Title] | 2.0          | 0.0046064816 | 4.32E-4     | 61         | 0.0050384817     | [high_performance_java_persistence].[dbo].[Post].[PostID], [high_performance_java_persistence].[dbo].[Post].[Title] |          | PLAN_ROW | 0        | 1.0                |
       
      Test with sendStringParametersAsUnicode=false
       
      | Rows | Executes | StmtText                                                                                                                                                                           | StmtId | NodeId | Parent | PhysicalOp | LogicalOp  | Argument                                                                                                                                                          | DefinedValues                                                                                                       | EstimateRows | EstimateIO | EstimateCPU | AvgRowSize | TotalSubtreeCost | OutputList                                                                                                          | Warnings | Type     | Parallel | EstimateExecutions |
      | ---- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | ------ | ------ | ---------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | ------------ | ---------- | ----------- | ---------- | ---------------- | ------------------------------------------------------------------------------------------------------------------- | -------- | -------- | -------- | ------------------ |
      | 1    | 1        | SELECT PostId, Title FROM Post WHERE Title = @P0                                                                                                                                   | 1      | 1      | 0      |            |            |                                                                                                                                                                   |                                                                                                                     | 1.0          |            |             |            | 0.0032831        |                                                                                                                     |          | SELECT   | 0        |                    |
      | 1    | 1        |   |--Index Seek(OBJECT:([high_performance_java_persistence].[dbo].[Post].[IDX_Post_Title]), SEEK:([high_performance_java_persistence].[dbo].[Post].[Title]=[@P0]) ORDERED FORWARD) | 1      | 2      | 1      | Index Seek | Index Seek | OBJECT:([high_performance_java_persistence].[dbo].[Post].[IDX_Post_Title]), SEEK:([high_performance_java_persistence].[dbo].[Post].[Title]=[@P0]) ORDERED FORWARD | [high_performance_java_persistence].[dbo].[Post].[PostID], [high_performance_java_persistence].[dbo].[Post].[Title] | 1.0          | 0.003125   | 1.581E-4    | 61         | 0.0032831        | [high_performance_java_persistence].[dbo].[Post].[PostID], [high_performance_java_persistence].[dbo].[Post].[Title] |          | PLAN_ROW | 0        | 1.0                |

      So, you can run the test for yourself and see that it works as explained in the article.

与[转帖]SQL Server JDBC – Set sendStringParametersAsUnicode to false相似的内容:

[转帖]SQL Server JDBC – Set sendStringParametersAsUnicode to false

https://vladmihalcea.com/sql-server-jdbc-sendstringparametersasunicode/ https://learn.microsoft.com/en-us/sql/connect/jdbc/setting-the-connection-prop

[转帖]SQL Server 聚集索引和 非聚集索引 说明

https://www.cndba.cn/dave/article/4506 索引是与表或视图关联的磁盘上结构,可以加快从表或视图中检索行的速度。 索引包含由表或视图中的一列或多列生成的键。 这些键存储在一个结构(B 树)中,使 SQL Server 可以快速有效地查找与键值关联的行。 1 聚集索引

[转帖]SQL Server 不同版本之间的 区别说明

2021-05-12 23:5062070原创SQLServer 本文链接:https://www.cndba.cn/dave/article/4527 SQL Server 数据库版本也是在不断的进行迭代。目前主流存在的版本有:SQL Server 2008、2012、2014、2016、2017

[转帖]SQL Server 体系结构中的2个主要引擎 说明

2020-03-18 16:2321450原创SQLServer 本文链接:https://www.cndba.cn/dave/article/4498 SQL Server 由两个主要引擎组成∶关系引擎(relational engine)和存储引擎(storage engine)。 1 关系引擎

[转帖]SQL Server 内部数据库版本 及兼容表

2022-04-20 09:043100转载SQLServer Microsoft SQL Server 的较新版本创建的数据库无法附加或还原到较早的版本。之所以存在此限制,是因为较旧的版本不知道新版本中引入的文件格式有哪些变更。 如果你尝试将数据库附加到早期版本、或者还原到早期版本,将会收到 SQ

[转帖]Sql Server中通过sql命令获取cpu占用及产生锁的sql

https://www.jb51.net/article/266255.htm 这篇文章主要介绍了Sql Server中通过sql命令获取cpu占用及产生锁的sql,需要的朋友可以参考下 获取SQLSERVER中产生锁的SQL语句 1 2 3 4 5 6 7 8 9 10 11 12 13 14 1

[转帖]sql server 2016不能全部用到CPU的逻辑核心数的问题

https://blog.csdn.net/u011966339/article/details/122969685 最近在工作中遇到了,sql server 2016 不能把CPU 的核心全部用到的问题.通过分析工具看到 总共CPU核心有72核,但sql 只能用到40核心,想信也有很多人遇到这问题

[转帖]SQL Server各版本序列号/激活码/License/秘钥

https://www.cnblogs.com/cqpanda/p/16184853.html SQL Server 2019# Enterprise:HMWJ3-KY3J2-NMVD7-KG4JR-X2G8GEnterprise Core:2C9JR-K3RNG-QD4M4-JQ2HR-8468J

[转帖]SQL Server 2022 正式发布,支持 Azure 最多的 SQL Server 版本!

https://www.modb.pro/db/559467 SQLSERVER 最新版 2022年11月16日,我们宣布正式发布 SQL Server 2022,这是迄今为止支持 Azure 最多的 SQL Server 版本,并在性能、安全性和可用性方面不断创新。这标志着 SQL Server

[转帖]Sql Server 创建临时表

https://cdn.modb.pro/db/513973 创建临时表 方法一: create table #临时表名(字段1 约束条件,字段2 约束条件,.....) create table ##临时表名(字段1 约束条件,字段2 约束条件,.....) 方法二: select * into