Converting integer value from a db column to text in oracle - oracle

I have a requirement in db.
1).Table ABC, column: check_amount number number(18,4). This basically contains check amount for eg. 3000.50 to be paid to an employee.
Now a cheque is issued and that check contains this check_amount in number as well as in text form.for eg.check will have:
pay to <emplyee_name> ****$3000.50**** ****THREE THOUSAND DOLLARS AND FIFTY CENTS****
I have to generate this text using DB column value and display that on check.
Can anybody help me out, how can i achieve this in oracle 11g ?
Hint:I have heard of Julien format, but that is not working. Any suggestions is greatly appreciated.
From
Nalin

Since Julian format works only for whole numbers, you can separate the decimal parts and then apply the Julian format trick to the separated numbers. Here's a simple demo.
DECLARE
x NUMBER (8, 2) := 1253.5;
y NUMBER;
z NUMBER;
BEGIN
y := FLOOR (x);
z := 100 * (x - y);
DBMS_OUTPUT.put_line (TO_CHAR (TO_DATE (y, 'j'), 'jsp'));
IF (z > 0)
THEN
DBMS_OUTPUT.put_line (TO_CHAR (TO_DATE (z, 'j'), 'jsp'));
END IF;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line ('err:' || SQLERRM);
END;

There is a limitation while using Julian dates ,It ranges from 1 to 5373484. That’s why if you put the values after 5373484, it will throw you an error as shown below:
ORA-01854: julian date must be between 1 and 5373484
To cater the above problem ,create a function ,and with little trick with j->jsp ,you can fetch the desired result.
CREATE OR REPLACE FUNCTION spell_number (p_number IN NUMBER)
RETURN VARCHAR2
AS
TYPE myArray IS TABLE OF VARCHAR2 (255);
v_decimal PLS_INTEGER;
l_str myArray
:= myArray ('',
' thousand ',
' million ',
' billion ',
' trillion ',
' quadrillion ',
' quintillion ',
' sextillion ',
' septillion ',
' octillion ',
' nonillion ',
' decillion ',
' undecillion ',
' duodecillion ');
l_num VARCHAR2 (50) DEFAULT TRUNC (p_number);
l_return VARCHAR2 (4000);
BEGIN
FOR i IN 1 .. l_str.COUNT
LOOP
EXIT WHEN l_num IS NULL;
IF (SUBSTR (l_num, LENGTH (l_num) - 2, 3) <> 0)
THEN
l_return :=
TO_CHAR (TO_DATE (SUBSTR (l_num, LENGTH (l_num) - 2, 3), 'J'),
'Jsp')
|| l_str (i)
|| l_return;
END IF;
l_num := SUBSTR (l_num, 1, LENGTH (l_num) - 3);
END LOOP;
v_decimal := 100* (p_number -TRUNC(p_number)) ;
IF v_decimal>0 THEN
RETURN l_return ||' Dollars AND '||TO_CHAR (TO_DATE (v_decimal, 'j'), 'jsp')|| ' Cents';
ELSE
RETURN l_return ||' Dollars' ;
END IF;
END;
/
select spell_number(122344343444444.23) from dual;
Output:
One Hundred Twenty-Two trillion Three Hundred Forty-Four billion Three Hundred Forty-Three million Four Hundred Forty-Four thousand Four Hundred Forty-Four Dollars AND twenty-three Cents Blog Link

Related

In PL/SQL function I am getting error "Only a function is allowed here". How can i resolve it?

