13

Handling Numbers

In Chapter 11, "Manipulating Strings," you learned about the use of strings and string functions in Personal Oracle7. In Chapter 12, "Dealing with Dates," you explored the advantages of the DATE datatype and the relevant Oracle built-in functions. The topic of this chapter is the use of numeric datatypes in Personal Oracle7.

Specifying a Numeric Column

A column's datatype is specified in the CREATE TABLE and ALTER TABLE statements. The general syntax for specifying a numeric datatype is

NUMBER ([precision [, scale]])

where precision is an optional argument that specifies the number of digits of precision that Oracle should store for column values and scale is an optional argument indicating the number of digits to the right of the decimal point that Oracle should store for column values.

If you don't specify precision or scale, Oracle accepts a number of up to 38 digits of precision --;the maximum precision that Oracle offers. When you specify a column, consider limiting the width of numeric values by using an appropriate precision. For example, if a column stores a patient's body temperature in degrees Fahrenheit, you would specify the column as

Body_Temp_F Number(4,1)

A precision of 4 and a scale of 1 allow Body_Temp_F to store a total of four digits, including one digit to the right of the decimal point. As a result, Oracle accepts the following values:

SQL> update Patient

  2  set Body_Temp_F = 99.2

  3  where

  4  Patient_ID = 'A2002';

1 row updated.

SQL> update Patient

  2  set Body_Temp_F = 103.8

  3  where

  4  Patient_ID = 'E3893';

1 row updated.

The column definition prevents a bad value from accidentally being stored in the table.

SQL> update Patient

  2  set Body_Temp_F = 1003.8

  3  where

  4  Patient_ID = 'N3393';

set Body_Temp_F = 1003.8

                *

ERROR at line 2:

ORA-01438: value larger than specified precision allows for this column

Of course, this definition for Body_Temp_F allows values up to 999.9 degrees Fahrenheit--;an impossible value for humans. In addition to specifying the precision and scale, you also need to specify a CHECK constraint for this column to restrict its values to a range. I explain how to use constraints in Chapter 14, "Defining Table and Column Constraints."

If you specify a value for precision but not for scale, Oracle truncates the fractional value of a real number before storing the value in the column. Consider the following examples:

SQL> create table Number_Demo (

  2  Int_Value    number(3),

  3  Real_Value   number(3,1),

  4  Num_Value    number);

Table created.

SQL> insert into Number_Demo

  2  (Int_Value)

  3  values

  4  (1234);

(1234)

 *

ERROR at line 4:

ORA-01438: value larger than specified precision allows for this column

SQL> insert into Number_Demo

  2  (Int_Value)

  3  values

  4  (12.2);

1 row created.

SQL> select Int_Value

  2  from Number_Demo;

INT_VALUE

---------

       12

If values for precision and scale have been furnished and you store a numeric value whose scale exceeds the column's scale, Oracle truncates the fractional value to the column's scale.

SQL> insert into Number_Demo

  2  (Real_Value)

  3  values

  4  (3.144);

1 row created.

SQL> select Real_Value

  2  from Number_Demo;

REAL_VALUE

----------

       3.1

In addition to the NUMBER datatype, Oracle accepts the following keywords that describe a numeric column.

Oracle supports these other datatypes to provide compatibility with ANSI SQL and other relational database systems such as IBM SQL/DS and DB2. The NUMERIC, DECIMAL, and DEC datatypes are identical to the NUMBER datatype. INTEGER, INT, and SMALLINT are translated to NUMBER(38). FLOAT, DOUBLE PRECISION, and REAL are all translated to NUMBER. Unless you are converting a database schema from some non-Oracle database, you should generally use the NUMBER datatype when specifying columns.

How Oracle Stores Numbers

Oracle doesn't store numbers in the manner used by programming languages such as C and FORTRAN. For example, in C a floating-point variable requires the same amount of storage regardless of its value, whereas in Oracle the number of bytes used to store a number depends on the number's precision. To illustrate this fact, you can use an Oracle built-in function called VSIZE, which returns the number of bytes used by its argument.

The following code fragment is a query of a table that contains a number column named Num_Value. To the right of the column value is VSIZE(Num_Value)--;which returns the number of bytes used to store Num_Value. Oracle can store two digits of precision in one byte. Another byte is used for storing the sign and exponent.

SQL> select Num_Value, vsize(Num_Value)

  2  from Number_Demo;

NUM_VALUE VSIZE(NUM_VALUE)

