PL/SQL by Example / Scalar Data Types
Data types in PL/SQL are divided into three basic categories: scalar, composite, and reference. Scalar types typically store only a single value, composite types hold arrays of values, or they can be more complex structures like records or objects. A reference data type serves as a pointer.
Boolean and Number
The previous part, in addition to code structure and control flow, introduced two basic data types in a simplified way – number and boolean. The following example demonstrates two, or rather three, methods of assigning a value to a variable: default initialization in the declarative part, assignment in the execution part, and assignment using an SQL query with the INTO keyword.
DECLARE
l_Bool BOOLEAN; -- boolean data type, cannot be used in SQL
l_Number NUMBER:=100; -- number type
BEGIN
l_Number:=200;
SELECT 300
INTO l_Number
FROM dual -- auxiliary system table
;
IF (l_Bool IS NULL) THEN -- default value of a variable is always NULL
dbms_output.put_line(l_Number); -- 300
END IF;
END;
The NUMBER data type can store both integer values and floating-point values. Precision (total number of digits) and scale (number of digits after the decimal point) can be specified manually. If not specified, the maximum precision and scale are used. In PL/SQL, we can also work with various numeric subtypes, such as INT, which internally maps to NUMBER(38,0). For maximum simplification, I will only use NUMBER from now on; more information about numeric data types can be found in the documentation.
Character and String
The CHAR type is used for working with strings of a predetermined fixed length, while VARCHAR2 is used for variable-length strings. The maximum size depends on the configuration and the chosen character set, and again, can be found in the documentation. However, it generally holds that for very long text, using the CLOB (“Character Large Object”) data type is more appropriate. String concatenation and basic functions for working with them are described in the following example.
DECLARE l_Character CHAR(1):='A'; -- fixed size l_String VARCHAR2(4000):='apple'; -- maximum size BEGIN -- string concatenation using double pipes dbms_output.put_line(l_String||' '||l_Character); -- apple A -- substring extraction dbms_output.put_line(SUBSTR(l_String, 1, 3)); -- app -- position within the string dbms_output.put_line(INSTR(l_String, 'pl')); -- 3 -- replacement dbms_output.put_line(REPLACE(l_String, 'p', 'P')); -- aPPle END;
Date
For storing dates, Oracle uses the DATE type, which includes the year, month, day, hour, minute, and second. Assignment is done using the TO_DATE() function, and conversion to a human-readable string is done with the TO_CHAR() function. Both functions accept a format as their second parameter.
DECLARE
l_Date DATE;
l_String VARCHAR2(20);
BEGIN
l_Date:=TO_DATE('1.1.2016', 'DD.MM.YYYY'); -- assignment
-- implicit conversion according to database settings
l_String:=l_Date;
dbms_output.put_line(l_String); -- 01.01.16
-- explicit conversion using format
l_String:=TO_CHAR(l_Date, 'DD.MM.YYYY');
dbms_output.put_line(l_String); -- 01.01.2016
-- retrieving and converting the current date
l_String:=TO_CHAR(SYSDATE, 'HH24:MI:SS DD.MON.YY');
dbms_output.put_line(l_String); -- 17:30:12 21.MAR.16
-- retrieving and converting to the name of the day of the week
l_String:=TO_CHAR(SYSDATE, 'DAY');
dbms_output.put_line(l_String); -- MONDAY
-- overriding the database language setting
l_String:=TO_CHAR(SYSDATE, 'DY', 'NLS_DATE_LANGUAGE=ENGLISH');
dbms_output.put_line(l_String); -- MON
END;
Basic date operations like adding an hour, day, or month are summarized in the following example.
DECLARE
l_Date DATE:=TO_DATE('01.01.2016', 'DD.MM.YYYY');
BEGIN
-- adding a day
dbms_output.put_line(l_Date+1); -- 02.01.16
-- adding an hour
dbms_output.put_line(TO_CHAR(l_Date+1/24, 'HH24:MI')); -- 01:00
-- adding a month
dbms_output.put_line(ADD_MONTHS(l_Date, 1)); -- 01.02.16
-- subtracting a year
dbms_output.put_line(ADD_MONTHS(l_Date, -12)); -- 01.01.15
-- last day of the month
dbms_output.put_line(LAST_DAY(l_Date)); -- 31.01.16
-- rounded difference in months
dbms_output.put_line(ROUND(MONTHS_BETWEEN(l_Date+80, l_Date))); -- 3
END;
%TYPE
If we are unsure about the data type or if it might change over time, we can choose to declare it based on another variable or a table column.
DECLARE l_Number NUMBER; l_Employee_ID l_Number%TYPE; -- NUMBER l_FirstName employees.first_name%TYPE; -- VARCHAR2(20) BEGIN l_Employee_ID:=100; l_FirstName:='Steven'; dbms_output.put_line(l_Employee_ID||' '||l_FirstName); -- 100 Steven END;
Constant and NOT NULL Clause
The PL/SQL language also supports constants, which cannot be overwritten with another value within the block’s body. The NOT NULL clause, which checks if a given variable is initialized, is also very useful.
DECLARE
L_NUMBER CONSTANT NUMBER:=100; -- constant
l_Bool BOOLEAN NOT NULL:=FALSE; -- non-NULL type
BEGIN
-- CANNOT DO L_NUMBER:=200;
-- CANNOT DO l_Bool:=NULL;
IF (NOT l_Bool) THEN
dbms_output.put_line(L_NUMBER); -- 100
END IF;
END;
Maximum Size of Basic Data Types
| Data Type | Size in SQL | Size in PL/SQL |
|---|---|---|
NUMBER |
± 999…(38 “nines”) | ± 999…(38 “nines”) |
CHAR |
2000 bytes * | 32767 bytes * |
VARCHAR2 |
4000 bytes * | 32767 bytes * |
CLOB |
8 TB to 128 TB ** | 128 TB |
* note that different characters have different sizes in UTF-8
** depends on the database block size setting