Skip to Main Content
  • Questions
  • Formatting negative values to sort correctly but keep the formatting

Breadcrumb

Question and Answer

Connor McDonald

Thanks for the question, David.

Asked: November 09, 2017 - 5:16 pm UTC

Last updated: November 13, 2017 - 3:20 pm UTC

Version: ORACLE 12c

Viewed 1000+ times

You Asked

I have an old and a new query. I need help with the new one. The old query works fine. For the new one, I can't seem to find a way to format two columns (latitude and longitude, I need 6 digits after the decimal) in such a way as they sort correctly.

The is the old query which works correctly, the 'to_number' function in the ORDER BY works in this SQL but I get errors when trying it in the new SQL probably because of the UNION:

SELECT a.platformid,
TRIM(to_char(a.latitude,'90.0000000')) as latitude,
TRIM(to_char(a.longitude,'990.0000000')) as longitude,
a.month,
TRIM(to_char(a.watertemperature, '990.00')) as watertemperature,
TRIM(to_char(a.watersalinity, '990.000')) as watersalinity,
TRIM(to_char(a.currentspeed, '9990.00')) as currentspeed,
a.waterobsdepth
FROM OCEAN a
INNER JOIN SURFACE b
ON a.platformid = b.platformid
AND a.latitude = b.latitude
AND a.longitude = b.longitude
WHERE b.insertiontime >= TO_DATE ('20171107100000', 'yyyymmddhh24miss')
AND b.insertiontime <= TO_DATE ('20171108100000', 'yyyymmddhh24miss')
AND b.distributionCd = 'A'
ORDER BY a.platformid, to_number(a.latitude), to_number(a.longitude), a.waterobsdepth

This is the NEW query which does produce the correct data, but the latitude and longitude for the negative values sorts incorrectly because they are characters. The reason for the new query is to include data from the QUALITY table that was added after the original date. I have tried using to_number but get various errors. I tried not having any formatting in the subqueries and only formatting in the top select, but I get errors for not matching the UNION. I have tried using to_number to format the number but it doesn't keep the 6 digits after the decimal point. Any ideas? The longitude and latitude columns are DATA TYPE = NUMBER (12,7) in the tables.
If I do no formatting with to_char I don't get the 6 digits after the decimal point.

SELECT tb1.platformid,
tb1.latitude,
tb1.longitude,
tb1.month,
tb1.watertemperature,
tb1.watersalinity,
tb1.currentspeed,
tb1.waterobsdepth
FROM
(SELECT a.platformid,
TRIM(to_char(a.latitude, '90.0000000')) as latitude,
TRIM(to_char(a.longitude, '990.0000000')) as longitude,
a.month,
TRIM(to_char(a.watertemperature, '990.00')) as watertemperature,
TRIM(to_char(a.watersalinity, '990.000')) as watersalinity,
TRIM(to_char(a.currentspeed, '9990.00')) as currentspeed,
a.waterobsdepth
FROM OCEAN a, SURFACE b
WHERE b.insertiontime >= TO_DATE ('20171107100000', 'yyyymmddhh24miss')
AND b.insertiontime <= TO_DATE ('20171108100000', 'yyyymmddhh24miss')
AND a.platformid = b.platformid
AND a.latitude = b.latitude
AND a.longitude = b.longitude
AND b.distributionCd = 'A') tb1
UNION
(SELECT c.platformid,
TRIM(to_char(c.latitude, '90.0000000')) as latitude,
TRIM(to_char(c.longitude, '990.0000000')) as longitude,
c.month,
TRIM(to_char(c.watertemperature, '990.00')) as watertemperature,
TRIM(to_char(c.watersalinity, '990.000')) as watersalinity,
TRIM(to_char(c.currentspeed, '9990.00')) as currentspeed,
c.waterobsdepth
FROM OCEAN c, QUALITY d where d.changedate >= to_date('20171107100000','yyyymmddhh24miss')
and d.changedate <= to_date('20171108100000','yyyymmddhh24miss')
and d.observationtime > (to_date('20171107100000','yyyymmddhh24miss') - 30)
and c.platformid = d.platformid
and c.latitude = d.latitude
and c.longitude = d.longitude
and d.distributionCd = 'A')
ORDER BY platformid, latitude, longitude, waterobsdepth;

and Connor said...

All you need to do is defer your formatting until the final query, eg

SQL> create table t ( x number );

Table created.

SQL> insert into t values (1.123);

1 row created.

SQL> insert into t values (2.456);

1 row created.

SQL> insert into t values (3.123);

1 row created.

SQL>
SQL>
SQL> create table t1 ( x number );

Table created.

SQL> insert into t1 values (10.234);

1 row created.

SQL> insert into t1 values (20.324);

1 row created.

SQL> insert into t1 values (30.321);

1 row created.

-- base data sorts fine

SQL>
SQL> select *
  2  from
  3   ( select x from t
  4     union
  5     select x from t1
  6   )
  7  order by 1;

         X
----------
     1.123
     2.456
     3.123
    10.234
    20.324
    30.321

6 rows selected.

-- strings do not

SQL>
SQL>
SQL> select *
  2  from
  3   ( select to_char(x,'fm99.99') char_version from t
  4     union
  5     select to_char(x,'fm99.99') char_version from t1
  6   )
  7  order by 1;

CHAR_V
------
1.12
10.23
2.46
20.32
3.12
30.32

6 rows selected.

-- so do string conversion last

SQL>
SQL>
SQL> select to_char(x,'fm99.99') char_version
  2  from
  3   ( select x from t
  4     union
  5     select x from t1
  6   )
  7  order by x;

CHAR_V
------
1.12
2.46
3.12
10.23
20.32
30.32

6 rows selected.

SQL>


Now, if there is the potential for duplicates to occur in your UNION, eg if you had in the source data

20.321
20.322

which would then appear as dups due to the formatting, you can use the ROUND/CEIL/TRUNC/etc functions to get the data cleansed in the inner queries.

Rating

  (1 rating)

Is this answer out of date? If it is, please let us know via a Comment

Comments

Formatting and Sorting after a UNION

David, November 13, 2017 - 2:15 pm UTC

Thank you for such a quick response. I was pulling my hair out. Such a simple thing once it is explained.
Thank you. These types of results will help a lot of people and impact major decisions.
Connor McDonald
November 13, 2017 - 3:20 pm UTC

Glad we could help

More to Explore

PL/SQL demos

Check out more PL/SQL tutorials on our LiveSQL tool.

PL/SQL docs

PL/SQL reference manual from the Oracle documentation library