Is there a setting on an Oracle 11gR1 or R2 Client to change the data type returned by SELECT COUNT(*) from DOUBLE to LONG in an ODBC API call? - c++

Current C++ Application using ODBC API calling Oracle Client 10g (all versions), 11gR1 (v11.1.0.6) returns a LONG (4-byte integer) for
SELECT COUNT(*) from tablename
Change Oracle client to 11gR1 (v11.1.0.7) or higher (11.2.x.y etc) and that same statement returns a DOUBLE (8-byte floating value equivalent to oracle NUMBER data type).
Since the backend DB is the same version, there must be a client side setting (I presume) that has changed the default behaviour of this Aggregate SQL function to return a double rather than a long. I'm hoping to find such a setting that I can either set programmatically through an ODBC API call, or in the Oracle Client configuration itself.
I even tried using SQL CAST to make it an INT
SELECT CAST(COUNT(*) AS INT) FROM tablename
but that still returns a DOUBLE (8-byte floating NUMBER).
Note: given that I use ODBC, I've written alot of generic C++ code supporting the return value as LONG, as that is how it's been for 10 years via ODBC. I'd like to maintain that if possible without having to write ORACLE specific code within my applications.

Aggregate function count return an INTEGER, it is usually referred to as NUMBER(38,0).
All numeric values in an Oracle database are stored in the Oracle NUMBER format.
Number of Bytes:
round((length(p)+s)/2)+1
p = precision;
s = 0 for positive numbers, 1 for negative numbers;
0 and negative infinity consume 1 byte, positive infinity consumes 2 bytes;
select round((length(38)+0)/2)+1 from dual; --20 bytes
Try this (not tested)..
select vsize(count(*)) from tablename;
select dump(count(*)) from tablename;
Data type conversion/mapping is possible with OCI external data types.

Related

what is the best practice for storing date and time in class/object?

