"Arithmetic overflow error converting expression to data type nvarchar": pypyodbc + SQL Server - sql-server

|id | language
|123456| GE
I can't update data in SQL Server.
I'm beginner.I am writing telegram bot, and i want add new languages.Therefore, I use the database.
I have a table with 2 columns, one declared as nvarchar(id), and the other as nchar(language) and i'm trying change language.
import pypyodbc
...
#for example
id = 123456
lang = 'EN'
#
data = ("""
UPDATE bottest
SET language = ?
WHERE id = ?;
""")
k = (id, lang)
cursor.execute(data, k).rowcount
cursor.commit()
Getting this error:
File "C:\Users\MyPC\PycharmProjects\bottest_flask_i\venv\lib\site-packages\pypyodbc.py", line 1007, in check_success
ctrl_err(SQL_HANDLE_STMT, ODBC_obj.stmt_h, ret, ODBC_obj.ansi)
File "C:\Users\MyPC\PycharmProjects\bottest_flask_i\venv\lib\site-packages\pypyodbc.py", line 977, in ctrl_err
raise DataError(state,err_text)
pypyodbc.DataError: ('22003', '[22003] [Microsoft][ODBC SQL Server Driver][SQL Server]Arithmetic overflow error converting expression to data type nvarchar.')

Your parameter values are specified in the wrong order. The parameter for language (in the SET clause) appears before the parameter for id (in the WHERE clause) so you need to specify the language parameter value first. That is, instead of
k = (id, lang)
you need to use
k = (lang, id) # same order as the parameter placeholders appear in the command text

Related

Storing binary with JDBCTemplate

I have table like
create table test(payload varbinary(max))
I am trying to store text lines in compressed format in the database using the following code
String sql = "insert into test(payload) values (compress(:payload))
MapSqlParametersource msps = new MapSqlParameterSource();
msps.addValue("payload", "some text", Types.VARBINARY)
NamedParameterJdbcTemplate npjt = //;
npjt.update(sql, msps);
This gives the following error -
String is not in a valid hex format
If I provide the datatype in MapSqlParameterSource as VARCHAR, it doesn't give any error, but then using MSSQL's decompress function returns garbage value
select decompress(payload) from test

psycopg2 write list of strings (with text delimiter) to a postgres array

Objective:
I have a list containing strings, some have single quotes in them (as part of the string itself) ;
listOfStr = ['A sample string', "A second string with a ' single quote", 'a third string', ...]
Note that each entry does not necessarily use the same text delimiter, some are single quoted, other (the ones containing single quote as part of the string) are double quoted.
I want to insert my list as a postgresql ARRAY using psycopg2:
import psycopg2
connString = (...) # my DB parameters here.
conn = psycopg2.connect(connString)
curs = conn.cursor()
update_qry = ("""UPDATE "mytable" SET arraycolumn = {listofStr}::varchar[],
timestamp = now() WHERE id = {ID}""".format(listofStr=listofStr,
ID=ID))
curs.execute(update_qry)
The problem:
But I get this error:
SyntaxError: syntax error at or near "["
LINE 1: UPDATE "mytable" SET arraycolumn = ['A sample string'...
If I specify the ARRAY data type in the SQL query by adding the word 'ARRAY' in front of my list:
update_qry = ("""UPDATE "mytable" SET arraycolumn = ARRAY {listofStr}::varchar[],
timestamp = now() WHERE id = {ID}""".format(listofStr=listofStr,
ID=ID))
I get this error:
UndefinedColumn: column "A second string with a ' single quote" does not exist
LINE 1: 'A sample string', "A second string with a '...
I don't know how to fix it.
Environment:
Ubuntu 18.04 64 bits 5.0.0-37-generic x86_64 GNU/Linux
Python 3.6.9 (default, Nov 7 2019, 10:44:02)
psycopg2 2.7.7
psycopg2-binary 2.8.4
"PostgreSQL 10.10 (Ubuntu 10.10-0ubuntu0.18.04.1) on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0, 64-bit"
Related threads:
Postgres/psycopg2 - Inserting array of strings
Doc:
http://initd.org/psycopg/docs/usage.html -> # list adaptation
Basically the question should have been closed as a duplicate. However, you know Piro's answer and I think you have a problem with interpreting it.
id = 1
list_of_str = ['A sample string', "A second string with a ' single quote", 'a third string']
update_qry = """
UPDATE mytable
SET arraycolumn = %s,
timestamp = now()
WHERE id = %s
"""
cur = conn.cursor()
cur.execute(update_qry, [list_of_str, id])
conn.commit()
I agree with #piro that you really want Bind Parameters,
rather than attempting to do any crazy quoting.
You already know how to accomplish that when inserting
one simple VARCHAR row per list element.
I recommend you create a TEMP TABLE and
send your data to the database in that way.
Then consult https://www.postgresql.org/docs/current/sql-expressions.html#SQL-SYNTAX-ARRAY-CONSTRUCTORS
and use this example to munge rows of the temp table into an array:
SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE 'bytea%');
You will want an expression like
SELECT ARRAY(SELECT my_text FROM my_temp_table);
It is possible that your temp table will also need an integer column,
to preserve element order.