this is the error
i am getting while running the code although. it does not show error in code execution
I want to create a function in which I have to display the total balance in words.
CREATE OR REPLACE FUNCTION spell_number (p_number IN NUMBER)
RETURN VARCHAR2
AS
TYPE my Array IS TABLE OF VARCHAR2 (255);
l_str myArray
:= myArray ('',
' thousand ',
' million ',
' billion ',
' trillion ',
' quadrillion ',
' quintillion ',
' sextillion ',
' septillion ',
' octillion ',
' nonillion ',
' decillion ',
' undecillion ',
' duodecillion ');
l_num VARCHAR2 (50) DEFAULT TRUNC (p_number);
l_return VARCHAR2 (4000);
BEGIN
FOR i IN 1 .. l_str.COUNT
LOOP
EXIT WHEN l_num IS NULL;
IF (SUBSTR (l_num, LENGTH (l_num) - 2, 3) <> 0)
THEN
l_return :=
TO_CHAR (TO_DATE (SUBSTR (l_num, LENGTH (l_num) - 2, 3), 'J'),
'Jsp')
|| l_str (i)
|| l_return;
END IF;
l_num := SUBSTR (l_num, 1, LENGTH (l_num) - 3);
END LOOP;
RETURN l_return;
END;
Ah, that's Reports Builder.
Remove CREATE OR REPLACE and it should be just fine.

how to get output as text while input is in the numeric form

how to get output of numeric/numbers in character/alphabet form?
like i give input:- 1234
then output should be like:- one two three four AND (in other condition) one
thousand two hundred thirty four.
Try this
select to_char(to_date(1234, 'j'), 'jsp') from dual
Output will be
one thousand two hundred thirty-four
Execute below function.
CREATE OR REPLACE FUNCTION NUMTOWORDS (P_IN IN NUMBER)
RETURN VARCHAR2
IS
V_WORDS VARCHAR2 (20) := '';
V_CHAR VARCHAR2 (20) := '';
V_LENGTH NUMBER;
TYPE ARRAY_TYPE IS VARRAY (10) OF VARCHAR2 (10);
V_ARRAY ARRAY_TYPE := ARRAY_TYPE ('ZERO', 'ONE', 'TWO', 'THREE', 'FOUR', 'FIVE', 'SIX', 'SEVEN', 'EIGHT', 'NINE');
BEGIN
SELECT LENGTH (TO_CHAR (P_IN)) INTO V_LENGTH FROM DUAL;
FOR I IN 1 .. V_LENGTH
LOOP
V_CHAR := V_ARRAY ( (TO_NUMBER (SUBSTR (TO_CHAR (P_IN), I, 1)) + 1));
V_WORDS := CONCAT (V_WORDS, CONCAT (V_CHAR, ' '));
END LOOP;
RETURN TRIM (V_WORDS);
END;
"" SELECT NUMTOWORDS(674) FROM DUAL;

PL/SQL XMLType operation performance

