Get Hibernate to bind parameter type "char(1)" instead of "nvarchar(4000)"? - sql

DB side: SQL Server 2012. A data column is char(1) datatype.
ORM: Hibernate
Intention: Hibernate Query can bind a parameter properly, so no implicit datatype conversion in the query.
//not working as I intend. DB side still sees nvarchar(4000)
Query.setCharacter("paramName", myChar)
Query.setParameter("paramName", myChar, Hibernate.Type.CHARACTER)
Query.setString("paramName", myCharStr)
None of above variation works. SQL server profiler indicates that the bound parameter type is navarchar(4000). SQL server ends up doing some implicit datatype conversion when running the query, and it messed up some of my initial intention. (Rare and elusive deadlocks (select for update; then update) in case of multiple concurrent transactions)
Also it came to my attention that JDBC PreparedStatement API does not even have "setCharacter()". Not sure if this means anything. (http://docs.oracle.com/javase/7/docs/api/java/sql/PreparedStatement.html )
Is there a way to achieve the intention to bind a fixed length char, not nvarchar?

Actually it is not a Hibernate issue. Rather it is that collation set on this particular db column is different than DB collation. Once collation on this data column is fixed, then any of above Hibernate method variation can bind proper parameter type. In case anyone else runs into similar issue.
Edit: another important setting is "sendStringParametersAsUnicode". SQL server JDBC driver by default sent character as "nvarchar" or "nchar", unless you append "sendStringParametersAsUnicode=false" in your connection string;
http://technet.microsoft.com/en-us/library/ms378988.aspx
http://blogs.msdn.com/b/sqlcat/archive/2010/04/05/character-data-type-conversion-when-using-sql-server-jdbc-drivers.aspx

Related

SQL data types for AnyLogic

I am saving the output of my AnyLogic model into an SQL server database. For non-AnyLogic aficionados, AnyLogic is based on Java. However, I am not sure what data types I need to specify for my columns in the database.
So far I am using these:
double in AnyLogic : float in SQL
string in AnyLogic : varchar in SQL
int in AnyLogic : int in SQL
I also have parameters that are of type Option list, which is, if I understand correctly, a form of Java enum. I tried to save those parameters as varchar, but this (obviously) does not work. In addition, my model contains various boolean parameters. For my boolean parameters, I add columns of type bit in SQL by running:
ALTER TABLE myTable
ADD my_bool BIT NOT NULL DEFAULT 0;
However, running the model returns this error
SQLServerException: Invalid column name 'false'. Caused by: Invalid column name 'false'
So concretely, how can I export parameters of type Option list and boolean?
This addresses the original question which was tagged MySQL.
I don't know all the issues around "option list". Seems like a string (with a length such as varchar(255)) would work. You can also look into the built-in enum type, although I would not normally recommend using enums.
I would recommend using boolean instead of bit as the equivalent for boolean. Seems more mnemonic.
That said, MySQL understands false as a constant. You can check this by running:
select false
This also works:
select "false"
However, this returns the error that you specify:
select `false`
I suspect that the code being generated is using this construct. You will need to look at the code -- and you might need to figure out some other way of handling this. In MySQL you can use 0 for false and that might fix your problem.
The AnyLogic database is a standard HSQLDB database (not something proprietary) but they've added AnyLogic client functionality to define 'column types' as though they are Java types (with special types for option lists and compiled-on-the-fly-and-run Java code).
If you look at the db.script file (HSQLDB just stores the persistent DB data as an SQL script which creates the tables and INSERTs the values) you can see the underlying HSQLDB types which map closely to SQL Server types.
boolean --> BOOLEAN
double --> DOUBLE
int --> INT
String --> VARCHAR(16777216)
Date --> TIMESTAMP
Code --> VARCHAR(16777216)
Option List --> VARCHAR(255)
NB: The 'Java column types' are supposed to make it easier for a non-technical user to understand what they will get from a Java perspective when querying that column but, for example, they are confusing in that queries will return Java nulls for missing values, so a boolean column actually effectively returns a Boolean.
That should help.
I managed to address part of my problem. I am now able to store String variables from Java into my SQL database. The issue was due to incorrect use of quotations.
Java uses double quotations for String variables (e.g.: ""). SQL expects single quotations (e.g.: '') for string-like columns such as varchar() and char()
I had to amend my SQL query to this:
String insertTableSQL = "INSERT INTO test (my_string) VALUES(" +" '"+my_variable_string+"' )";
Note that my_variable_string is a derivative of a Java enum, which I obtained by executing String my_variable_string= my_enum.name();

colon(:) and dot(.) as millisecond separator in datetime2

I have migrated a Sybase database to SQL server 2008.
The main application that using the database trying to set some of dateTime2 column with data like 1986-12-24 16:56:57:81000 which is giving this error:
Conversion failed when converting date and/or time from character string.
Running the same query using dot(.) instead of colon(:) as millisecond separator like 1986-12-24 16:56:57.81000 or limiting the milliseconds to 3 digits like 1986-12-24 16:56:57:810 will solve the problem.
NOTE:
1- I don't have access to the source of application to fix this issue and there are lots of table with the same problem.
2. Application connect to database using ODBC connection.
Is there any fast forwarding solution or should i write lots of triggers on all tables to fix it using the above solutions?
Thanks in advance
AS Gordon Linoff said
A trigger on the current table is not going to help because the type
conversion happens before the trigger is called. Think of how the
trigger works: the data is available in a "protorow".
But There is a simple answer!
Using SQL Server Native Client Connection instead of basic SQL Server ODBC connection handle everything.
Note:
1. As i used SQL Server 2008 version 10 of SQL server native client works fine but not the version 11 (it's for SQL Server 2012).
2. Use Regional Settings make some other conversion problem so don't use it if you don't need it.
Select REPLACE(getdate(), ':', '.')
But it will Give String Formate to datetime Which is not covert into DateTime formate
Why would you need triggers? You can use update to change the last ':' to '.':
update t
set col = stuff(col, 20, 1, '.');
You also mistakenly describe the column as datetime2. That uses an internal date/time format. Your column is clearly a string.
EDIT:
I think I misinterpreted the question (assuming the data is already in a table). Bring the data into staging tables and do the conversion in another step.
A trigger on the current table is not going to help because the type conversion happens before the trigger is called. Think of how the trigger works: the data is available in a "protorow".
You could get a trigger to work by creating views and building a trigger on a view, but that is even worse. Perhaps the simplest solution would be:
Change the name and data type of the column so it contains a string.
Add a computed column that converts the value to datetime2.

performance of parameterised SQL

I have a query like
SELECT *
FROM myTable
WHERE key LIKE 'XYZ'
The value 'XYZ' is entered by users (and may include % and _)
If I construct the query using string concatenation it runs in 10 seconds.
But this is unsafe, and I should use a parameterised query.
So I'm constructing the query using the odbc command object and it's execute method, and passing a parameter.
SELECT *
FROM myTable
WHERE key LIKE ?
Unfortunately the parameterised SQL execute method takes a full minute.
This query is one of many that are part of a drill-down / investigation package, and I've had similar slow downs with all the parameterised queries (compared to string concatenation).
How do I find out where the time is going (and fix it) ?
Here's my guess without further information.
I've had similar problems on SQL Server. In SQL Server when the column on your table is 'varchar' and the parameterised query parameter is 'nvarchar' (or vice versa), this causes SQL Server to ignore an available index because the parameter type doesn't match the index type, which in turn results in a table scan.
It's possible the same thing happens for Sybase. If you can see the generated query you can confirm if there's a type mismatch.
If this is the case, then two solutions would be
explicitly set the type of the parameter to match the column type
change the type of the column to match the parameter type being generated
Mitch had the right suggestion.
I had to change the connection string to use the OLEDB driver, then I could set the options:
Optimize Prepare=None
Select Method=Direct

Setting collation property in the connection string to SQL Server 2005

I have a ASP.Net web application with connection string for SQL Server 2005 in the web.config.
Data Source=ABCSERVER;Network Library=DBMSSOCN;Initial Catalog=myDataBase;
User ID=myUsername;Password=myPassword;
I want to specify the collation property in the web.config for different languages like French like
Data Source=ABCSERVER;Network Library=DBMSSOCN;Initial Catalog=myDataBase;
User ID=myUsername;Password=myPassword;Collation=French_CS_AS
But the Collation word is not valid in the connection string.
What is the correct keyword that we need to use to specify the collation in SQL Server 2005 connection string?
Edit
I understand that collation can be set during the database installation and can also be changed. I do not want to change it permanently in the database. But I want the SQLClient to set the collation based on the application's settings.
I only want use it when using SQL Query like
SELECT * FROM TESTTABLE ORDER BY TESTCOLUMN COLLATE French_CS_AS
I am trying to ensure that for a given connection, all the commands/queries for that connection would automatically use the "French_CS_AS" - based on the property setting in the connection string, rather than changing the query definitions
You cannot set collation for a connection. It's simply not supported. See SQL Server Native Client: Connection strings and OLE DB for a really interesting blog article on how connection strings parse out.
You can set a language for a connection. Setting the language for a connection changes how dates are handled and causes system error messages to be provided in the specified language. See Session Language for more information on setting language.
A warning about using collations on non-Unicode types from COLLATE (Transact-SQL):
Code page translations are supported for char and varchar data types, but not for text data type. Data loss during code page translations is not reported.
Ideally, if you want consistent multilingual support from your data you should be using Unicode data types (nvarchar, etc.). You should also see the Collation and International Terminology article on MSDN for more information on this. It contains references to some additional articles that are quite useful as well so don't stop there.

Mapping to varchar and nvarchar in hibernate

If there are 2 columns in database, eg.
code varchar(3)
name nvarchar(50)
How to tell hibernate to pass varchar for searching by code?
In the hibernate mappings string is mapped to nvarchar and it produces queries like:
Select code, name From table where code=N'AAA' (instead of code='AAA')
This is very bad as it causes index scan instead of index seek operation (scanning all index nodes instead of directly going to requested one)
As code is used in millions of rows as well as in several indexes and foreign keys, changing it from varchar to nvarchar will cause performance degradation (more IO operations as nvarchar uses twice more space than varchar).
Is there any way to tell hibernate to do mapping according to database type, not to Java type?
Thanks
Probably you already solved this, but I had a similar problem.
I'm using jTDS JDBC driver and I solved the index scan problem by adding:
;sendStringParametersAsUnicode=false;prepareSQL=0
to the end of the jTDS connection string.
Probably it would not had solved your problem because by doing this, jTDS will only use VARCHAR (no NVARCHAR anymore).
Also, I had to disable the prepared SQL, because Hibernate is using 'like' instead of '=' when generating the queries and by using 'like' combined with a variable (SELECT ... WHERE column LIKE #var) causes an index scan (MSSQL 2000).
I'm assuming you're talking about NHibernate rather than Hibernate because the latter does not use nvarchar in its default SqlServer dialect.
The way to solve your problem is to specify column type as "AnsiString" in your mapping:
<property name="Code" type="AnsiString"/>
Take a look at this post for more details.
<type-mapping>
<sql-type jdbc-type="NVARCHAR" hibernate-type="string" />
</type-mapping>
Add the above code in the hibernate reveng file.
In hibernate.properties set the property hibernate.connection.defaultNChar=false.
You can either hide your tables behind views or use nstring type. This type is available in hibernate-core 4.x. In hibernate-core 3.6.10.Final you will need to define the custom type nstring - see the comment in the url:
Getting Hibernate and SQL Server to play nice with VARCHAR and NVARCHAR.