Converting base32 to a decimal number - sql

Is there anybody who can help me converting base32 values to decimal numbers?
I have a sql statement which returns me a list of base32 values. For example the return of the select looks like this:
5
8
H
13r
Now I need to figure out which of these values is the highest (in this case 13r). Therefore I would need a function to convert these base32 values to decimal numbers in order to sort them.
Does anybody have such a function or does anybody have a different approach?

It will be little bit tricky.
You can create a function which will separate each character/digit from the original base32 number and multiply it with 32^(position in base32 number) to get decimal equivalent number and use that function in your query.
create function convert32todecimal(p_num in varchar2)
return number
as
lv_outnum number;
begin
select sum(val) into lv_outnum from
(
select power(32,pos) * case when d between '0' and '9'
then to_number(d)
else 10 + ascii(d) - ascii('A')
end as val
from (
select upper(substr(p_num,length(p_num)+1-level,1)) d,
level - 1 as pos
from dual
connect by level <= length(p_num)
)
);
return lv_outnum;
end;
/
Now, use this function in your query as following:
with your_data(num) as (
select '5' from dual union all
select '8' from dual union all
select 'H' from dual union all
select '13r' from dual
)
select num, convert32todecimal(num)
from your_data
order by 2 desc;
db<>fiddle demo
Cheers!!

Related

How to compare Hexadecimal values in oracle SQL

I have a column with HEXADECIMAL values and I want to do a comparison with other HEXADECIMAL value like below but I am getting inaccurate results
SELECT vc_vin_vis_start_range FROM ok_dc.gms3_vc_mme_vin_si_detail
WHERE TO_NUMBER(vc_vin_vis_start_range) >= TO_NUMBER('100001');
Is there a way to compare two HEXADECIMAL values in a query.
Any help is appreciated!
Use TO_NUMBER( hex_value, , 'xxxxxxxxxx' ) (where the number of xs is the maximum length of your hexadecimal VARCHAR2 column):
Oracle Setup:
CREATE TABLE ok_dc.gms3_vc_mme_vin_si_detail ( vc_vin_vis_start_range ) AS
SELECT '1' FROM DUAL UNION ALL
SELECT 'FFFFF' FROM DUAL UNION ALL
SELECT '100001' FROM DUAL UNION ALL
SELECT '100002' FROM DUAL UNION ALL
SELECT NULL FROM DUAL;
Query:
SELECT vc_vin_vis_start_range
FROM ok_dc.gms3_vc_mme_vin_si_detail
WHERE TO_NUMBER(vc_vin_vis_start_range, 'xxxxxxxxxx') >= TO_NUMBER('100001', 'xxxxxxxxxx');
Output:
VC_VIN_VIS_START_RANGE
----------------------
100001
100002
Assuming the column vc_vin_vis_start_range does not have null values, then I would do:
SELECT vc_vin_vis_start_range FROM ok_dc.gms3_vc_mme_vin_si_detail
WHERE TO_NUMBER(vc_vin_vis_start_range,
rpad('x', length(vc_vin_vis_start_range) , 'x'))
>= TO_NUMBER('100001', 'xxxxxx');
The rpad() function builds an hex format according to the each value you want to convert.

Is there any function in oracle to keep a string length fixed?

I have a scenario like when a column value exceeds the length of 10 characters, I need to take a sub-string for only 10 characters (left most) but if it is shorter than that it should be left padded with zeroes. I tried the following:
with data1 as (select '1234567890123' as dummy1 from dual)
select CASE when (length(dummy1)>10) then substr(dummy1,1,10) else lpad(dummy1,10,'0') end from data1;
But this seems to me quite a longer way to do. Is there any shorter way to achieve this, maybe an Oracle function?
I tried to Google this but could not find any relevant result.
lpad is enough to do the job :
SELECT LPAD( '1234567890123', 10, '0' ) AS formatted
FROM dual;
Just use SUBSTR and LPAD together:
WITH data ( value ) AS (
SELECT '1234567890123' FROM DUAL UNION ALL
SELECT '1' FROM DUAL
)
SELECT LPAD( SUBSTR( value, 1, 10 ), 10, '0' ) AS formatted
FROM data;
Output:
FORMATTED
----------
1234567890
0000000001

selecting the value from column having highest digit count after decimal places

I have the below table named SAXTION_EG and it this table contain various colulms out of which there is one column named STR_RATE and in this columncontain values like
STR_RATE
1.11317
123.08546759
8.49111
now please advise me the oracle query by which i can select the maximum value in terms of decimal point for example in the above mentioned case the value 123.08546759 has highest decinal count so it has 8 digits count after decimal, as my objective is to fetch the value having highest digit count after decimal
You can try some thing like this. Logic is first get the position of the decimal point. Then get the string after the decimal. After that count the no of chars in that substring. Then use the MAX to get the aggregated max value
SELECT MAX(LENGTH(SUBSTR(STR_RATE, INSTR(STR_RATE, '.')+ 1)))
FROM your_table
In the solution below, I assume str_rate is of data type NUMBER, so it must be converted to character first. I added a couple of sample values to check that integer values are treated correctly, and to illustrate a case when two values have the same, highest number of decimal digits. In this case the solution returns both such values (see result set at the bottom).
with
saxtion_eg ( str_rate ) as (
select 1.11317 from dual union all
select 123.08546759 from dual union all
select 8.49111 from dual union all
select 582 from dual union all
select 0.00000001 from dual
),
prep ( str_rate, char_rate, pos ) as (
select str_rate, to_char(str_rate), instr(to_char(str_rate), '.')
from saxtion_eg
),
final ( char_rate, dec_digits, max_dec_digits ) as (
select char_rate,
case pos when 0 then 0 else length(char_rate) - pos end,
max(case pos when 0 then 0 else length(char_rate) - pos end) over ()
from prep
)
select char_rate as str_rate
from final
where dec_digits = max_dec_digits
;
STR_RATE
----------------
123.08546759
.00000001
This query return the maximum value in terms of decimal point:
select max(STR_RATE) keep (dense_rank last order by length(STR_RATE-trunc(STR_RATE)))
from SAXTION_EG

convert a function into SQL statement

I have a below function for rounding the value of an amount based on some logic -
Currently I use this round function in my SQL to get the rounded value of amount as
f_round_value(nom_ccy_cd,tran_amt) -
However, my current requirement is to not use this function. Instead I am trying to achieve the same in SQL directly. Should I use case statements, or any other way to achieve the below logic..
I am using oracle 10i
Function f_round_value ( in_t_ccy_cd IN CCY.ccy_cd%TYPE, in_n_amt IN NUMBER)
RETURN VARCHAR2 AS
ln_dec_place_cnt CCY.decimal_place_cnt%TYPE;
ln_out_amt NUMBER;
lv_out_amt_str VARCHAR2(30);
lb_decimal_reqd BOOLEAN :=TRUE;
lb_neg_val BOOLEAN :=FALSE;
BEGIN
IF in_n_amt IS NULL THEN
lv_out_amt_str:=NULL;
ELSE IF in_n_amt < 0 THEN
lb_neg_val:=TRUE;
END IF;
IF in_t_ccy_cd IN (C_CCY_CD_JP, C_CCY_CD_IT, C_CCY_CD_IR, C_CCY_CD_KR) THEN
ln_dec_place_cnt :=0;
lb_decimal_reqd:=FALSE;
ELSE
ln_dec_place_cnt :=2; lb_decimal_reqd:=TRUE;
END IF;
ln_out_amt:=ROUND(in_n_amt,ln_dec_place_cnt);
IF lb_decimal_reqd THEN
lv_out_amt_str:=TRIM(TO_CHAR(ln_out_amt,'S999,999,999,999,990.99'));
ELSE
lv_out_amt_str:=TRIM(TO_CHAR(ln_out_amt,'S999,999,999,999,999'));
END IF;
IF lb_neg_val THEN
lv_out_amt_str:='('||SUBSTR(lv_out_amt_str,2)||')';
ELSE
lv_out_amt_str:= SUBSTR(lv_out_amt_str,2);
END IF;
END
Any help will be appreciated.
You do realize there are currencies with 3 decimal places don't you? Anyway, you don't show the complete contents of the CCY table, but if it should contain the decimal places of each currency, you're in luck. You have everything you need. Here is a sample CCY table with 4 currencies and a list with a value for each currency.
WITH
CCY AS(
SELECT 'BGN' ccy_cd, '975' ccy_id, 2 DecPlaces, 'Bulgarian lev' CCY_Name, 'Bulgaria' Cntry FROM dual UNION ALL
SELECT 'BHD', '048', 3, 'Bahraini dinar', 'Bahrain' FROM dual UNION ALL
SELECT 'BIF', '108', 0, 'Burundian franc', 'Burundi' FROM dual UNION ALL
SELECT 'BMD', '060', 2, 'Bermudian dollar', 'Bermuda' FROM dual
),
CcyValues as(
SELECT 'BGN' ccy_cd, 15.852 amt FROM dual UNION ALL
SELECT 'BHD', -15.852 FROM dual UNION ALL
SELECT 'BIF', 15.852 FROM dual UNION ALL
SELECT 'BMD', -15.852 FROM dual
)
SELECT v.ccy_cd, v.amt, y.DecPlaces,
translate( to_char( round( v.amt, y.DecPlaces ),
CASE y.DecPlaces
WHEN 2 THEN 'FM999,999,999,999,990.99PR'
WHEN 3 THEN 'FM999,999,999,999,990.999PR'
ELSE 'FM999,999,999,999,990PR'
END ), '<>', '()' ) Amt_Str
FROM CcyValues v
JOIN CCY y
on y.ccy_cd = v.ccy_cd;

