My organization is currently testing a new version of a vendor's software. The most significant change as far as the database goes is the upgrade from SQL Anywhere 9 (9.0.2.3924) to SQL Anywhere 11 (11.0.1.2538). For the most part, everything works great, but there is an issue I am having trouble diagnosing.

SQLA9 and SQLA11 treat this scenario differently and I am wondering if someone could explain why or point me to documentation to help. It's easiest explained by a stripped-down example:

-- Create table
CREATE TABLE mytable ( amount CHAR(10) NOT NULL );

-- Insert test data
INSERT INTO mytable ( amount ) VALUES ( 20000 );
INSERT INTO mytable ( amount ) VALUES ( 10000 );
INSERT INTO mytable ( amount ) VALUES ( 0 );
INSERT INTO mytable ( amount ) VALUES ( 'Included' );
INSERT INTO mytable ( amount ) VALUES ( -1000 );

At this point, both databases return the same data, of course:

-- Lookie at all the data
SELECT * FROM mytable;

    SQLA9               SQLA11
amount              amount
'20000'             '20000'
'10000'             '10000'
'0'                 '0'
'Included'          'Included'
'-1000'             '-1000'

I need to do numeric operations on the data that is actually numeric, so I use ISNUMERIC():

-- Select numeric entries
SELECT * FROM mytable WHERE ISNUMERIC( amount ) = 1;

    SQLA9               SQLA11
amount              amount
'20000'             '20000'
'10000'             '10000'
'0'                 '0'
'-1000'             '-1000'

One last requirement- The numbers I'm working with have to be positive numbers and non-zero:

-- Select numeric entries greater than zero
SELECT * FROM mytable WHERE ISNUMERIC( amount ) = 1 AND amount > 0;

    SQLA9               SQLA11
amount              amount
'20000'             '20000'
'10000'             '10000'

This is the data I want, so I will attempt to use SUM() to get the total:

-- Get the sum of non-zero numeric entries 
SELECT SUM( amount ) FROM mytable WHERE ISNUMERIC( amount ) = 1 AND amount > 0

    SQLA9               SQLA11
SUM( amount )       Cannot convert 'Included' to a numeric
'30000'             SQLCODE=-157, ODBC 3 State="07006"

The error is being thrown by "amount > 0" in SQLA11. Without it, the SUM() works. That predicate only appears to be an issue when using numeric aggregates (SUM, AVG, etc). The database must be applying the WHERE clause to the data first, in either scenario, so I am confused as to why this is happening. The SQLA9 behaviour seems correct, but maybe I'm missing something obvious.

Thanks in advance!

Notes:

  1. I can't change the schema :)
  2. The topic should probably be modified to something more sensible.

asked 21 Mar '11, 16:29

Ralph%20Wissing's gravatar image

Ralph Wissing
3153711
accept rate: 0%

edited 15 Mar '13, 18:10

Mark%20Culp's gravatar image

Mark Culp
22.5k9129264


SQL Anywhere's query execution engine was completely rewritten for version 10 and this is the reason why you are seeing the difference. SQL does not give you any guarentees on the order in which the predicates are computed. In your example, the executing engine has chosen to evaluate "amount > 0" prior to evaluating "isnumeric(amount) = 1".

This works in my simple tests:

SELECT SUM( if isnumeric( amount ) = 1 then cast( amount as int ) else 0 endif ) as total
  FROM mytable 
 WHERE ( case 
         when ISNUMERIC( amount ) = 1 then
            ( if amount > 0 then 1 else 0 endif )
         else 0
         end ) > 0;

I needed to "hide" the amount value in the SUM operator inside the IF clause so that the execution engine did not attempt to (prematurely) evaluate the column as numeric while reading the column from the row.

permanent link

answered 21 Mar '11, 17:04

Mark%20Culp's gravatar image

Mark Culp
22.5k9129264
accept rate: 40%

edited 21 Mar '11, 17:09

That explains it, thanks Mark!

(22 Mar '11, 09:16) Ralph Wissing

Based on Mark's example, the following (somewhat shorter) query works with SA 12.0.1, too:

:::SQL
SELECT SUM(if isnumeric(amount) = 1
           then if cast(amount as int) > 0 then cast(amount as int) else 0 end if
           else 0 end if) as total
FROM mytable

It doesn't need a particular WHERE clause as for the if expression, since the following is guaranteed (as to the docs):

IF condition THEN expression1 [ ELSE expression2 ] { ENDIF | END IF }

expression1 is evaluated only if condition is TRUE. Similarly, expression2 is evaluated only if condition is FALSE.

As such, an IF expression (or an CASE expression) can be used when "short-circuited" evaluation (as in C/C++) is needed.

permanent link

answered 22 Mar '11, 04:36

Volker%20Barth's gravatar image

Volker Barth
29.5k291441646
accept rate: 32%

edited 22 Mar '11, 04:37

This works great too, thanks!

(22 Mar '11, 09:19) Ralph Wissing

I asked something along these lines wrt the order of results. The response I received indicated that the order of things that are equal is decided when the statement is processed, not necessarily by the order you type it.

I would guess that just because the isnumeric is first, it does not remove the rows it excludes from evaluation from the second condition's evaluation. 'Included' is not numeric, so it will return an error for > 0.

I've also seen it stated that storing numbers as text is generally a bad idea, but as you said you can't change the schema.

Since they are strings, this may work:

:::SQL

SELECT SUM( amount ) FROM mytable 
WHERE ISNUMERIC( amount ) = 1 AND amount not like '-%'

Just a guess.

permanent link

answered 21 Mar '11, 16:54

Siger%20Matt's gravatar image

Siger Matt
3.1k486493
accept rate: 13%

edited 21 Mar '11, 16:56

That's a pretty clever solution. It looks like it works for the examples I tested it with.

(22 Mar '11, 09:18) Ralph Wissing
Your answer
toggle preview

Follow this question

By Email:

Once you sign in you will be able to subscribe for any updates here

By RSS:

Answers

Answers and Comments

Markdown Basics

  • *italic* or _italic_
  • **bold** or __bold__
  • link:[text](http://url.com/ "title")
  • image?![alt text](/path/img.jpg "title")
  • numbered list: 1. Foo 2. Bar
  • to add a line break simply add two spaces to where you would like the new line to be.
  • basic HTML tags are also supported

Question tags:

×137
×95
×4

question asked: 21 Mar '11, 16:29

question was seen: 2,229 times

last updated: 15 Mar '13, 18:10