Convert differences between SQL Server 2008 R2 and 2012 - sql-server-2008-r2

I've got an issue while executing a SQL statement in SQL Server 2012 while it's perfectly working in a SQL Server 2008 R2...
The error message is :
Msg 8114, Level 16, State 5, Line 1
Error converting data type varchar
to numeric.
I want to execute this SQL Select statement :
Select
count(*)
from IMPORTBM
inner join ATTRIBUTE on ATT_ATTRIBUTE_ID = IMP_ATTRIBUTE_ID
where IMP_LOCATION_ID = 2
AND IMP_SERIAL_ID = 310001
AND IMP_VERSION_ID = 1
AND (
(ATT_ATTRIBUTE = 'PS_APISizing'
AND IMP_VALUE = 'C')
OR
(ATT_ATTRIBUTE = 'DTD'
AND ISNUMERIC(IMP_VALUE) = 1
AND CAST( IMP_VALUE as NUMERIC(38,19)) <= 0.469)
OR
(ATT_ATTRIBUTE = 'IOD'
AND ISNUMERIC(IMP_VALUE) = 1
AND CAST( IMP_VALUE as NUMERIC(38,19)) BETWEEN 3.684 AND 4.225)
)
Could you help me finding out why it's not working with SQL Server 2012 please ?

The problem is that conditions are not always evaluated in the order you place them. This means that Cast(IMP_VALUE as numeric(38, 19)) can fail when it is a non-numeric value, if the query execution plan happens to evaluate this before the IsNumeric(IMP_VALUE) = 1.
This is not determined by how many clauses there are or what version of SQL Server you're running--those are only coincidences. The problem is exactly as I described. The fact that you get different results with different queries is due to different queries, or different servers, using different execution plans.
The cure is to make sure that all conditions will not throw an error, no matter what order they are executed in.
For all versions of SQL Server, you can do this:
Convert(
numeric(38,19),
CASE WHEN IsNumeric(IMP_VALUE) = 1 THEN IMP_VALUE ELSE NULL END
) <= 0.469
Or, in SQL Server 2012 and up, you can use Try_Convert:
Try_Convert(numeric(38,19), IMP_VALUE) <= 0.469
If your server complains that this is not a built-in function name, then it is likely that your database was upgraded from a prior version and you need to alter the database compatibility level. Note that doing this could have some other effects in the system that you need to consider, so study up on it first, and try it out in a non-production system before doing it in production (or do it during a non-critical period and set the compatibility level back if you run into any issues). The danger is not that any damage will occur, but that query results could be different or errors could occur (different ordering of query or stored procedure results being a more common occurrence).
ALTER DATABASE database_name
SET COMPATIBILITY_LEVEL = 110; -- SQL Server 2012

What is the definition of the table? (IMP_VALUE at least). Why not store numeric in the 1st place...
2008 and 2012 query plan probably differ and does not handle the parameter and condition in the same order.
With SQL Server 2012 (compatibility level 110) you can try to replace the 2 cast:
AND ISNUMERIC(IMP_VALUE) = 1
AND CAST( IMP_VALUE as NUMERIC(38,19)) <= 0.469)
by
AND TRY_CONVERT(NUMERIC(38,19), IMP_VALUE) <= 0.469
AND TRY_CONVERT(NUMERIC(38,19), IMP_VALUE) BETWEEN 3.684 AND 4.225
It returns NULL without error when it cannot convert it.

Related

DB2 - If table is empty for date X, insert, else go on

