How to always show two decimal places on a number in PL/SQL - sql

Folks,
I have a existing stored procedure that I'm trying to update to always show two decimal places even when it's a whole number or single decimal number.
This stored procedure builds out a message that has to show v_credit_amt as a two decimal number, yet the value assigned to v_credit_amt can be either a whole number or single decimal or a two decimal value
i.e. 175 should display as 175.00, 250.5 should display as 250.50, 395.95 should display as 395.95.
Here is the relevant stock pl/sql
v_credit_amt number(22,2);
select NVL(sit_credit_limit,0)
into v_credit_amt
from sites
where sit_pkey = p_site_id;
I thought that formatting via to_char during the select would solve this, ala
select NVL(to_char(sit_credit_limit, 'fm99999.00'),0)
into v_credit_amt
from sites
where sit_pkey = p_site_id;
--v_credit_amt := to_char(v_credit_amt, 'fm99999.00');
insert into SQL_TXT values (v_credit_amt);
commit;
As you can see from the commented out line above, I've also tried it once the v_credit_amt variable is defined
I've also tried it with the to_number function
select NVL(to_number(sit_credit_limit, '99999.00'),0)
into v_credit_amt
from sites
where sit_pkey = p_site_id;
However if the value stored in the sit_credit_limit column is a whole number without decimals, or a number like 250.5 v_credit_amt shows the same in all cases when querying SQL_TXT table I'm inserting to for debugging.
SQL> select * from SQL_TXT;
COL1
--------------------------------------------------------------------------------
175
250.5
This particular event simply concatenates multiple message portions into a single long message string that is returned i.e.
if p_message_id = '14' then
v_message := 'Comtrol Check In Message Posted';
v_string := v_string || '008' || lpad(length(v_fname || ' ' || v_lname), 3, '0') || v_fname || ' ' || v_lname;
v_string := v_string || '059' || lpad(1, 3, '0') || '0';
--v_string := v_string || '089' || lpad(1, 3, v_credit_amt) || to_char(v_credit_amt);
v_string := v_string || '089' || lpad(length(to_char(v_credit_amt, 'fm9999999.00')), 3, '0') || to_char(v_credit_amt, 'fm9999999.00');
v_string := v_string || '106' || lpad(length(nvl(v_acct_number,'')), 3, '0') || v_acct_number;
--v_string := v_string || '106' || v_acct_number;
v_string := v_string || '164' || lpad(1, 3, '0') || '0';
v_string := v_string || '174' || lpad(length(v_rm_phone_num), 3, '0') || v_rm_phone_num;
v_string := v_string || '175' || lpad(length(v_rm_id), 3, '0') || v_rm_id;
v_string := v_string || '183' || lpad(1, 3, '0') || '0';
endif;
However I cannot get the final string to properly have two decimals in all use cases.
For example if the value in the db is 125 I get this for a final string
144450034629999008009Bob Smith05900100{89006125}1060073307542164001017400340917500
34091830010
however it should have been
144450034629999008009Bob Smith05900100{89007125.00}1060073307542164001017400340917500
34091830010
Sorry for the formatting above, I can't see how to bold a section without a code block so I've highlighted the relative portions instead {}
What am I missing if I need to always display a number to two decimals even if a whole or 1 decimal value is given?

You actually, have to insert the data with formatting. (Assuming the targeted column is VARCHAR) What ever format you fetch and put into a NUMBER variable, will be a NUMBER.
A number doesn't have a format to be saved after-all. Only for display, the formatting comes into picture.
insert into SQL_TXT values (TO_CHAR(v_credit_amt,'FM99999.00'));
commit;
If the INSERT-ed column is NUMBER again.
You still want to go with
SELECT TO_CHAR(COL1,'FM99999.00') FROM SQL_TEXT;

Related

How can I fix these Trigger Errors? Oracle 11g Express