I have a PL/SQL function to parse my XML block and return it into a VARCHAR variable.
My XML block is not too complex. Have only 14 tags and don't have any repeating structure.
Inside my function I made this XMLType operation:
SELECT px_Header
|| 'R5'
|| RPAD (NVL (A1, ' '), 2)
|| RPAD (NVL (A2, ' '), 10)
|| RPAD (NVL (A3, ' '), 3)
|| RPAD (NVL (A4, ' '), 1)
|| RPAD (NVL (A5, ' '), 1)
|| RPAD (NVL (A6, ' '), 10)
|| RPAD (NVL (A7, ' '), 1)
|| RPAD (NVL (A8, ' '), 8)
|| RPAD (NVL (A9, ' '), 1)
|| RPAD (NVL (A10, ' '), 16)
|| RPAD (NVL (A11, ' '), 2)
|| RPAD (NVL (A12, ' '), 8)
|| RPAD (NVL (A13, ' '), 1)
|| RPAD (NVL (A14, ' '), 1)
|| CHR (13)
|| CHR (10)
INTO lv_return
FROM XMLTABLE (
'/IN_DATA'
PASSING px_Block
COLUMNS A1 VARCHAR2 (2) PATH 'A1',
A2 VARCHAR2 (10)
PATH 'A2',
A3 VARCHAR2 (3) PATH 'A3',
A4 VARCHAR2 (1) PATH 'A4',
A5 VARCHAR2 (1)
PATH 'A5',
A6 VARCHAR2 (10)
PATH 'A6',
A7 VARCHAR2 (1)
PATH 'A7',
A8 VARCHAR2 (8) PATH 'A8',
A9 VARCHAR2 (1)
PATH 'A9',
A10 VARCHAR2 (16)
PATH 'A10',
A11 VARCHAR2 (2)
PATH 'A11',
A12 VARCHAR2 (8)
PATH 'A12',
A13 VARCHAR2 (1)
PATH 'A13',
A14 VARCHAR2 (1)
PATH 'A14');
This function takes 0.0945 seconds to perform. Not to so much, I think. But my process need to iterate 100000 times, so I call this function 100000 time.
Is there any faster way to implement this to improve, even if only a few thousandths of a second?
Combine XML and process one large document instead of many little documents. This at least reduces the amount of context switches between SQL and PL/SQL, and
probably allows Oracle to take advantage of other efficiencies as well.
Instead of processing one-row-at-a-time:
<IN_DATA><A1>1</A1><A2>2</A2></IN_DATA>
Process multiple rows:
<ALL_DATA>
<IN_DATA><A1>1</A1><A2>2</A2></IN_DATA>
<IN_DATA><A1>3</A1><A2>4</A2></IN_DATA>
</ALL_DATA>
With code like this:
--Process XML.
declare
v_strings sys.odcivarchar2list;
px_Block xmltype := xmltype('
<ALL_DATA>
<IN_DATA><A1>1</A1><A2>2</A2></IN_DATA>
<IN_DATA><A1>3</A1><A2>4</A2></IN_DATA>
</ALL_DATA>');
begin
--Get strings.
select A1||' '||A2
bulk collect into v_strings
from
xmltable
(
'/ALL_DATA/IN_DATA' passing px_Block columns
a1 varchar2 (2) path 'A1',
a2 varchar2 (10) path 'A2'
);
--Display strings for testing.
for i in 1 .. v_strings.count loop
dbms_output.put_line(v_strings(i));
end loop;
end;
/
Results:
1 2
3 4
EDIT
If the documents cannot be combined, and if the input string is relatively simple and can be trusted, replacing the XML processing with string functions may significantly improve performance.
The example below uses a smaller input string, but changing from XML to string processing shrinks the time from 30 seconds to almost none.
--Process XML.
declare
v_result varchar2(100);
v_xml xmltype := xmltype('<IN_DATA><A1>1</A1><A2>2</A2></IN_DATA>');
v_string varchar2(100) := '<IN_DATA><A1>1</A1><A2>2</A2></IN_DATA>';
function get_string_from_xml(p_xml xmltype) return varchar2 is
v_string varchar2(100);
begin
--Get strings.
select A1||' '||A2
into v_string
from
xmltable
(
'/IN_DATA' passing v_xml columns
a1 varchar2 (2) path 'A1',
a2 varchar2 (10) path 'A2'
);
return v_string;
end get_string_from_xml;
function get_string_from_string(p_string varchar2) return varchar2 is
v_string varchar2(100);
begin
return
substr(p_string, instr(p_string, '<A1>') + 4, instr(p_string, '</A1>') - instr(p_string, '<A1>') - 4)
||' '||
substr(p_string, instr(p_string, '<A2>') + 4, instr(p_string, '</A2>') - instr(p_string, '<A2>') - 4);
end get_string_from_string;
begin
--27 seconds
/*
for i in 1 .. 100000 loop
v_result := get_string_from_xml(v_xml);
end loop;
*/
--0 seconds
for i in 1 .. 100000 loop
v_result := get_string_from_string(v_string);
end loop;
end;
/
Another way of looking at this is that your original function takes 2.5 hours to process 100,000 XML documents with a single thread. That sounds quite reasonable. Some simple parallelism should be able to shrink the time from hours to tens of minutes.

PL/SQL checking if number is Integer

I want to check my employee table if there is any employee who works as an
employee for exactly one year or two years, or three years, ...
The Problem for me now is that i dont know how i can check if a number is
an integer or not.
CREATE OR REPLACE PROCEDURE jubilar
IS
v_cur_date DATE := TO_DATE('03-JAN-2012');
v_cur_year NUMBER;
v_first_name employees.first_name%TYPE;
v_last_name employees.last_name%TYPE;
v_hire_date employees.hire_date%TYPE;
CURSOR c_emp_cursor IS
SELECT first_name, last_name, hire_date FROM employees;
BEGIN
OPEN c_emp_cursor;
LOOP
FETCH c_emp_cursor INTO v_first_name, v_last_name, v_hire_date;
EXIT WHEN c_emp_cursor%NOTFOUND;
v_cur_year := round((v_cur_date - v_hire_date) / 365, 1);
IF v_cur_year ???
DBMS_OUTPUT.PUT_LINE(v_first_name || ' ' || v_last_name || ' ist heute ' || v_cur_year || ' Jahre im Unternehmen tätig.');
END IF;
END LOOP;
CLOSE c_emp_cursor;
END jubilar;
/
At this line
IF v_cur_year ??
I need to check if v_cur_year is an integer or not. Because if it is the employee
works for exactly X year as an employee. And i need to know that.
EDIT:
I tried this:
CREATE OR REPLACE PROCEDURE jubilar
IS
v_cur_date DATE := TO_DATE('03/01/12', 'dd/mm/yy');
v_cur_year NUMBER;
v_cur_year_temp VARCHAR2(100);
v_first_name employees.first_name%TYPE;
v_last_name employees.last_name%TYPE;
v_hire_date employees.hire_date%TYPE;
CURSOR c_emp_cursor IS SELECT first_name, last_name, hire_date FROM employees;
BEGIN
OPEN c_emp_cursor;
LOOP
FETCH c_emp_cursor INTO v_first_name, v_last_name, v_hire_date;
EXIT WHEN c_emp_cursor%NOTFOUND;
v_cur_year := round((v_cur_date - v_hire_date) / 365, 1);
v_cur_year_temp := TO_CHAR(v_cur_year);
IF REGEXP_COUNT(v_cur_year_temp, ',') = 0 THEN
DBMS_OUTPUT.PUT_LINE(v_first_name || ' ' || v_last_name || ' ist heute ' || v_cur_year || ' Jahre im Unternehmen tätig.' || TO_CHAR(v_hire_date));
END IF;
END LOOP;
CLOSE c_emp_cursor;
END jubilar;
/
But it gives me wrong persons with hire_date for example 17/01/2005
I also tried this:
CREATE OR REPLACE PROCEDURE jubilar
IS
v_cur_date DATE := TO_DATE('03/01/12', 'dd/mm/yy');
v_cur_year NUMBER;
v_first_name employees.first_name%TYPE;
v_last_name employees.last_name%TYPE;
v_hire_date employees.hire_date%TYPE;
CURSOR c_emp_cursor IS SELECT first_name, last_name, hire_date FROM employees;
BEGIN
OPEN c_emp_cursor;
LOOP
FETCH c_emp_cursor INTO v_first_name, v_last_name, v_hire_date;
EXIT WHEN c_emp_cursor%NOTFOUND;
v_cur_year := round((v_cur_date - v_hire_date) / 365, 1);
IF trunc(v_cur_year) = v_cur_year THEN
DBMS_OUTPUT.PUT_LINE(v_first_name || ' ' || v_last_name || ' ist heute ' || v_cur_year || ' Jahre im Unternehmen tätig.' || TO_CHAR(v_hire_date));
END IF;
END LOOP;
CLOSE c_emp_cursor;
END jubilar;
/
But it gives me wrong persons with hire_date for example 17/01/2005
This may help you ..
DECLARE
d1 DATE := TO_DATE ('01/12/12', 'mm/dd/yy');
d2 DATE := SYSDATE;
n1 NUMBER;
chk VARCHAR2 (100);
BEGIN
n1 := ROUND ( (d2 - d1) / 365, 1);
DBMS_OUTPUT.put_line (n1);
chk := TO_CHAR (n1);
DBMS_OUTPUT.put_line (chk);
IF REGEXP_COUNT (chk, '.') != 0
THEN
---YOUR LOGIC
END IF;
END;
To check for employees who are employed exactly two years at e.g. January 14th, you can use this:
select *
from employees
where months_between(DATE '2014-01-14', trunc(hire_date)) / 12 = 2;
(I prefer using ANSI date literals over the to_date() function. Works on multiple DBMS and is less typing)
months_between(...) / 12 will result in a fractional value if it's not exactly one year and therefore the comparison with a non-fractional value will fail.
Just change the comparison date (DATE '2014-01-14') and the condition ( =2) to accommodate for other dates or durations.
SQLFiddle example: http://sqlfiddle.com/#!4/27c3d/4
I would do it like this:
with y as
(select trunc(ADD_MONTHS(SYSDATE, -12 * LEVEL)) as the_date, LEVEL as anniversary
from dual
connect by LEVEL <= 6)
select first_name, last_name, hire_date, anniversary, the_date
join y on the_date = hire_date

insert or update columns according weekend

declare
str varchar2(4000);
i int;
begin
for i in 1 ..31 loop
str:= str || 'col' || i || ' varchar2(2)';
if i < 31 then
str := str || ',';
end if;
end loop;
execute immediate 'create table t1 ('|| str ||')';
end;
/
I'm newbie in pl/sql
this procedure creates t1 table with 31 columns. 31 is day of month (say May). I can't create the procedure which have condition like this :
if col(i) i in ('Sat','Sun') ...
insert into t1 value ('r')
for example, this procedure must insert 'r' into col5,col6,col12,col13 .. columns
because 5th,6th,12th,13th of may in Sun or Sat
BEGIN
FOR i IN 1..31 LOOP
IF( to_char(sysdate-1+i,'DAY') IN ('SAT', 'SUN') )
THEN
INSERT INTO t1 (col(i))
VALUES ('r');
END IF;
END LOOP;
END;
/
I tried with this procedure but there are several errors
where must I correct my mistakes
thanks in advance
I agree with Bob Jarvis that you ought to have a better data model. But just for grins let's assume you have to work with what you've got.
This procedure takes a month and year as parameters, and generates a range of days from them. I have given the MON_T table two columns, MON and YR as a primary key, because I can't help myself.
create or replace procedure gen_month_rec
( p_mon in mon_t.mon%type
, p_yr in mon_t.yr%type )
is
lrec mon_t%rowtype;
empty_rec mon_t%rowtype;
first_dt date;
last_d pls_integer;
begin
lrec := empty_rec;
lrec.mon := p_mon;
lrec.yr := p_yr;
first_dt := to_date('01-'||p_mon||'-'||p_yr, 'dd-mon-yyyy');
last_d := to_number(to_char(last_day(first_dt),'dd'));
for i in 1 .. last_d
loop
if to_char(first_dt-+ (i-1),'DAY') in ('SAT', 'SUN')
then
execute immediate 'begin lrec.col'
||trim(to_char(i))
||' := ''r''; end;';
end if;
end loop;
insert into mon_t values lrec;
end;
I suggest that you read up on the rules of data normalization. It appears that this table suffers from a major problem in that it has what are known as "repeating groups" - in this case, 31 fields for information about each day. You probably need a table with the full date you're interested in, and then the fields which describe that date. Perhaps something like the following:
CREATE TABLE CALENDAR_DAY
(CAL_DATE DATE PRIMARY KEY,
COL NUMBER,
<definitions of other fields needed for each day>);
Given the above your code becomes
DECLARE
dtMonth_of_interest DATE := TRUNC(SYSDATE, 'MONTH');
BEGIN
FOR i IN 0..30 LOOP
IF( to_char(dtMonth_of_interest+i,'DAY') IN ('SAT', 'SUN') )
THEN
INSERT INTO CALENDAR_DAY (CAL_DATE, COL)
VALUES (dtMonth_of_interest + i, 'r');
END IF;
END LOOP;
END;
Hopefully this gives you some ideas.
Share and enjoy.

Resources