I have a PL/SQL script with a variable called v_credit_hours that is a number data type. I fetch my fields from a cursor and insert them into a table where the credit_hours field is also a number. I need to pad v_credit_hours with leading zeros and end up with four digits before inserting it. So 50 hours should like like 0050. I have this line just before my insert statement.
v_credit_hours := LPAD(ROUND(v_credit_hours), 4, 0);
The problem is that it does not change anything. 50 still comes out as 50. Nothing happens if I change 4 to 10 or if I remove LPAD completely. I tried changing v_credit_hours to v_credit_hours * 8 inside the ROUND, and that altered the result. It is as if Oracle is just ignoring LPAD. It comes out fine in this query, but not when I use PL/SQL. I also tried adding TO_CHAR between LPAD and ROUND, but that did nothing.
SELECT LPAD(ROUND(50), 4, 0) FROM dual;
Can I not use LPAD in this way? I can do it up in my original cursor, but I really only wanted to see the leading zeros in the final output.
Its sorta hard to tell if v_credit_hours is typed as a Number or a Varchar. However, if you do it this way you will get what you want.
DECLARE
v_formatted_credit_hours varchar2(60);
BEGIN
SELECT lpad(round(50),4,0)
INTO v_formatted_credit_hours
FROM dual ;
SYS.dbms_output.put_line( v_formatted_credit_hours) ;
END;
hopefully that helps to shine a little light on your issue.
Related
I am using a query
SELECT CAST(000027 AS VARCHAR2(15)) FROM dual;
but this removes the starting 0000 zero's and return me 27 only.
How can I make it return 000027?
The value 000027 and 27, both as numbers, are identical to Oracle, and it won't "see" the leading zeroes. The closest thing to what you want here might be to left pad the number/string with zeroes, to a fixed length. Assuming you want a numerical string of length 6, you could try:
SELECT LPAD(CAST(000027 AS VARCHAR2(15)), 6, '0')
FROM dual;
Demo
Use a string literal instead of a numeric literal:
SELECT CAST('000027' AS VARCHAR2(15)) FROM dual
though the cast may be redundant anyway depending on what you are trying to do.
If you are stuck starting from a number then it's probably losing the leading zeros earlier than you think, as numbers don't actually have those.
If you know how many digits there should be then you can pad, or use to_char() instead of cast():
SELECT TO_CHAR(000027, 'FM000000') FROM dual
If you need to you can still cast that to varchar2(15), but not sure why what would be needed unless you're using this as part of a CTAS statement.
I have a function that returns a Select clause with a variable number of columns (from 2 to 31). Then, I need to do inserts into a table using the first + each of the other columns. For example, if my Select returns: ('A', '1', '2', '3') I need to insert ('A','1'), ('A','2') and ('A','3') into a given table. The problem is that I can't know how many columns I'll have in the original Select clause.
I tried to use the Select clause to open a cursor but, is there any way I can know how many columns the cursor has and, then, fetch them separately? Is there any other way to do this?
Thanks a lot in advance,
Ander.
You are in need of dbms_sql.describe_columns
Thanks,
I finally managed to solve it. As Sanders says, using dbms_sql, I open a cursor, parse it, get the column number with describe_columns, and in a loop, define their formats (luckily for me, all of them are varchar2(6)).
Finally, I use dbms_sql.column_value to get each column's value while I fetch all the rows.
im trying to insert in my db a 4 charactes password. It is generated by a sequence... the problem im having is that i cant find a way to make that happend. I just find out about triggers, i know the answer to my problem might be there and this is what i have till now :S
CREATE OR REPLACE TRIGGER TRG_PASSWORD
BEFORE INSERT OR UPDATE OF PASSWORD ON USER
FOR EACH ROW
BEGIN
SELECT SEQ_PASSWORD.NEXTVAL
INTO new.PASSWORD := LPAD(:new.PASSWORD,4,'0');
FROM DUAL;
END;
Im new with all these database stuff! plz help! and sorry for my english btw! i know its awful!
Odd password rules as it'd be relatively easy to guess a new user's... but you can do this:
SELECT LPAD(SEQ_PASSWORD.NEXTVAL, 4, '0')
INTO :new.PASSWORD
FROM DUAL;
Or from 11g:
:new.PASSWORD := LPAD(SEQ_PASSWORD.NEXTVAL, 4, '0');
As an alternative to LPAD you could also use TO_CHAR:
:new.PASSWORD := TO_CHAR(SEQ_PASSWORD.NEXTVAL, 'FM0000');
Of course, all of these assume your USER.PASSWORD field is defined as varchar2, not as number.
You probably don't want the 'OF PASSWORD' clause though; that means the sequence will only be used to set the password if that field is set by the insert or update statement, which doesn't seem very intuitive; and resetting the password on update of anything seems unhelpful too.
I want to convert a 9 digit number to 10 digit by appending a 0 to it.
For example In Table ABC say there is a column named B which takes a number which is at the max 10 digit long.
Now sometimes I will get a 9 digit number only.
So in that case when a 9 digit number is faced i need to fire a trigger to make it 10 digit and then insert in the table.
For that you need to create the column with character datatype so that it can hold the leading zeros.
You don't need to write any trigger for this simple operation. you can use lpad for this purpose:
eg.g
Insert into table1(number_col) values ( lpad(999999999, 10, '0'));
select * from table1;
| number_col |
|-----------------|
| 0999999999 |
To use this in trigger, create a trigger as follows (Not Tested);
create or replace trigger trg_table1
before insert or update of number_col on table1
for each row
begin
:new.number_col := lpad( :new.number_col, 10, '0' );
end;
You don't really need to make this a trigger. Adding a 0 to the front of the number is really only for humans, the computer doesn't care and that information can't be stored in the database unless you convert the column to a string format.
What you're looking for is one of three things: Either, change the way your forms display the information to add padding if the number is less 10,000,000,000 to affect the way the user sees the information (most recommended)
Or, use the lpad function to convert the number to a string with 0 padding if necessary
lpad(input,10,'0')
Note that this will require conversion back to a number to insert into the DB if it is possible for the user to edit this number. (second most recommended)
Lastly, you can always store the value in a string format and use lpad as above on insert.
I wouldn't recommend this as strings take up much more space than numbers, and the db won't search them as fast. Also, why store a number as a string purely for the user's sake, when you can change the way your data looks to the user programatically?
I'm trying to find an efficient, generic way to convert from string to a number in PL/SQL, where the local setting for NLS_NUMERIC_CHARACTERS settings is inpredictable -- and preferable I won't touch it. The input format is the programming standard "123.456789", but with an unknown number of digits on each side of the decimal point.
select to_number('123.456789') from dual;
-- only works if nls_numeric_characters is '.,'
select to_number('123.456789', '99999.9999999999') from dual;
-- only works if the number of digits in the format is large enough
-- but I don't want to guess...
to_number accepts a 3rd parameter but in that case you to specify a second parameter too, and there is no format spec for "default"...
select to_number('123.456789', null, 'nls_numeric_characters=''.,''') from dual;
-- returns null
select to_number('123.456789', '99999D9999999999', 'nls_numeric_characters=''.,''') from dual;
-- "works" with the same caveat as (2), so it's rather pointless...
There is another way using PL/SQL:
CREATE OR REPLACE
FUNCTION STRING2NUMBER (p_string varchar2) RETURN NUMBER
IS
v_decimal char;
BEGIN
SELECT substr(VALUE, 1, 1)
INTO v_decimal
FROM NLS_SESSION_PARAMETERS
WHERE PARAMETER = 'NLS_NUMERIC_CHARACTERS';
return to_number(replace(p_string, '.', v_decimal));
END;
/
select string2number('123.456789') from dual;
which does exactly what I want, but it doesn't seem efficient if you do it many, many times in a query. You cannot cache the value of v_decimal (fetch once and store in a package variable) because it doesn't know if you change your session value for NLS_NUMERIC_CHARACTERS, and then it would break, again.
Am I overlooking something? Or am I worrying too much, and Oracle does this a lot more efficient then I'd give it credit for?
The following should work:
SELECT to_number(:x,
translate(:x, '012345678-+', '999999999SS'),
'nls_numeric_characters=''.,''')
FROM dual;
It will build the correct second argument 999.999999 with the efficient translate so you don't have to know how many digits there are beforehand. It will work with all supported Oracle number format (up to 62 significant digits apparently in 10.2.0.3).
Interestingly, if you have a really big string the simple to_number(:x) will work whereas this method will fail.
Edit: support for negative numbers thanks to sOliver.
If you are doing a lot of work per session, an option may be to use
ALTER SESSION SET NLS_NUMERIC_CHARACTERS = '.,'
at the beginning of your task.
Of course, if lots of other code is executed in the same session, you may get funky results :-)
However we are able to use this method in our data load procedures, since we have dedicated programs with their own connection pools for loading the data.
Sorry, I noticed later that your question was for the other way round. Nevertheless it's noteworthy that for the opposite direction there is an easy solution:
A bit late, but today I noticed the special format masks 'TM9' and 'TME' which are described as "the text minimum number format model returns (in decimal output) the smallest number of characters possible." on https://docs.oracle.com/cloud/latest/db112/SQLRF/sql_elements004.htm#SQLRF00210.
It seems as if TM9 was invented just to solve this particular problem:
select to_char(1234.5678, 'TM9', 'NLS_NUMERIC_CHARACTERS=''.,''') from dual;
The result is '1234.5678' with no leading or trailing blanks, and a decimal POINT despite my environ containing NLS_LANG=GERMAN_GERMANY.WE8MSWIN1252, which would normally cause a decimal COMMA.
select to_number(replace(:X,'.',to_char(0,'fmd'))) from dual;
btw
select to_number(replace('1.2345e-6','.',to_char(0,'fmd'))) from dual;
and if you want more strict
select to_number(translate(:X,to_char(0,'fmd')||'.','.'||to_char(0,'fmd'))) from dual;
Is it realistic that the number of digits is unlimited?
If we assume it is then isn't it a good reason to look into the requirements more carefully?
If we have that fantastic situation when the initial string is super long, then the following does the trick:
select
to_number(
'11111111.2222'
, 'FM' || lpad('9', 32, '9') || 'D' || lpad('9', 30, '9')
, 'NLS_NUMERIC_CHARACTERS=''.,'''
)
from
dual