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

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

Related

Converting base32 to a decimal number

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!!

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

Select where record does not exists

I am trying out my hands on oracle 11g. I have a requirement such that I want to fetch those id from list which does not exists in table.
For example:
SELECT * FROM STOCK
where item_id in ('1','2'); // Return those records where result is null
I mean if item_id '1' is not present in db then the query should return me 1.
How can I achieve this?
You need to store the values in some sort of "table". Then you can use left join or not exists or something similar:
with ids as (
select 1 as id from dual union all
select 2 from dual
)
select ids.id
from ids
where not exists (select 1 from stock s where s.item_id = ids.id);
You can use a LEFT JOIN to an in-line table that contains the values to be searched:
SELECT t1.val
FROM (
SELECT '1' val UNION ALL SELECT '2'
) t1
LEFT JOIN STOCK t2 ON t1.val = t2.item_id
WHERE t2.item_id IS NULL
First create the list of possible IDs (e.g. 0 to 99 in below query). You can use a recursive cte for this. Then select these IDs and remove the IDs already present in the table from the result:
with possible_ids(id) as
(
select 0 as id from dual
union all
select id + 1 as id from possible_ids where id < 99
)
select id from possible_ids
minus
select item_id from stock;
A primary concern of the OP seems to be a terse notation of the query, notably the set of values to test for. The straightforwwrd recommendation would be to retrieve these values by another query or to generate them as a union of queries from the dual table (see the other answers for this).
The following alternative solution allows for a verbatim specification of the test values under the following conditions:
There is a character that does not occur in any of the test values provided ( in the example that will be - )
The number of values to test stays well below 2000 (to be precise, the list of values plus separators must be written as a varchar2 literal, which imposes the length limit ). However, this should not be an actual concern - If the test involves lists of hundreds of ids, these lists should definitely be retrieved froma table/view.
Caveat
Whether this method is worth the hassle ( not to mention potential performance impacts ) is questionable, imho.
Solution
The test values will be provided as a single varchar2 literal with - separating the values which is as terse as the specification as a list argument to the IN operator. The string starts and ends with -.
'-1-2-3-156-489-4654648-'
The number of items is computed as follows:
select cond, regexp_count ( cond, '[-]' ) - 1 cnt_items from (select '-1-2-3-156-489-4654648-' cond from dual)
A list of integers up to the number of items starting with 1 can be generated using the LEVEL pseudocolumn from hierarchical queries:
select level from dual connect by level < 42;
The n-th integer from that list will serve to extract the n-th value from the string (exemplified for the 4th value) :
select substr ( cond, instr(cond,'-', 1, 4 )+1, instr(cond,'-', 1, 4+1 ) - instr(cond,'-', 1, 4 ) - 1 ) si from (select cond, regexp_count ( cond, '[-]' ) - 1 cnt_items from (select '-1-2-3-156-489-4654648-' cond from dual) );
The non-existent stock ids are generated by subtracting the set of stock ids from the set of values. Putting it all together:
select substr ( cond, instr(cond,'-',1,level )+1, instr(cond,'-',1,level+1 ) - instr(cond,'-',1,level ) - 1 ) si
from (
select cond
, regexp_count ( cond, '[-]' ) - 1 cnt_items
from (
select '-1-2-3-156-489-4654648-' cond from dual
)
)
connect by level <= cnt_items + 1
minus
select item_id from stock
;

sql query to calculate odd and even place digits sum

I need a SQL query/function which will find out SUM of digits, which is present in even positions and SUM of digits which is present in odd position.
Example:
If number is 440065385:
x=4+0+6+3+5=18(x is holding sum of odd positions)
y=4+0+5+8=17 (y holds the sum of even positions).
Thanks.
You can mimic an iteration using level and connect by to achieve the same
with iterate as
(select level as i from dual connect by level <=25)
select
table1.column1,
sum(decode(mod(iterate.i,2),0,substr(table1.column1,iterate.i,1))) sum_even,
sum(decode(mod(iterate.i,2),1,substr(table1.column1,iterate.i,1))) sum_old
from table1 ,iterate
where iterate.i <=length(table1.column1)
group by table1.column1
You could join your table with a numbers table, to split all digits into rows, then sum odds and even position digits separately.
with positions(position) as
(select level from dual connect by level <=25
),
digits as
(
select
t1.v,
n.position ,
cast(substr(t1.v ,length(t1.v) - n.position + 1, 1) as int) as digit
from my_table t1 left join positions n
on n.position <= length(t1.v)
)
select
v,
sum(case when mod(position,2)=0 then digit else 0 end) as evensum,
sum(case when mod(position,2)=1 then digit else 0 end) as oddsum
from
digits
group by
v
Test Sql Fiddle, the result:
V EVENSUM ODDSUM
440065385 17 18

How do I sort a VARCHAR column in SQL server that contains numbers?