--------- ----------------

      123                3

     1234                3

    12345                4

   123456                4

  1234567                5

 12345678                5

123456789                6

12345.679                6

Converting a Number to a String

You will typically want to convert a numeric value to a string value for two reasons:

Automatic Conversion of a Number to a String

In some situations SQL automatically converts a number to a string. For example, if you perform an INSERT or UPDATE, Oracle converts a specified number to a character value if it is being stored in a VARCHAR2 column.

SQL> insert into Product

  2  (Product_ID, Manufacturer_ID, Initial_Retail_Value)

  3  values

  4  ('D1001',1001,500);

1 row created.

If you look at the row that was just inserted, you can see that the number 1001, which was used to specify a value for Manufacturer_ID, has been converted to a VARCHAR2 value of 1001, which is stored in the Manufacturer_ID column.

SQL> select Product_ID, Manufacturer_ID, Initial_Retail_Value

  2  from Product

  3  where

  4  Product_ID = '&Product_ID';

Enter value for product_id: D1001

old   4: Product_ID = '&Product_ID'

new   4: Product_ID = 'D1001'

PRODUCT_ MANUFA INITIAL_RETAIL_VALUE

-------- ------ --------------------

D1001    1001                    500

Using TO_CHAR to Convert a Number to a String

The TO_CHAR function is used to explicitly convert a number to a string. Its syntax is

TO_CHAR (number [,format])

where number is the numeric expression to be converted and format is the optional format model to be used by TO_CHAR.

Here's an example of using TO_CHAR without a format. Notice that the first column--;Real_Value--;is right-justified by SQL*Plus, whereas the second column--;to_char(Real_Value)--;is left-justified by SQL*Plus because it is a character column.

SQL> select Real_Value, to_char(Real_Value)

  2  from Number_Demo;

REAL_VALUE TO_CHAR(REAL_VALUE)

---------- ----------------------------------------

       3.1 3.1

This is an appropriate point to discuss the Oracle number format model. The following code segments show the most important format model elements--;the ones that you'll rely on most often.

To specify the number of digits to display, use 9 for each digit. You also can add a comma and decimal point to the specified format.

SQL> select Product_ID, to_char(Initial_Retail_Value,'9,999.99')

  2  from Product

  3  order by Product_ID;