SQL to generate a list of numbers from 1 to 100

Using the DUAL table, how can I get a list of numbers from 1 to 100?
Your question is difficult to understand, but if you want to select the numbers from 1 to 100, then this should do the trick:
Select Rownum r
From dual
Connect By Rownum <= 100
Another interesting solution in ORACLE PL/SQL:
SELECT LEVEL n
FROM DUAL
CONNECT BY LEVEL <= 100;
Using Oracle's sub query factory clause: "WITH", you can select numbers from 1 to 100:
WITH t(n) AS (
SELECT 1 from dual
UNION ALL
SELECT n+1 FROM t WHERE n < 100
)
SELECT * FROM t;
Do it the hard way. Use the awesome MODEL clause:
SELECT V
FROM DUAL
MODEL DIMENSION BY (0 R)
MEASURES (0 V)
RULES ITERATE (100) (
V[ITERATION_NUMBER] = ITERATION_NUMBER + 1
)
ORDER BY 1
Proof: http://sqlfiddle.com/#!4/d41d8/20837
You could use XMLTABLE:
SELECT rownum
FROM XMLTABLE('1 to 100');
-- alternatively(useful for generating range i.e. 10-20)
SELECT (COLUMN_VALUE).GETNUMBERVAL() AS NUM
FROM XMLTABLE('1 to 100');
DBFiddle Demo
If you want your integers to be bound between two integers (i.e. start with something other than 1), you can use something like this:
with bnd as (select 4 lo, 9 hi from dual)
select (select lo from bnd) - 1 + level r
from dual
connect by level <= (select hi-lo from bnd);
It gives:
4
5
6
7
8
Peter's answer is my favourite, too.
If you are looking for more details there is a quite good overview, IMO, here.
Especially interesting is to read the benchmarks.
Using GROUP BY CUBE:
SELECT ROWNUM
FROM (SELECT 1 AS c FROM dual GROUP BY CUBE(1,1,1,1,1,1,1) ) sub
WHERE ROWNUM <=100;
Rextester Demo
A variant of Peter's example, that demonstrates a way this could be used to generate all numbers between 0 and 99.
with digits as (
select mod(rownum,10) as num
from dual
connect by rownum <= 10
)
select a.num*10+b.num as num
from digits a
,digits b
order by num
;
Something like this becomes useful when you are doing batch identifier assignment, and looking for the items that have not yet been assigned.
For example, if you are selling bingo tickets, you may want to assign batches of 100 floor staff (guess how i used to fund raise for sports). As they sell a batch, they are given the next batch in sequence. However, people purchasing the tickets can select to purchase any tickets from the batch. The question may be asked, "what tickets have been sold".
In this case, we only have a partial, random, list of tickets that were returned within the given batch, and require a complete list of all possibilities to determine which we don't have.
with range as (
select mod(rownum,100) as num
from dual
connect by rownum <= 100
),
AllPossible as (
select a.num*100+b.num as TicketNum
from batches a
,range b
order by num
)
select TicketNum as TicketsSold
from AllPossible
where AllPossible.Ticket not in (select TicketNum from TicketsReturned)
;
Excuse the use of key words, I changed some variable names from a real world example.
... To demonstrate why something like this would be useful
I created an Oracle function that returns a table of numbers
CREATE OR REPLACE FUNCTION [schema].FN_TABLE_NUMBERS(
NUMINI INTEGER,
NUMFIN INTEGER,
EXPONENCIAL INTEGER DEFAULT 0
) RETURN TBL_NUMBERS
IS
NUMEROS TBL_NUMBERS;
INDICE NUMBER;
BEGIN
NUMEROS := TBL_NUMBERS();
FOR I IN (
WITH TABLA AS (SELECT NUMINI, NUMFIN FROM DUAL)
SELECT NUMINI NUM FROM TABLA UNION ALL
SELECT
(SELECT NUMINI FROM TABLA) + (LEVEL*TO_NUMBER('1E'||TO_CHAR(EXPONENCIAL))) NUM
FROM DUAL
CONNECT BY
(LEVEL*TO_NUMBER('1E'||TO_CHAR(EXPONENCIAL))) <= (SELECT NUMFIN-NUMINI FROM TABLA)
) LOOP
NUMEROS.EXTEND;
INDICE := NUMEROS.COUNT;
NUMEROS(INDICE):= i.NUM;
END LOOP;
RETURN NUMEROS;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN NUMEROS;
WHEN OTHERS THEN
RETURN NUMEROS;
END;
/
Is necessary create a new data type:
CREATE OR REPLACE TYPE [schema]."TBL_NUMBERS" IS TABLE OF NUMBER;
/
Usage:
SELECT COLUMN_VALUE NUM FROM TABLE([schema].FN_TABLE_NUMBERS(1,10))--integers difference: 1;2;.......;10
And if you need decimals between numbers by exponencial notation:
SELECT COLUMN_VALUE NUM FROM TABLE([schema].FN_TABLE_NUMBERS(1,10,-1));--with 0.1 difference: 1;1.1;1.2;.......;10
SELECT COLUMN_VALUE NUM FROM TABLE([schema].FN_TABLE_NUMBERS(1,10,-2));--with 0.01 difference: 1;1.01;1.02;.......;10
If you want to generate the list of numbers 1 - 100 you can use the cartesian product of {1,2,3,4,5,6,6,7,8,9,10} X {0,10,20,30,40,50,60,70,80,90}
https://en.wikipedia.org/wiki/Cartesian_product
Something along the lines of the following:
SELECT
ones.num + tens.num
FROM
(
SELECT 1 num UNION ALL
SELECT 2 num UNION ALL
SELECT 3 num UNION ALL
SELECT 4 num UNION ALL
SELECT 5 num UNION ALL
SELECT 6 num UNION ALL
SELECT 7 num UNION ALL
SELECT 9 num UNION ALL
SELECT 10 num
) as ones
CROSS JOIN
(
SELECT 0 num UNION ALL
SELECT 10 num UNION ALL
SELECT 20 num UNION ALL
SELECT 30 num UNION ALL
SELECT 40 num UNION ALL
SELECT 50 num UNION ALL
SELECT 60 num UNION ALL
SELECT 70 num UNION ALL
SELECT 80 num UNION ALL
SELECT 90 num
) as tens;
I'm not able to test this out on an oracle database, you can place the dual where it belongs but it should work.
SELECT * FROM `DUAL` WHERE id>0 AND id<101
The above query is written in SQL in the database.