code:
create or replace trigger "ARTICULOSCAT_INSERT" AFTER insert on
"ARTICULOSCAT" for each row BEGIN INSERT INTO
BITACORA (USUARIO,FECHA,TABLA_AFECTADA,ACCION_EFECTUADA,COLUMNAS,VALORES_ANT,VALORES_NUEVOS)
VALUES(USER,SYSDATE,'ArticulosCat','Insert','I_ID_ARTICULO,V_DESCRIPCION_100,I_ID_UM,F_PRECIO,I_ID_IMPUESO',
CONCAT('I_ID_ARTICULO: ',:OLD.I_ID_ARTICULO,',','V_DESCRIPCION_100: ',:OLD.V_DESCRIPCION_100,',','I_ID_UM: ',:OLD.I_ID_UM,',','F_PRECIO: ',:OLD.F_PRECIO,',','I_ID_IMPUESTO: ',:OLD.I_ID_IMPUESTO),
CONCAT('I_ID_ARTICULO: ',:NEW.I_ID_ARTICULO,',','V_DESCRIPCION_100: ',:NEW.V_DESCRIPCION_100,',','I_ID_UM: ',:NEW.I_ID_UM,',','F_PRECIO: ',:NEW.F_PRECIO,',','I_ID_IMPUESTO: ',:NEW.I_ID_IMPUESTO)
END;​
Error:
Compilation failed, line 3 (02:17:30) The line numbers associated with
compilation errors are relative to the first BEGIN statement. This
only affects the compilation of database triggers. PL/SQL: ORA-00909:
invalid number of argumentsCompilation failed, line 2 (02:17:30) The
line numbers associated with compilation errors are relative to the
first BEGIN statement. This only affects the compilation of database
triggers. PL/SQL: SQL Statement ignoredCompilation failed, line 4
(02:17:30) The line numbers associated with compilation errors are
relative to the first BEGIN statement. This only affects the
compilation of database triggers. PLS-00103: Encountered the symbol
"end-of-file" when expecting one of the following: ( begin case
declare end exception exit for goto if loop mod null pragma raise
return select update while with
The issue lies in your use of the CONCAT function. As you can see from the documentation, this only accepts two parameters.
What you should use instead is the concatenation operator ||, as this can be used to string together many different parts and is, IMO, far easier to read than a series of nested CONCATs!
Therefore your trigger would look something like:
CREATE OR REPLACE TRIGGER articuloscat_insert
AFTER INSERT ON articuloscat
FOR EACH ROW
BEGIN
INSERT INTO bitacora
(usuario,
fecha,
tabla_afectada,
accion_efectuada,
columnas,
valores_ant,
valores_nuevos)
VALUES
(USER,
SYSDATE,
'ArticulosCat',
'Insert',
'I_ID_ARTICULO,V_DESCRIPCION_100,I_ID_UM,F_PRECIO,I_ID_IMPUESO',
'I_ID_ARTICULO: ' || :old.i_id_articulo || ',' || 'V_DESCRIPCION_100: ' || :old.v_descripcion_100 || ',' || 'I_ID_UM: ' || :old.i_id_um || ',' || 'F_PRECIO: ' || :old.f_precio || ',' || 'I_ID_IMPUESTO: ' || :old.i_id_impuesto,
'I_ID_ARTICULO: ' || :new.i_id_articulo || ',' || 'V_DESCRIPCION_100: ' || :new.v_descripcion_100 || ',' || 'I_ID_UM: ' || :new.i_id_um || ',' || 'F_PRECIO: ' || :new.f_precio || ',' || 'I_ID_IMPUESTO: ' || :new.i_id_impuesto);
END articuloscat_insert;
/
You were also missing a semi-colon (;) at the end of the insert, which I have added for you.

I want to insert one billion records in the oracle database

I have to insert one billion record in oracle database table.
I have 2 column .
One column is a seqence number which follows till 1,000,000,000
Second column is a varchar2 field ... and it is in the format of abc~122~373~7777777~5367
. That is first column must contain 3 random characters followed by '~' then 3 random number followed by '~' then again 3 random number followed by '~' then followed by 7 random number followed by '~' and finally 4 random number followed by '~'.
And I need all records to be unique.
create or replace function fn RETURN VARCHAR2 IS
label varchar2(24) := '';
BEGIN
-- abc~122~373~7777777~5367
select
chr(97 + floor(dbms_random.value*26)) || chr(97 + floor(dbms_random.value*26)) || chr(97 + floor(dbms_random.value*26))|| '~'
|| rpad(floor(dbms_random.value*123),3, '9') || '~'
|| rpad(floor(dbms_random.value*123),3, '8') || '~'
|| rpad(floor(dbms_random.value*1234567),7, '6') || '~'
|| rpad(floor(dbms_random.value*1234),4, '4') into label
from dual ;
RETURN label;
END;
and you could create a table by invoking it n times as below.
create table testtable as select fn from dual connect by level <= 1000000000;

PLS-00103: Encountered the symbol "end-of-file" compile error

Can't find anything wrong with this, but it compiles with errors. Anyone see what I am blind to?
CREATE OR REPLACE FUNCTION myusername.fun_hr_format_phone (tmp in VARCHAR2) RETURN VARCHAR2
IS tmpVar VARCHAR2(12);
BEGIN
SELECT DECODE(
LENGTH(tmp),
10, SUBSTR(tmp,1,3) || '-' || SUBSTR(tmp,4,3) || '-' || SUBSTR(tmp,7,4),
7, SUBSTR(tmp,1,3) || '-' || SUBSTR(tmp,4,4), tmp
) INTO tmpVar FROM dual;
RETURN tmpVar;
EXCEPTION
WHEN NO_DATA_FOUND THEN NULL;
WHEN OTHERS THEN RAISE;
END;
/
In case you're using SQL*Plus: beware of empty lines, your code it totally correct syntactically.

How to format my output in PL/SQL into straight columns?

I am wondering how I can get my output of the following code to be displayed in a neat column. The output now is not neatly organized. Is there something I need to put in front of my variable?
DBMS_OUTPUT.PUT_LINE('Employee Name: ' || (v_name));
DBMS_OUTPUT.PUT_LINE('Job: ' || (v_job));
DBMS_OUTPUT.PUT_LINE('Total Pay: ' || TO_CHAR(v_pay, '$999G999G999D99'));
You're outputting a single string, so there is no opportunity for the values to be neatly aligned, as they could be in a simple select. The concatenation (||) just sticks together exactly what it is given.
All you need to do is manually pad the strings:
DBMS_OUTPUT.PUT_LINE('Employee Name: ' || (v_name));
DBMS_OUTPUT.PUT_LINE('Job: ' || (v_job));
DBMS_OUTPUT.PUT_LINE('Total Pay: ' || TRIM(TO_CHAR(v_pay, '$999G999G999D99')));
The to_char of the number column leaves it left-padded (right-aligned) to the width of the format mask:
SQL> select TO_CHAR(4.97,'$999G999G999D99') from dual;
TO_CHAR(4.97,'$9
----------------
$4.97
... so in this case you want a trim around that so it only uses your own padding.
DECLARE
v_name varchar2(30) := 'Joe Bloggs';
v_job varchar2(20) := 'Contractor';
v_pay number := 52657.3;
BEGIN
DBMS_OUTPUT.PUT_LINE('Employee Name: ' || v_name);
DBMS_OUTPUT.PUT_LINE('Job: ' || v_job);
DBMS_OUTPUT.PUT_LINE('Total Pay: '
|| TRIM(TO_CHAR(v_pay, '$999G999G999D99')));
END;
/
Employee Name: Joe Bloggs
Job: Contractor
Total Pay: $52,657.30
PL/SQL procedure successfully completed.
Display as columns - this is a simple way. You may employ Rpad(), Lpad() for this, which would be more advanced I guess:
DECLARE
v_name varchar2(30) := 'Joe Bloggs';
v_job varchar2(20) := 'Contractor';
v_pay number := 52657.3;
BEGIN
DBMS_OUTPUT.PUT_LINE('Employee Name'||chr(9)||chr(9)||chr(9)||'Job'||chr(9)||chr(9)||chr(9)||chr(9)||chr(9)||'Total Pay');
DBMS_OUTPUT.PUT_LINE('--------------------------------------------------------');
DBMS_OUTPUT.PUT_LINE(v_name||chr(9)||chr(9)||chr(9)||chr(9)||chr(9)||v_job||chr(9)||chr(9)||TRIM(TO_CHAR(v_pay, '$999G999G999D99')));
END;
/
Employee Name Job Total Pay
--------------------------------------------------------
Joe Bloggs Contractor $52,657.30

Text wrapping in oracle

how can we wrap a text (coulmn value) basing ona stndard length of lets say 40 characters in to multi line in ORACLE SQL only.
select regexp_replace(column_name, '(.{40})', '\1' || chr(10) || chr(13))
from some_table;
In SQL Plus you can assign column widths
column column_name format a40
select column_name from table_name
The above format the output to be 40 columns wide and it would wrap anything to the next line.
Output display is usually controlled by the client
Some code I got from Martin Burbridge and modified:
CREATE OR REPLACE FUNCTION line_wrap(p_str_to_wrap VARCHAR2
,p_max_linesize PLS_INTEGER
,p_indent_spaces_each_line PLS_INTEGER DEFAULT 0
,p_separator VARCHAR2 DEFAULT ' ') RETURN VARCHAR2 IS
-- This function will insert chr(10)'s (newlines) at the separator
-- nearest the specified linesize.
-- The separator will default to a space if none provided.
-- The p_indent_spaces_each_line parameter allows each line of wrapped text to be
-- indented x spaces if desired. The indent_spaces will default to 0 if none provided.
v_max_linesize PLS_INTEGER := nvl(p_max_linesize
,80);
v_indent_spaces_each_line PLS_INTEGER := nvl(p_indent_spaces_each_line
,0);
v_separator VARCHAR2(20) := nvl(p_separator
,' ');
v_str_to_wrap VARCHAR2(4000) := p_str_to_wrap || v_separator;
v_line VARCHAR2(4000);
v_position PLS_INTEGER;
v_wrapped_text VARCHAR2(4000);
v_sql_errmsg VARCHAR2(4000);
BEGIN
WHILE v_str_to_wrap IS NOT NULL
LOOP
v_line := substr(v_str_to_wrap
,1
,v_max_linesize);
v_position := instr(v_line
,v_separator
,-1);
IF v_position = 0
THEN
v_position := v_max_linesize;
END IF;
v_line := substr(v_line
,1
,v_position);
IF v_indent_spaces_each_line > 0
THEN
v_wrapped_text := v_wrapped_text || chr(10) || lpad(' '
,v_indent_spaces_each_line
,' ') || v_line;
ELSE
v_wrapped_text := v_wrapped_text || chr(10) || v_line;
END IF;
v_str_to_wrap := substr(v_str_to_wrap
,v_position + 1);
END LOOP;
RETURN v_wrapped_text;
EXCEPTION
WHEN OTHERS THEN
v_sql_errmsg := 'Error in word_wrap: ' || SQLERRM;
raise_application_error(-20001
,v_sql_errmsg);
END;
-- How to use this function in a select statement:
select line_wrap(my_string,
40,
2,
' ')
from my_table.