PRODUCT_ID   TO_CHAR(I

------------ ---------

A2001           350.00

A504            585.00

A509            850.00

A903          1,050.00

B311            185.00

To display a number with leading zeros, use 0 at the beginning of the format.

SQL> select Product_ID, to_char(Initial_Retail_Value,'0,999.99')

  2  from Product

  3  order by Product_ID;

PRODUCT_ID   TO_CHAR(I

------------ ---------

A2001         0,350.00

A504          0,585.00

A509          0,850.00

A903          1,050.00

B311          0,185.00

To display a leading dollar sign, begin the format with a $.

SQL> select Product_ID, to_char(Initial_Retail_Value,'$9,999.99')

  2  from Product

  3  order by Product_ID;

PRODUCT_ID   TO_CHAR(IN

------------ ----------

A2001           $350.00

A504            $585.00

A509            $850.00

A903          $1,050.00

B311            $185.00

To enclose a negative value in angle brackets, add PR to the format.

SQL> select Symbol, to_char(Last_qtr_EPS,'$99.99PR')

  2  from Security_Price

  3  order by Symbol;

SYMBO TO_CHAR(

----- --------

ABC     <$.58>

ACME    $1.81

ZGEGE   <$.18>

To return the sign of each value, add S to the format.

SQL> select Symbol, to_char(Last_qtr_EPS,'S99.99')

  2  from Security_Price

  3  order by Symbol;

SYMBO TO_CHA

----- ------

ABC     -.58

ACME   +1.81

ZGEGE   -.18

Finally, if you want a number to appear in scientific notation, follow the specified precision with EEEE.

SQL> select Num_Value, to_char(Num_Value,'9.9999EEEE')

  2  from Number_Demo

  3  order by Num_Value;

NUM_VALUE TO_CHAR(NUM_

--------- ------------

      123   1.2300E+02

     1234   1.2340E+03

    12345   1.2345E+04

12345.679   1.2346E+04

Converting a String to a Number

The TO_NUMBER function is the converse of TO_CHAR: It converts a character expression to a number by specifying a format. The syntax for TO_NUMBER is

TO_NUMBER (string [,format])

where string is the character expression to be converted and format is the optional format model to be used by TO_NUMBER.

TO_NUMBER uses the same format model as TO_CHAR. As an example, here is how you would convert a string value, representing earnings per share, to a number.

SQL> update Security_Price

  2  set Last_Qtr_EPS = to_number('$2.81','$999.99')

  3  where

  4  Symbol = 'ZGEGE';

1 row updated.

SQL> select Symbol, Last_Qtr_EPS

  2  from Security_Price

  3  where

  4  Symbol = 'ZGEGE';

SYMBO LAST_QTR_EPS

----- ------------

ZGEGE         2.81

Using Statistical Built-In Functions

Oracle furnishes the following statistical functions that are actually group functions.

Because these are group functions, their use is described in detail in Chapter 15, "More Sophisticated Queries."

Rounding and Truncating Numbers

Oracle provides four built-in functions related to rounding and truncating fractional numbers.

ROUND(value,[scale])

TRUNC(value,[scale])

FLOOR(value)

CEIL(value)

In these functions value is a numeric expression, and scale is an optional argument indicating the number of digits that the function should use for rounding or truncating. (The default is 0.)

The following examples show how you can use each function.

ROUND

The ROUND function has two arguments: the numeric expression and an optional number of digits to be used for rounding. If the second argument isn't supplied, ROUND returns the value of its numeric argument rounded to the nearest integer. If the second argument is supplied, ROUND returns the value of its numeric argument rounded to the nearest fractional number with the specified number of digits to the right of the decimal point. ROUND can be used with literal values, as shown in the following example:

SQL> select round(123.2) from dual;

ROUND(123.2)

------------

         123

SQL> select round(123.27,1) from dual;

ROUND(123.27,1)

---------------

          123.3

SQL> select round(101.8) from dual;

ROUND(101.8)

------------

         102

The ROUND function also accepts numeric expressions.

SQL> select Current_Used_Value*0.87, Round(Current_Used_Value*0.87)

  2  from Product;

CURRENT_USED_VALUE*0.87 ROUND(CURRENT_USED_VALUE*0.87)

----------------------- ------------------------------

                  217.5                            218

                  339.3                            339

                 587.25                            587

                 717.75                            718

                  330.6                            331

TRUNC

The TRUNC function is similar to the ROUND function. However, instead of rounding to the nearest integer, TRUNC removes the fractional portion of its numeric argument. You can supply a literal number to TRUNC.

SQL> select trunc(123.33), trunc(123.567,2)

  2  from dual;

TRUNC(123.33) TRUNC(123.567,2)

------------- ----------------

          123           123.56

TRUNC also accepts numeric expressions.

SQL> select Current_Used_Value*0.87, Trunc(Current_Used_Value*0.87)

  2  from Product;

CURRENT_USED_VALUE*0.87 TRUNC(CURRENT_USED_VALUE*0.87)

----------------------- ------------------------------

                  217.5                            217

                  339.3                            339

                 587.25                            587

                 717.75                            717

                  330.6                            330

FLOOR

The FLOOR function is almost identical to the TRUNC function except that FLOOR cannot truncate to a fractional number. The FLOOR function returns the integer that is less than or equal to its numeric argument.

SQL> select floor(128.3), floor(129.8)

  2  from dual;

FLOOR(128.3) FLOOR(129.8)

------------ ------------

         128          129

The argument for FLOOR can also be a numeric expression.

SQL> select Current_Used_Value*0.87, Floor(Current_Used_Value*0.87)

  2  from Product;

CURRENT_USED_VALUE*0.87 FLOOR(CURRENT_USED_VALUE*0.87)

----------------------- ------------------------------

                  217.5                            217

                  339.3                            339

                 587.25                            587

                 717.75                            717

                  330.6                            330

CEIL

The CEIL function returns a ceiling integer for its numeric argument--;the smallest integer that is greater than or equal to its argument. CEIL can accept constants.

SQL> select ceil(128.3), ceil(129.8)

  2  from dual;

CEIL(128.3) CEIL(129.8)

----------- -----------

        129         130

The argument for CEIL can also be a numeric expression.

SQL> select Current_Used_Value*0.87, Ceil(Current_Used_Value*0.87)

  2  from Product;

CURRENT_USED_VALUE*0.87 CEIL(CURRENT_USED_VALUE*0.87)

----------------------- -----------------------------

                  217.5                           218

                  339.3                           340

                 587.25                           588

                 717.75                           718

                  330.6                           331

Finding the Largest or Smallest Value

You can use the MAX and MIN functions to retrieve the largest and smallest values for a particular column in a table. Technically, MAX and MIN are group functions. However, you aren't required to specify the SELECT statement's GROUP BY clause to use these functions. As an example, here is how you would retrieve the largest and smallest estimates for labor costs from the Depot Estimate table:

SQL> select min(Labor_Cost), max(Labor_Cost)

  2  from Depot_Estimate;

MIN(LABOR_COST) MAX(LABOR_COST)

--------------- ---------------

             56             202

The preceding query retrieves the smallest and largest values for the Labor_Cost column found in the entire Depot_Estimate table. But suppose you want to compare the value of two or more columns. Specifically, suppose that you need to find the larger cost--;labor or parts. In this case the MAX and MIN functions won't work because they each can accept only a single argument. However, two functions can serve this purpose: GREATEST and LEAST. The syntax for these functions is quite simple.

GREATEST(expression1, ... ,expressionN)

LEAST(expression1, ... ,expressionN)

where expression1 through expressionN are valid SQL expressions.

Using the GREATEST function, you submit the following query to retrieve whichever is larger--;Labor_Cost or Parts_Cost.

SQL> select greatest(Labor_Cost,Parts_Cost)

  2  from Depot_Estimate;



GREATEST(LABOR_COST,PARTS_COST)

-------------------------------

                            202

                            123

                            183

                            103

                            104

                            151

6 rows selected.

Oracle returns an error message if the datatypes of the expressions don't match.

SQL> select greatest(Labor_Cost,Estimated_Date_for_Completion)

  2  from Depot_Estimate;

select greatest(Labor_Cost,Estimated_Date_for_Completion)

                           *

ERROR at line 1:

ORA-00932: inconsistent datatypes

The GREATEST and LEAST functions don't indicate which value is largest or smallest. To obtain that information, you can use the DECODE function in conjunction with GREATEST and LEAST in the following way:

SQL> select decode(greatest(Labor_Cost,Parts_Cost),

  2                Labor_Cost,'Labor',

  3                Parts_Cost,'Parts'),

  4         greatest(Labor_Cost,Parts_Cost)

  5  from Depot_Estimate;

DECODE( GREATEST(LABOR_COST,PARTS_COST)

------- -------------------------------

Labor                               202

Labor                               123

Parts                               183

Labor                               103

Parts                               104

Parts                               151

6 rows selected.

The first argument to the DECODE function is the expression that you want to decode--;in this case, GREATEST(Labor_Cost,Parts_Cost). First, you compare the value returned by GREATEST to the value of Labor_Cost, and if the values are equal, DECODE returns the string Labor. If they're not equal, DECODE compares the value returned by GREATEST to the value of Parts_Cost; if those two values are equal, then DECODE returns the string Parts.

The next SELECT statement illustrates how to use the DECODE function to return a string value that concatenates the type of cost--;Labor or Parts--;with the amount.

SQL> select decode(greatest(Labor_Cost,Parts_Cost),

  2                Labor_Cost,'Labor = ' || to_char(Labor_Cost),

  3                Parts_Cost,'Parts = ' || to_char(Parts_Cost),

  4                'Unknown')

  5  from Depot_Estimate;

DECODE(GREATEST(LABOR_COST,PARTS_COST),LABOR_COS

------------------------------------------------

Labor = 202

Labor = 123

Parts = 183

Labor = 103

Parts = 104

Parts = 151

6 rows selected.

The previous query is just another example of how a function can be used as the argument to another function. In this case the TO_CHAR function, when concatenated with a literal string, is an argument to the DECODE function.

Determining Whether a Value Is Null

When developing an Oracle application, you are bound to encounter situations in which a screen or report will return information about a column that can be null. If you want to return a specific value in place of a null value, you can use Oracle's NVL function to make the replacement. The syntax is

NVL (column-value, substitute-value)

where column-value is the column value to evaluate and substitute-value is the value that the NVL function will return if column-value is null.

Consider the following situation. The Repair Header table records the amount, if any, that the customer has placed on deposit toward the repair. If you select Deposit_Amount from Repair_Header, some of the values for Deposit_Amount are null.

SQL> select Repair_ID, Deposit_Amount

  2  from Repair_Header

  3  order by Repair_ID;

REPAIR_ID DEPOSIT_AMOUNT

--------- --------------

      501            125

      502

      503            120

      504

      505             85

      506

6 rows selected.

You can use the NVL function to return a zero instead of a null.

SQL> select Repair_ID, NVL(Deposit_Amount,0.0)

  2  from Repair_Header

  3  order by Repair_ID;

REPAIR_ID NVL(DEPOSIT_AMOUNT,0.0)

--------- -----------------------

      501                     125

      502                       0

      503                     120

      504                       0

      505                      85

      506                       0

6 rows selected.

The NVL function can be used as an argument for other Oracle built-in functions. For example, you can query the Repair_Header table for the average deposit.

SQL> select avg(Deposit_Amount)

  2  from Repair_Header;

AVG(DEPOSIT_AMOUNT)

-------------------

                110

When you embed the NVL function inside the AVG function, the result is different.

SQL> select avg(NVL(Deposit_Amount,0.0))

  2  from Repair_Header;

AVG(NVL(DEPOSIT_AMOUNT,0.0))

----------------------------

                          55

The reason for the difference is that the AVG function uses the zero values returned by NVL, not the null values.

Miscellaneous Numeric Functions

The following code segments show some additional built-in mathematical functions that Oracle provides. These functions can be used in all four DML statements: SELECT, INSERT, UPDATE, and DELETE.

The function EXP(n) returns the value of the mathematical constant e raised to the nth power.

SQL> select exp(2.1) from dual;

 EXP(2.1)

---------

8.1661699

LN(n) returns the natural logarithm of n, where n is greater than zero.

SQL> select ln(1.0), ln(2.781828) from dual;

  LN(1.0) LN(2.781828)

--------- ------------

        0    1.0231083

The function LOG(m, n) returns the logarithm of n, using a base of m.

SQL> select log(10,1), log(10,2) from dual;

LOG(10,1) LOG(10,2)

--------- ---------

        0    .30103

POWER(m, n) returns the value of m raised to the nth power.

SQL> select power(10,2), power(2,16) from dual;

POWER(10,2) POWER(2,16)

----------- -----------

        100       65536

The function SQRT(n) returns the square root of n. If n is less than zero, Oracle returns an error. Here is an example of its use:

SQL> select sqrt(256) from dual;

SQRT(256)

---------

       16

SQL> select sqrt(-0.001) from dual;

ERROR:

ORA-01428: argument '-.001' is out of range

The MOD Function

MOD provides the modulus function in Oracle SQL. Its syntax is

MOD(m, n)

where m is a number and n is a divided into m.

MOD returns the remainder of m divided by n.

SQL> select mod(23,5)

  2  from dual;

MOD(23,5)

---------

        3

The ABS Function

The ABS function returns the absolute value of its argument, as shown in the following example:

SQL> select abs(-21.34), abs(131)

  2  from dual;

ABS(-21.34)  ABS(131)

----------- ---------

      21.34       131

The SIGN Function

The SIGN function returns the location of its numeric argument relative to zero. SIGN(value) returns the following indicators:

Following is an example of how SIGN works:

SQL> select sign(-22), sign(0), sign(7.2)

  2  from dual;

SIGN(-22)   SIGN(0) SIGN(7.2)

--------- --------- ---------

       -1         0         1

Converting a Number to Its ASCII Character Value

Another built-in function--;CHR--;converts an integer expression to its equivalent ASCII character. Here is an example:

SQL> select chr(60), chr(120)

  2  from dual;

C C

- -

< x

Trigonometric Functions

Oracle provides six built-in trigonometric functions. For each function, the argument is expressed in radians.

SIN(value) returns the sine of value.

SQL> select sin(3.14159/2) from dual;

SIN(3.14159/2)

--------------

             1

The function COS(value) returns the cosine of value.

SQL> select cos(0) from dual;

   COS(0)

---------

        1

TAN(value) returns the tangent of value.

SQL> select tan(3.14159/4) from dual;

TAN(3.14159/4)

--------------

     .99999867

The function SINH(value) returns the hyperbolic sine of value.

SQL> select sinh(3.14159/6) from dual;

SINH(3.14159/6)

---------------

      .54785297

COSH(value) returns the hyperbolic cosine of value.

SQL> select cosh(3.14159/6) from dual;

COSH(3.14159/6)

---------------

      1.1402381

The function TANH(value) returns the hyperbolic tangent of value.

SQL> select tanh(3.14159/6) from dual;

TANH(3.14159/6)

---------------

      .48047244

Summary

Keep the following concepts in mind when you are constructing SQL statements for your application: