Oracle : large Number getting converted scientific notation (40 digits) on concatenation - sql

Oracle converts Number to string on concatenation or moving it to_char.
Example:
select value from tab;
value
9076725748515642018090620180906173606980000000000000000000000000000000000000000
select RPAD(value,LENGTH(value)+1,chr(135)) from tab;
RPAD(value,LENGTH(value)+1,chr(135))
9.0767257485156420180906201809061736E+78‡
Intended result
9076725748515642018090620180906173606980000000000000000000000000000000000000000‡

Maybe PL/SQL can help you. You can try below script.
CREATE OR REPLACE FUNCTION test123 (p_number NUMBER) return VARCHAR2
IS
v_no VARCHAR2(100);
BEGIN
EXECUTE IMMEDIATE
'select '''||p_number||'''||CHR(135) '||
' from dual'
INTO v_no;
RETURN v_no;
END;
/
SELECT TEST123(9076725748515642018090620180906173606980000000000000000000000000000000000000000)
FROM DUAL;
OUTPUT
--------------------------------------------------------------------------------
9076725748515642018090620180906173606980000000000000000000000000000000000000000‡

Related

Oracle Function or Query to get data for all DB tables?

I have to get the min and max dates for the data stored in all the database tables and display same along with the table names. I have written below function to do same
CREATE OR REPLACE FUNCTION data_bound(tbl_name IN VARCHAR2) RETURN VARCHAR2
AS
reqdates VARCHAR2;
BEGIN
EXECUTE IMMEDIATE 'SELECT concat(min(rec_date),max(rec_date)) from ' || tbl_name INTO reqdates;
RETURN reqdates;
END;
And I am trying to fetch data using below.
SELECT table_name, data_bound(table_name) FROM user_tables;
But my function does seems to be working, getting multiple errors. Please can you advise what's wrong here and if there's another better approach.
You can use:
CREATE FUNCTION data_bound(
tbl_name IN VARCHAR2
) RETURN VARCHAR2
AS
reqdates VARCHAR2(39);
BEGIN
EXECUTE IMMEDIATE 'SELECT TO_CHAR(min(rec_date), ''YYYY-MM-DD HH24:MI:SS'')
|| ''-'' || TO_CHAR(max(rec_date), ''YYYY-MM-DD HH24:MI:SS'')
FROM ' || DBMS_ASSERT.SIMPLE_SQL_NAME(tbl_name)
INTO reqdates;
RETURN reqdates;
END;
/
Which, if you have the table:
CREATE TABLE table_name (rec_date) AS
SELECT TRUNC(SYSDATE, 'YYYY') FROM DUAL UNION ALL
SELECT SYSDATE FROM DUAL;
Then:
SELECT data_bound('TABLE_NAME') FROM DUAL;
Outputs:
DATA_BOUND('TABLE_NAME')
2022-01-01 00:00:00-2022-04-19 18:57:25
db<>fiddle here
Error 1: data_bound() function is defined. data_retention() function is called
Error 2: tbl_name is taken as input, but not used (t_name is used once)
Error 3: 'from' keyword is missing in the execute immedeate statement
Error 4: 'into' keyword should be placed before 'from tableName'

String Pattern Matching in Sql / Pl/sql

I need to match a string to a pattern to validate the given string.
The given string could be like this 1234/5678.
I should validate the string in such a way that the first four and the last four characters will have to be numbers and they must be seperated by a slash.
How can I do this in SQL or PL/SQL?
I tried different functions such as REGEXP_LIKE, REGEXP_REPLACE,REGEXP_SUBSTR.
Can anyone please help me on this?
If this needs to be done in PL/SQL (e.g. you're validating user input, rather than data in a table), you can create a function to do the validation, e.g.:
DECLARE
v_str VARCHAR2(10);
FUNCTION validate_string (in_str VARCHAR2) RETURN BOOLEAN
IS
BEGIN
RETURN regexp_like(in_str, '\d{4}/\d{4}');
END validate_string;
PROCEDURE validation_output (in_str VARCHAR2)
IS
BEGIN
IF validate_string (in_str => in_str) THEN
dbms_output.put_line(in_str||': validated');
ELSE
dbms_output.put_line(in_str||': not validated');
END IF;
END validation_output;
BEGIN
v_str := '1234/5678';
validation_output (v_str);
v_str := '12/5678';
validation_output (v_str);
v_str := NULL;
validation_output (v_str);
END;
/
1234/5678: validated
12/5678: not validated
: not validated
if you are using oracle you can user regexp_like
https://www.techonthenet.com/oracle/regexp_like.php
if you are using mysql regexp or rlike
https://dev.mysql.com/doc/refman/5.5/en/regexp.html
for sqlserver IsMatch()
https://github.com/zzzprojects/Eval-SQL.NET/wiki/SQL-Server-Regex-%7C-Use-regular-expression-to-search,-replace-and-split-text-in-SQL#sql-regex---ismatch
ORACLE
SELECT * FROM T WHERE COL REGEXP_LIKE REGULAREXP
MYSQL
SELECT * FROM T WHERE COL RLIKE REGULAREXP
SELECT * FROM T WHERE COL REGEXP REGULAREXP
sample table:
SELECT * FROM ns_98;
4321/4567
43/45
43898/4521
4388/4521
43885/45215
4388///4521
SELECT a
FROM ns_98
WHERE REGEXP_LIKE (a,'^[0-9]{4}/{1}[0-9]{4}$');
output:
4321/4567
4388/4521

Encrypting strings in SQL (likely ORACLE PL SQL). Caesar Cipher

I need something like the Caesar Cipher to be used in my string columns for the every value in each column. It should be made something like n+1:
ABcd012Ab -> BCde123Bc
The string characters may be null, may contain sepparators (, - etc.), they may be upper and lower case (it doesnt matter).
Finaly, it should be created as a procedure, and this procedure should be then used inside an UPDATE query.
It shold maybe look something like this:
Create procedure text_change(n varchar(1000))
declare #i char
declare #l varchar(100 char)
begin
For each #l in n
For each #i in #l
loop
#i = ????
end loop;
return #l;
end;
UPDATE name_of_table
SET name_of_column = text_change(column)
Would be very happy for any help!
Why restrict yourself to Caesar Cipher? You could make use of DBMS_CRYPTO package which allows you to use Data Encryption Standard (DES)
Docs
Firstly, get execute permission to this package from DBA.
SQL> GRANT EXECUTE ON DBMS_CRYPTO TO HR;
Grant succeeded.
Then create a function like this.
CREATE OR REPLACE FUNCTION my_encrypt(
p_source VARCHAR2,
p_key VARCHAR2 )
RETURN VARCHAR2
AS
BEGIN
RETURN UTL_RAW.CAST_TO_VARCHAR2 ( DBMS_CRYPTO.encrypt( UTL_RAW.CAST_TO_RAW (p_source),
dbms_crypto.DES_CBC_PKCS5, UTL_RAW.CAST_TO_RAW (p_key) ) );
END;
/
This uses DES_CBC_PKCS5 Block Cipher Suite.
So, when you run a query like this you get encrypted data.
SQL> SELECT my_encrypt('TREASURE UNDER OAK TREE',
2 'The DBMS_CRYPTO package replaces DBMS_OBFUSCATION_TOOLKIT') AS
3 encrypted
4 FROM dual;
ENCRYPTED
----------------------------
┐↨┐┐♣!┐ o)|┐┐┐┐┐┐┐┐
Decrypt function
CREATE OR REPLACE FUNCTION my_decrypt ( p_source VARCHAR2, p_key VARCHAR2 )
RETURN VARCHAR2 AS
BEGIN
RETURN UTL_RAW.CAST_TO_VARCHAR2 ( DBMS_CRYPTO.decrypt( UTL_RAW.CAST_TO_RAW (p_source), dbms_crypto.DES_CBC_PKCS5, UTL_RAW.CAST_TO_RAW (p_key) ) );
END;
/
SQL> SELECT my_decrypt( my_encrypt('TREASURE UNDER OAK TREE',
2 'The DBMS_CRYPTO package replaces DBMS_OBFUSCATION_TOOLKIT') ,
3 'The DBMS_CRYPTO package replaces DBMS_OBFUSCATION_TOOLKIT') AS
4 decrypted
5 FROM dual;
DECRYPTED
---------------------------------
TREASURE UNDER OAK TREE
You could also use it to encrypt and decrypt the columns in the table.
update yourtable set SOMETEXT =
my_encrypt(SOMETEXT,'The DBMS_CRYPTO package replaces DBMS_OBFUSCATION_TOOLKIT');
update yourtable set SOMETEXT =
my_decrypt(SOMETEXT,'The DBMS_CRYPTO package replaces DBMS_OBFUSCATION_TOOLKIT');
If you only want to "encrypt" alphanumerics, you might try the following. You didn't specify what you wanted done with 9, Z, or z so I just took the next ASCII character (:, [, and { respectively):
SELECT mycolumn
, TRANSLATE( mycolumn
, '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
, '123456789:BCDEFGHIJKLMNOPQRSTUVWXYZ[bcdefghijklmnopqrstuvwxyz{' )
FROM mytable;
Hope this helps.
EDIT: I'm not sure why I've continued to think about this, but here is a general solution with user-defined function using Oracle's TRANSLATE() function. It doesn't include numbers but I'm sure those would be an easy addition:
CREATE OR REPLACE FUNCTION caesar_cipher
( p_source IN VARCHAR2, p_offset IN PLS_INTEGER )
RETURN VARCHAR2
IS
c_abc CONSTANT VARCHAR2(128) := 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
v_offset PLS_INTEGER;
v_target VARCHAR2(32767);
v_transl VARCHAR2(128);
BEGIN
v_offset := MOD( p_offset, LENGTH(c_abc) );
IF (v_offset < 0) THEN
v_offset := v_offset + LENGTH(c_abc);
END IF;
v_transl := SUBSTR(c_abc, v_offset+1) || SUBSTR(c_abc, 1, v_offset);
v_target := TRANSLATE( p_source, c_abc || LOWER(c_abc), v_transl || LOWER(v_transl) );
RETURN v_target;
END;
/
To "decrypt" use a negative value for the offset instead of a positive one; that is, CAESAR_CIPHER('CDE', -2) is the opposite of CAESAR_CIPHER('ABC', 2). It strikes me as more efficient than examining every character of the source string but I've not run a test to be sure.

Ways of handling unknown data type in oracle

I have a statement that executes a sql like this:
execute immediate cursor_rule.rule_sql into rule_result ;
my problem is that the output of rule_sql can be anything from null, to boolean to a number.
How do I define rule_result in a situation like this?
You can use:
DECLARE
rule_result VARCHAR2(4000);
BEGIN
EXECUTE IMMEDIATE :your_sql INTO rule_result;
EXCEPTION
WHEN NO_DATA_FOUND THEN
NULL; -- Handle what should happen when the SQL returns zero rows.
WHEN TOO_MANY_ROWS THEN
NULL; -- Handle what should happen when the SQL returns two or more rows.
END;
/
If the result of your sql statement is a:
String data type then it gets stored in the rule_result as is.
numeric data type then Oracle will implicitly call TO_CHAR on it to convert it to a VARCHAR2 value exactly long enough to hold its significant digits.
DATE data type then Oracle will implicitly call TO_CHAR( date_value, NLS_DATE_FORMAT ) using the NLS_DATE_FORMAT session parameter as the format model to convert it to a string.
TIMESTAMP data type then Oracle will implicitly call TO_CHAR( timestamp_value, NLS_TIMESTAMP_FORMAT ) using the NLS_TIMESTAMP_FORMAT session parameter as the format model to convert it to a string.
You can parse the SQL statement using DBMS_SQL to discover the column data type. For example:
declare
l_cursor_id pls_integer := dbms_sql.open_cursor;
l_pointless_count pls_integer;
l_desc_cols dbms_sql.desc_tab;
l_sql long := 'select dummy as teststring, 123 as testnum, sysdate as testdate from dual';
begin
dbms_sql.parse(l_cursor_id, l_sql, dbms_sql.native);
dbms_sql.describe_columns(l_cursor_id, l_pointless_count, l_desc_cols);
for i in 1..l_desc_cols.count loop
dbms_output.put_line
( rpad(l_desc_cols(i).col_name,31) || lpad(l_desc_cols(i).col_type,4) );
end loop;
dbms_sql.close_cursor(l_cursor_id);
end;
Output:
TESTSTRING 1
TESTNUM 2
TESTDATE 12
Type codes are defined in DBMS_TYPES and the documentation (which as I discovered last week do not necessarily agree).

comma separated parameter in plsql stored procedure

create or replace procedure PROC_MYDATA (inputStr IN VARCHAR2,
p_RecordSet IN OUT SYS_REFCURSOR) is
begin
OPEN p_RecordSet FOR
(select * from myTable where name in (inputStr));
end PROC_MYDATA;
In the PLSQL Test window, I am trying to set,
inputStr = 'A','B'
and I am getting this error:
ORA-01722: invalid number
I also tried to put escape character for single quote.
inputStr = '''A''','''B'''
Same error.
Can someone please help me understand what am I doing wrong?
I'm afraid it doesn't work this way:
SELECT * from myTable where name in (inputStr);
You can use dynamic SQL, as in #Bob Jarvis' answer, or you can do the following:
SELECT * FROM myTable WHERE REGEXP_LIKE(name, '^(' || REPLACE(inputStr, ',', '|') || ')$');
The difficulty with the latter is that, in Oracle, a regular expression can be at most 512 bytes long. So your inputStr would be limited to 508 bytes (since we're adding four bytes for the anchors and the grouping).
To use a list of comma-separated values you'll need to build and execute the statement dynamically:
create or replace procedure PROC_MYDATA (inputStr IN VARCHAR2,
p_RecordSet IN OUT SYS_REFCURSOR)
is
strSql VARCHAR2(32767);
begin
strSql := 'select * from myTable where name in (' || inputStr || ')';
OPEN p_RecordSet FOR strSql;
end PROC_MYDATA;
You should use this with a string which contains the single-quote characters in it to delimit each string; thus, use
DECLARE
inputStr VARCHAR2(100);
csrCursor SYS_REFCURSOR;
BEGIN
inputStr = '''A'', ''B''';
PROC_MYDATA(inputStr, csrCursor);
-- ...code to use csrCursor;
CLOSE csrCursor;
END;
Share and enjoy.
This is faster
SELECT * from myTable where name in (select regexp_substr(inputStr,'[^,]+', 1, level) from dual
connect by regexp_substr(inputStr, '[^,]+', 1, level) is not null);