JPA - Error preparing CallableStatement - Index 4 out of range but only have 3 params

I'm using Spring Data JPA 1.10.2 with com.microsoft.sqlserver's sqljdbc 4.2. I get the following error:
o.h.engine.jdbc.spi.SqlExceptionHelper : Error preparing CallableStatement [User.pTest]
com.microsoft.sqlserver.jdbc.SQLServerException: The index 4 is out of range.
My entity class is:
#Entity
#NamedStoredProcedureQuery(name = "User.getUser", procedureName = "User.pTest", parameters = {
#StoredProcedureParameter(mode = ParameterMode.OUT, name = "session", type = byte[].class),
#StoredProcedureParameter(mode = ParameterMode.IN, name = "name", type = String.class),
#StoredProcedureParameter(mode = ParameterMode.IN, name = "date", type = Date.class)
})
#Data //lombok
public class User {
// serves no purpose other than to meet
// JPA requirement
#Id
private Long id;
}
The repository code is
public interface UserRepository extends Repository<User, Long> {
#Procedure("User.pTest")
byte[] getUserSession(#Param("name") String name,
#Param("date") Date date
);
}
My test code is as follows and when I run it I get the error:
#Test
public void testGettingApxSession() {
Calendar cal = new GregorianCalendar(2016,6,5);
byte[] b = userRepository.getUserSession("myName", cal.getTime());
}
How do I resolve the error?
Update
Forgot to include the relevant part of the SQL Server stored proc:
ALTER procedure [User].[pTest]
#session varbinary(max) out
,#name nvarchar(max) = null
,#opt nvarchar(max) = null
,#date datetime
as
begin
set #session = CAST(N'<?xml version="1.0" encoding="UTF-16" ?><Session session = 45"'/>' as varbinary(max))
end
If you specify the nth parameter, Microsoft SQL Server driver requires that you also include the other parameters up to n. For example, let's say your procedure has five parameters (in this order) each having a default value of null:
param1 = null
param2 = null
param3 = null
param4 = null
param5 = null
You may only want to specify a value for param4. But if you do, you also must at least specify param1, param2, and param3 in the call to the stored procedure.
That may not seem too much of an issue. But if you have many default params and only need to specify say the 15th parameter, it will be quite tedious to also have to specify the other 14.
At least that's not fatal. But it is fatal in combination with the following Hibernate rule: Hibernate requires that each specified param is not null. But what if the procedure requires that only param3 or param4 have a value (but not both)? If you specify param4 to have a value, then you must include param3 (per MS driver) and you must give it a value (per Hibernate). So, you're sunk.
The only solutions are
Use a different driver such as jTDS (which has an issue on truncating >8000 bytes) OR
Write a wrapper procedure to just have only the needed params (e.g. param4) and let it call the procedure with five parameters.
Details of the exception in the OP
The Microsoft SQL Server driver has found (in the db metadata) four stored procedure parameters and returns the name and index of each. Then, the Hibernate / JPA code is building the CallableStatement.Via #NamedStoredProcedureonly three parameters have been defined. So, it builds something like pTest(?,?,?). But then Hibernate uses the metadata to say that Date should be in the fourth position which is out of range. The built pTest only has three parameters.