recently i'm going to connect to PostgreSQL and I need to store date/time in my object to pass to the query for insert and update some table.
but there is not clear way in c++ to store and retrieve date/time.
any comment?
PostgreSQL TimeFormat 9+ version https://www.postgresql.org/docs/9.1/datatype-datetime.html
Which is a 8byte int(64 bit) time format in microsecond precision, UTC without timezone (from top of the table).
When you create a table you can either time-stamp the record by PostgreSQL current_timestamp , OR insert into table as integer 64bit microsecond format. since PostgreSQL has multiple time format you should decide time any the format you want from table
PostgreSQL approach CREATE,INSERT,RETRIEVE
"CREATE TABLE example_table(update_time_column TIMESTAMP DEFAULT CURRENT_TIMESTAMP)"
"INSERT INTO example_table(update_time_column) VALUES update_time_column=CURRENT_TIMESTAMP"
"SELECT (EXTRACT(epoch FROM update_time_column)*1000000) FROM example_table"
C++ approach
auto/int64_t cppTime = get64bitMicrosecondFormat from some library`
something similar to this answer: Getting an accurate execution time in C++ (micro seconds)
Then push your object / record to PostGRESQL, when retrieve in microseconds, adjust precision /1000 for milliseconds etc.
Just don't forget to synchronize PostgreSQL and C++ timestamp length (eg. 8byte - 8byte each side) otherwise, your time will be thresholded either side, and you will lose precision / get unexpected time.

how to call Sequence function from SQL into Informatica

I have a port 'Number_1' in expression transformation in Informatica. I connected the Number_1 port to a target sql table. I need to generate number for this port 'Number_1' every time i run the mapping starting from 1 till 999. once it reaches 999 then again the value of the Number_1 should reset to 1. I'm aware there is sequence generator trans. but i need to call Sequence function from SQL server. how to achieve above?
Create a stored procedure in sql server then use stored procedure tranformation to call this from Informatica
You may call it using Stored Procedure transfomation. You may also use a stored procedure as SQL Override on Source Qualifier, however...
I hope you know what you're doing, as this is in general a very bad idea. Each call requires communication between Intergration Service and database - this will cause huge delays. Therefore it's much better to use Informatica's Sequence Generator or - even better perhaps, if all you need is an integer port with round robin - a simple expression variable.
While maciejg says makes a lot of sense performance wise - however I've known a fair few people who were more comfortable using the native database sequencer than the inbuilt one ( even some Informatica specialists ).
The thing with Informatica sequencer is how much flexibility they give and when they get set wrong it can lead to unexpected numbers being picked.
One example I have is for a sequencer being used to create unique keys in a table - if you persist the value between sessions then it works fine until you select the incorrect option while reimporting the mapping.
If you choose to lookup your previous ending value from a config file / table and add the value produced by the sequencer to this then one day when someone by mistake sets the sequencer to persist values between runs you'll all of a sudden skip that many numbers in the sequence each time the session is restarted. Native db sequencers are very basic making them very predictable and fool proof

Cannot bind a SQL_TYPE_TIMESTAMP value using ODBC with MS SQL server (HY104: Invalid precision value)

I am trying to bind a SQL_TYPE_TIMESTAMP value using ODBC to DATETIME and/or TIMESTAMP columns in a MS SQL Server database and it's failing with the following error:
[HY104] (native 0): [Microsoft][ODBC Driver 11 for SQL Server]Invalid precision value
Does anyone know what the problem is? I am binding the value like this:
TIMESTAMP_STRUCT* tval = getTimestampFromSomewhere();
SQLRETURN ret = SQLBindParameter(stmt, column, SQL_PARAM_INPUT, SQL_C_TYPE_TIMESTAMP, SQL_TYPE_TIMESTAMP, 29, 9, tval, sizeof(TIMESTAMP_STRUCT), 0);
ColSize is set to 29 because according to Column Size docs, for the TIMESTAMP type it should be:
20+s (the number of characters in the yyyy-mm-dd hh:mm:ss[.fff...] format, where s is the seconds precision)
and seconds precision (the precision of the fraction field) is 9 because:
The value of the fraction field is the number of billionths of a second and ranges from 0 through 999,999,999. (source)
DecimalDigits is set to 9 because for all datetime types except SQL_TYPE_DATE it's
The number of digits to the right of the decimal point in the seconds part of the value (fractional seconds). (source)
According to answer to this question, it shouldn't be possible to insert a value into a TIMESTAMP column, but it does not work with DATETIME column for me either. And IMO the problem with TIMESTAMP should happen when actually executing the command, not when just binding the values. Therefore I think this is something else.
The same code is working for TIMESTAMP column + SQL_TYPE_TIMESTAMP with PostgreSQL 9.2.15 (driver "PostgreSQL Unicode" 9.3.300),
and for DATETIME and TIMESTAMP columns + SQL_TYPE_TIMESTAMP with MySQL 5.5.50 (driver "MySQL ODBC 5.3 Unicode Driver" 5.3.6).
By the way, I am running Xubuntu 16.04 64bit, the SQL Server's version is 12.0.2569 (running on Windows 10) and I am using "ODBC Driver 11 for SQL Server" version 11.0.2270.
Try setting precision to 3 for Sql Server and its datetime columns. They only have a precision of 3. You can easily check this using SQL Server Management Studio and try to set a value with a precision higher than 3 - you will get no error, but only 3 digits of the fraction will be stored.
Note that there is SQLDescribeParam function, which will return information about the decimal digits and precision values for a parameter in a query: https://msdn.microsoft.com/en-us/library/ms710188(v=vs.85).aspx
Update, about the question from the comment: You have to specify the fractional part in the full range from 1 to 999'999'9999. But, if I remember this right, Sql Server for example with a precision of '3' will only accept values where the last 6 digits of the fractional part are 0s.
Sample: It will work fine for a fractional part of 111'000'000 (which would correspond to 111'000'000 / 1'000'000'000 seconds), but setting a fractional part of 111'100'000 will fail with an error about invalid fraction or similar.

can string longer than 255 bytes be used as ODBC prepared statement parameter value?

I am using the libodbc++ ODBC wrapper for C++, designed similar to JDBC. I have a prepared statement "INSERT INTO t1 (col1) VALUES (?)", where t1.col1 is defined as VARCHAR(500).
When I call statement->setString(1, s), the value of s is truncated to 255. I suspect the libodbc++ library, but as I am not very familiar with ODBC I'd like to be sure that the wrapper doesn't just expose a restriction of the underlying ODBC. The ODBC API reference is too complicated to be skimmed quickly and frankly I really don't want to do that, so pardon me for asking a basic question.
NOTE: an un-prepared and un-parameterized insert statement via the same library inserts a long value ok, so it isn't a problem of the MySql DB.
For long string, use PreparedStatement::setAsciiStream() instead of PreparedStatement::setString().
But when use stream, I often encounter error "HY104 Invalid Precision Value", which is annoying because I have no idea how to tackle it head on, however I work around it with following steps:
1, order the columns in SQL statement, non-stream columns go first;
2, if that doesn't work, split the statement to multiple ones, update or query a single column per statement.
But(again), in order to insert a row first and then update some columns in stream manner, one may have to get the last insert id, which turns out to be another challenge which I again failed to tackle head on for now...
I don't know libodbc++, but PreparedStatements available via ODBC API can store more characters. I use it with Delphi/Kylix ODBC wrapper.
Maybe there is some configuration in libodbc++ to set value length limit? I have such setting in my Delphi library. If you use PreparedStatement then you can allocate big chunk of memory, divide it into fields and show ODBC where block for each column starts and how long is it via SQLBindParameter() function.

how to excute a simple query use sqlapi++ with oracle

here is the code:
cmd1.setCommandText("select * from lp.human_tb_meta_sex");
cmd1.Execute();
while (cmd1.FetchNext())
{
SAString sas=cmd1.Field("id").asString();
cout<<"sas id:"<
it gave me ORA-00932 error...I dont know why..?
Presumably "id" is the primary key.
If it is defined as a NUMBER in the database, that could include fractions (eg 3.5).
If you define it as NUMBER(10,0) then it will always be an integer.
Since you are trying to pull it out as a String [.asString()] there could be a conversion issue.