--DB2 version 10 on AIX
I have a stored procedure, which I need to update. And want to check if there is data for a certain date. If data exists, go on, else run insert and then go on.
IF (SELECT COUNT(*)
FROM SCHEMA1.TABLE1_STEP1
WHERE XDATE = '9/27/2014' < 1)
THEN (INSERT INTO SCHEMA1.TABLE1_STEP1 (SELECT * FROM SCHEMA2.TABLE2 FETCH FIRST 2 ROWS ONLY))
END IF;
This errors-out.
DB2 Database Error: ERROR [42601] [IBM][DB2/AIX64] SQL0104N An unexpected token "(" was found following "/2014') < 1) THEN". Expected tokens may include: "". SQLSTATE=42601
Any thoughts on what's wrong?
I'm guessing you probably want the less than sign outside of the parenthesis...
However, as an aside, you can also do this kind of statement without an IF (although, I don't have an AIX DB2 available to check for sure. It worked on DB2 for z/OS and LUW, however):
INSERT INTO SCHEMA1.TABLE1_STEP1
SELECT *
FROM SCHEMA2.TABLE2
WHERE NOT EXISTS (
SELECT *
FROM SCHEMA1.TABLE1_STEP1
WHERE XDATE = '9/27/2014'
)
FETCH FIRST 2 ROWS ONLY
Also, you're not providing an ORDER BY on the SCHEMA2.TABLE2 select, so your results could come back in any order (whatever is "easiest" for the database engine)... order is not guaranteed unless you provide the ORDER BY statement.

Query referencing aliased column in order by in SQLServer 2012

I have a SQL Select query that's embedded in a piece of C# code which I don't want to change. My problem is that the query executes fine on SQLServer 2008 but not 2012.
The offending line of code is:
Select code as SiteCode from TimeSheetContracts S order by S.SiteCode
Executed in a database on SQL2008 it works fine. The same database upgraded to SQLServer 2012 errors with the following...
Msg 207, Level 16, State 1, Line 2
Invalid column name 'SiteCode'.
If I edit the query to be
Select code as SiteCode from TimeSheetContracts S order by SiteCode
it works fine. Can anyone explain this?
There is no column in TimeSheetContracts called SiteCode, so a reference to s.SiteCode is not valid. Aliasing in ORDER BY is a little more strict since SQL Server 2000, when the syntax was a little more forgiving. The only way s.SiteCode would have worked on your SQL Server 2008 instance was if your database was in COMPATIBILITY_LEVEL = 80 (go ahead and try it on a different database that is 90 or greater). Once you move to SQL Server 2012, 80 is no longer an option. On a 2005, 2008 or 2008 R2 instance, try this:
CREATE DATABASE floob;
GO
USE floob;
GO
CREATE TABLE dbo.SalesOrderHeader(SalesOrderID INT);
GO
SELECT SalesOrderID AS ID FROM dbo.SalesOrderHeader AS h ORDER BY h.ID; -- fails
GO
ALTER DATABASE floob SET COMPATIBILITY_LEVEL = 80;
GO
SELECT SalesOrderID AS ID FROM dbo.SalesOrderHeader AS h ORDER BY h.ID; -- works
GO
USE master;
GO
DROP DATABASE floob;
If you want to use the column alias, you'll need to (and should always have been) just use the alias. If you want to use the table alias prefix, you'll need to use s.code.

SQL Server OpenQuery() behaving differently then a direct query from TOAD

The following query works efficiently when run directly against Oracle 11 using TOAD (with native Oracle drivers)
select ... from ... where ...
and srvg_ocd in (
select ocd
from rptofc
where eff_endt = to_date('12/31/9999','mm/dd/yyyy')
and rgn_nm = 'Boston'
) ...
;
The exact same query "never" returns if passed from SQL Server 2008 to the same Oracle database via openquery(). SQL Server has a link to the Oracle database using an Oracle Provider OLE DB driver.
select * from openquery( servername, '
select ... from ... where ...
and srvg_ocd in (
select ocd
from rptofc
where eff_endt = to_date(''12/31/9999'',''mm/dd/yyyy'')
and rgn_nm = ''Boston''
) ...
');
The query doesn't return in a reasonable amount of time, and the user kills the query. I don't know if it would eventually return with the correct result.
This result where the direct TOAD query works efficiently and the openquery() version "never" returns is reproducible.
A small modification to the openquery() gives the correct efficient result: Change eff_endt to trunc(eff_endt).
That is well and good, but it doesn't seem like the change should be necessary.
openquery() is supposed to be pass through, so how can there be a difference between the TOAD and openquery() behavior?
The reason we care is because we frequently develop complex queries with TOAD directly accessing Oracle. Once we have the query functioning and optimized, we convert it to an openquery() string for use in a SQL Server application. It is extremely aggravating to have a query suddenly fail with openquery() when we know it worked as a direct query. Then we have to search for a work-around through trial and error.
I would like to see the Oracle trace files for the two scenarios, but the Oracle server is within another organization, and we are not getting cooperation from the Oracle DBAs.
Does anyone know of any driver, or TOAD, or ??? issues that could account for the discrepancy? Is there any way to eliminate the problem such that both methods always give the same result?
I know you asked this a while ago but I just came across your question.
I agree, they should be the same. Obviously there is a difference. We need to find out where the difference is.
I am thinking out loud as I type...
What happens if you specify just a few column instead of select * from openquery?
How many rows are supposed to be returned?
What if, in the oracle select, you limit the returned rows?
How quickly does the openquery timeout?
Are TOAD and SS on the same machine? Are you RDPing into the SS and running toad from there?
Are they using the same drivers? including bit? (32/64) version?
Are they using the same account on oracle?
It is interesting that using the trunc() makes a difference. I assume [eff_endt] is one of the returned fields?
I am wondering if SS is getting all the rows back but it is choking on doing the date conversions. The date type in oracle may need to be converted to a ss date type before ss shows it to you.
What if you insert the rows from the openquery into a table where the date field is just a (n)varchar. I am thinking ss might just dump the date it is getting back from oracle into that text field without trying to convert it.
something like:
insert into mytable(f1,f2,f3,datetimeX)
select f1,f2,f3,datetimeX from openquery( servername, '
select f1,f2,f3,datetimeX from ... where ...
and srvg_ocd in (
select ocd
from rptofc
where eff_endt = to_date(''12/31/9999'',''mm/dd/yyyy'')
and rgn_nm = ''Boston''
) ...
');
What if toad or ss is modifying the query statement before sending it to oracle. You could fire up wireshark and see what toad and ss are actually sending.
I would be very curious if you get this resolved. I link ss to oracle often and have not run into this issue.
Here are basic things you can check for to see what the database is doing after it receives the query. First, check that the execution plans are the same in TOAD as when the query runs using openquery. You could plan the query yourself in TOAD using:
explain plan set statement_id = 'openquery_test' for <your query here>;
select *
from table(dbms_xplan.display(statement_id => 'openquery_test';
then have someone initiate the query using openquery() and have someone with permissions to view v$ tables to run:
select sql_id from v$session where username = '<user running the query>';
(If there's more than one connection with the same user, you'll have to find an additional attribute to isolate the row representing the session running the query.)
select *
from table(dbms_xplan.display_cursor('<value from query above'));
If those look the same then I'd move on to checking database waits and see what it's stuck on.
select se.username
, sw.event
, sw.p1text
, sw.p2text
, sw.p3text
, sw.wait_time_micro/1000000 as seconds_in_wait
, sw.state
, sw.time_since_last_wait_micro/1000000 as seconds_since_last_wait
from v$session se
inner join
v$session_wait sw
on se.sid = sw.sid
where se.username = '<user running the query>'
;
(again, if there's more than one session with the same username, you'll need another attribute to whittle it down to the one you're interested in.)
If the plans are different, then you need to find out why, or if they're the same, look into what it's waiting on (e.g. SQL*Net message to client ?) and why.
I noticed a difference using OLEDB through MS Access (2013) connecting to Oracle 10g & 11g tables, in that it did not always recognize indexes or primary keys on the Oracle tables properly. The same query through an MS Access 2000 database (using odbc) worked fine / had no problem with indexes & keys. The only way I found to fix the OLEDB version was to include all of the key fields in the SELECT -- which was not a satisfying answer, but it's all I could find. This might be an option to try through SSMS / OpenQuery(...) as well.
Besides that... you can try some alternatives to OPENQUERY, such as:
4-part names: SELECT ... FROM Server..Schema.Table
Execute AT: EXEC ('select...') at linked server
But as for why the OLEDB provider works differently than the native Oracle Provider -- the providers are not identical, and the native provider would be more likely to pave-over Oracle quirks than the more generic OLEDB provider would.

Checksum Validation after migration from Oracle to SQL Server

I am migrating a large database from oracle 11g to SQL SERVER 2008R2 using SSIS. How can the data integrity can be validated for numeric data using checksum?
In the past, I've always done this using a few application controls. It should be something that is easy to compute on both platforms.
Frequently, the end result is a query like:
select count(*)
, count(distinct col1) col1_dist_cnt
...
, count(distinct col99) col99_dist_cnt
, sum(col1) col1_sum
...
, sum(col99) col99_sum
from table
Spool to file, Excel or database and compare outcome. Save for project management and auditors.
Please read more on application control here. I wrote it for checks between various financial reporting systems for the regulatory reporting, so this approach serves most cases.
If exactly one field value is wrong, it will always show up. Two errors might compensate each other. For example row 1 col 1 gets the value from row 2 col 1.
To detect for that, multiply each value with something unique for the row. For instance, if you have a unique ID column or identity that is included in the migration too:
, sum(ID * col1) col1_sum
...
, sum(ID * col99) col99_sum
When you get number overflows, first try using the largest available precision (especially on SQL Server sometimes difficult). If not feasibly anymore, use mod function. Only few types of error are hidden by mod.
Icing on the cake is to auto generate these statements. On Oracle look at user_tables, user_tab_columns. On SQL Server look at syscolumns etc.

Postgres SQL operator does not exist; character = integer;

I am do replicating of two database (SQL Server 2000 and PostgreSQL). I use http://blog.hagander.net/archives/103-Replicating-from-MS-SQL-Server-to-PostgreSQL.html for this. Then I do last step the
ERROR: operator does not exist: character = integer; Error executing the query
appeared. I use the PostgreSQL 8.4.6 for that and ODBC drivers (all psqlodbc_08_04_0100.zip, psqlodbc_08_04_0200.zip) from here i also try to delete and install version that Synaptic called 9.0.2-1 and update odbc drivers i try (psqlodbc_09_00_0100.zip, psqlodbc_09_00_0101.zip, psqlodbc_09_00_0200.zip) it also return that error. The query launched from delphi where i use only System DSN runs normally
You need to fix your SQL statement.
I bet you have something like
WHERE character_column = 1
and you need to change that to
WHERE character_column = '1'
use single quote 'your_value' for non numeric data and double quote for column name and never the opposite.
select status, sum(amount) as sum from "sales" where ("date" <= '2017-04-30 23:59:59' and "customer_id" = 1) and "sales"."deleted_at" is null group by "status"