Bulkcopy data from Oracle to SQL Server : String was not recognized as a valid Boolean

I am trying to copy data from Oracle to SQL Server using bulkcopy. The problem is that Oracle's boolean data is stored as varchar2(1) as t and f while SQL Server boolean data type is bit and stored as 1 and 0. Therefore I got the following error.
The given value of type String from the data source cannot be converted to type bit of the specified target column.
System.InvalidOperationException: The given value of type String from the data source cannot be converted to type bit of the specified target column.
System.FormatException: Failed to convert parameter value from a String to a Boolean.
System.FormatException: String was not recognized as a valid Boolean.
Is there any way to solve this problem?
using (OracleConnection srcConn = new OracleConnection())
using (SqlConnection destConn = new SqlConnection())
{
srcConn.ConnectionString = AppInfo.SrcConnStr;
srcConn.Open();
destConn.ConnectionString = AppInfo.DestConnStr;
destConn.Open();
using (SqlCommand destCmd = new SqlCommand("SET DATEFORMAT mdy;", destConn))
using (OracleCommand srcCmd = new OracleCommand("ALTER SESSION SET NLS_DATE_FORMAT = 'yyyy-mm-dd hh24:mi:ss'", srcConn))
{
srcCmd.CommandText = "select * from test";
rd = srcCmd.ExecuteReader();
SqlBulkCopy copy = new SqlBulkCopy(destConn);
// ColumnMappings property is used to map column positions, not data type
copy.DestinationTableName = "test";
copy.NotifyAfter = 2000;
copy.SqlRowsCopied += new SqlRowsCopiedEventHandler(OnSqlRowsCopied);
copy.BulkCopyTimeout = 10000000;
try { copy.WriteToServer((IDataReader)rd); }
TargetCount = (Int32)(typeof(SqlBulkCopy).GetField("_rowsCopied", BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance)).GetValue(copy);
copy.Close();
rd.Close();
}
}
Note that I have cast the SqlDataReader to IDataReader in a hope that the cast will convert T to 1 and F to 0. Obviously it is not.
Please any help will be appreciated.
Thanks,
Why not try to changed the value type before get data . like :
select case when id='T' then 1 else 0 end as id from test
id is the column that has different datatype between oracle and sqlserver.

How to connect two sets of db components using master / detail relationship?

This is how my db components are connected in program:
The datatypes of components are (first row, from left to right): TDBGrid, TDataSource, TClientDataSet, TDataSetProvider, TSQLQuery, TSQLConnection. Second row is analogous to first row.
Client data set Query_LimitDetail is linked to master data source through properties MasterSource and MasterFields. Their values are as follows:
Query_LimitDetail->MasterSource = DataSource_Limit;
Query_LimitDetail->MasterFields = L"ID";
SQL command assigned to client data sets are:
select * from LIMIT order by NAME
select * from LIMITDETAIL where LIMIT_ID = :ID order by ACCUR
This is how i open queries:
Query_Limit->Open();
Query_LimitDetail->Open();
When trying to open detail query, program throws following error:
dbExpress driver does not support the TDBXTypes.UNKNOWN data type. Vendor error message: unknown ISC error 0
Is there something I didnt make correctly?
The solution is to set SQL parameter type just before opening the query:
Query_Limit->Close();
Query_Limit->Open();
// ID param
TParam *param = Query_LimitDetail->Params->Items[0];
param->DataType = ftInteger;
Query_LimitDetail->Close();
Query_LimitDetail->Open();
Alternative and more universal solution is to set parameter type in OnBeforeOpen event handler like this:
__fastcall MyDataModule::MyDataModule(TComponent *Owner) :
TDataModule(Owner)
{
...
Query_LimitDetail->BeforeOpen = Query_LimitDetail_OnBeforeOpen;
...
}
void __fastcall MyDataModule::Query_LimitDetailBeforeOpen(TDataSet *DataSet)
{
if (Query_LimitDetail->Params->Count == 0)
{
return;
}
// ID param
TParam *param = Query_LimitDetail->Params->Items[0];
param->DataType = ftInteger;
}
It is also neccessary to link column in master table (specified in MasterFields property) to column in detail table:
Query_LimitDetail->IndexFieldNames = L"LIMIT_ID";

Resources