I have a VARCHAR column in a SQL Server 2000 database that can contain either letters or numbers. It depends on how the application is configured on the front-end for the customer.
When it does contain numbers, I want it to be sorted numerically, e.g. as "1", "2", "10" instead of "1", "10", "2". Fields containing just letters, or letters and numbers (such as 'A1') can be sorted alphabetically as normal. For example, this would be an acceptable sort order.
1
2
10
A
B
B1
What is the best way to achieve this?
One possible solution is to pad the numeric values with a character in front so that all are of the same string length.
Here is an example using that approach:
select MyColumn
from MyTable
order by
case IsNumeric(MyColumn)
when 1 then Replicate('0', 100 - Len(MyColumn)) + MyColumn
else MyColumn
end
The 100 should be replaced with the actual length of that column.
There are a few possible ways to do this.
One would be
SELECT
...
ORDER BY
CASE
WHEN ISNUMERIC(value) = 1 THEN CONVERT(INT, value)
ELSE 9999999 -- or something huge
END,
value
the first part of the ORDER BY converts everything to an int (with a huge value for non-numerics, to sort last) then the last part takes care of alphabetics.
Note that the performance of this query is probably at least moderately ghastly on large amounts of data.
select
Field1, Field2...
from
Table1
order by
isnumeric(Field1) desc,
case when isnumeric(Field1) = 1 then cast(Field1 as int) else null end,
Field1
This will return values in the order you gave in your question.
Performance won't be too great with all that casting going on, so another approach is to add another column to the table in which you store an integer copy of the data and then sort by that first and then the column in question. This will obviously require some changes to the logic that inserts or updates data in the table, to populate both columns. Either that, or put a trigger on the table to populate the second column whenever data is inserted or updated.
SELECT *, CONVERT(int, your_column) AS your_column_int
FROM your_table
ORDER BY your_column_int
OR
SELECT *, CAST(your_column AS int) AS your_column_int
FROM your_table
ORDER BY your_column_int
Both are fairly portable I think.
you can always convert your varchar-column to bigint as integer might be too short...
select cast([yourvarchar] as BIGINT)
but you should always care for alpha characters
where ISNUMERIC([yourvarchar] +'e0') = 1
the +'e0' comes from http://blogs.lessthandot.com/index.php/DataMgmt/DataDesign/isnumeric-isint-isnumber
this would lead to your statement
SELECT
*
FROM
Table
ORDER BY
ISNUMERIC([yourvarchar] +'e0') DESC
, LEN([yourvarchar]) ASC
the first sorting column will put numeric on top.
the second sorts by length, so 10 will preceed 0001 (which is stupid?!)
this leads to the second version:
SELECT
*
FROM
Table
ORDER BY
ISNUMERIC([yourvarchar] +'e0') DESC
, RIGHT('00000000000000000000'+[yourvarchar], 20) ASC
the second column now gets right padded with '0', so natural sorting puts integers with leading zeros (0,01,10,0100...) in correct order (correct!) - but all alphas would be enhanced with '0'-chars (performance)
so third version:
SELECT
*
FROM
Table
ORDER BY
ISNUMERIC([yourvarchar] +'e0') DESC
, CASE WHEN ISNUMERIC([yourvarchar] +'e0') = 1
THEN RIGHT('00000000000000000000' + [yourvarchar], 20) ASC
ELSE LTRIM(RTRIM([yourvarchar]))
END ASC
now numbers first get padded with '0'-chars (of course, the length 20 could be enhanced) - which sorts numbers right - and alphas only get trimmed
I solved it in a very simple way writing this in the "order" part
ORDER BY (
sr.codice +0
)
ASC
This seems to work very well, in fact I had the following sorting:
16079 Customer X
016082 Customer Y
16413 Customer Z
So the 0 in front of 16082 is considered correctly.
This seems to work:
select your_column
from your_table
order by
case when isnumeric(your_column) = 1 then your_column else 999999999 end,
your_column
This query is helpful for you. In this query, a column has data type varchar is arranged by good order.For example- In this column data are:- G1,G34,G10,G3. So, after running this query, you see the results: - G1,G10,G3,G34.
SELECT *,
(CASE WHEN ISNUMERIC(column_name) = 1 THEN 0 ELSE 1 END) IsNum
FROM table_name
ORDER BY IsNum, LEN(column_name), column_name;
This may help you, I have tried this when i got the same issue.
SELECT *
FROM tab
ORDER BY IIF(TRY_CAST(val AS INT) IS NULL, 1, 0),TRY_CAST(val AS INT);
The easiest and efficient way to get the job done is using TRY_CAST
SELECT my_column
FROM my_table
WHERE <condition>
ORDER BY TRY_CAST(my_column AS NUMERIC) DESC
This will sort all numbers in descending order and push down all non numeric values
SELECT FIELD FROM TABLE
ORDER BY
isnumeric(FIELD) desc,
CASE ISNUMERIC(test)
WHEN 1 THEN CAST(CAST(test AS MONEY) AS INT)
ELSE NULL
END,
FIELD
As per this link you need to cast to MONEY then INT to avoid ordering '$' as a number.
SELECT *,
ROW_NUMBER()OVER(ORDER BY CASE WHEN ISNUMERIC (ID)=1 THEN CONVERT(NUMERIC(20,2),SUBSTRING(Id, PATINDEX('%[0-9]%', Id), LEN(Id)))END DESC)Rn ---- numerical
FROM
(
SELECT '1'Id UNION ALL
SELECT '25.20' Id UNION ALL
SELECT 'A115' Id UNION ALL
SELECT '2541' Id UNION ALL
SELECT '571.50' Id UNION ALL
SELECT '67' Id UNION ALL
SELECT 'B48' Id UNION ALL
SELECT '500' Id UNION ALL
SELECT '147.54' Id UNION ALL
SELECT 'A-100' Id
)A
ORDER BY
CASE WHEN ISNUMERIC (ID)=0 /* alphabetical sort */
THEN CASE WHEN PATINDEX('%[0-9]%', Id)=0
THEN LEFT(Id,PATINDEX('%[0-9]%',Id))
ELSE LEFT(Id,PATINDEX('%[0-9]%',Id)-1)
END
END DESC