SQL Occurence character Replace issue - sql

I'm working on Oracle's SQL and I would like to have the following results :
4,500,400,40 --> 4 500 400,40
5400200,00 --> 5 400 200,00
1200,988,00 --> 1 200 988,00
I tried to use REGEXP_REPLACE but with no result, so if someone could help me if would be wonderful.
Thanks.

Here's one option:
SQL> with test (col) as
2 (select '4,500,400,40' from dual union all
3 select '5400200,00' from dual union all
4 select '1200,988,00' from dual union all
5 select '28 200 600,5' from dual union all
6 select '40 500 600' from dual
7 )
8 select col,
9 to_char(
10 to_number( replace( replace(case when instr(col, ',') = 0 then col ||',00'
11 else col
12 end,
13 ' ', ','
14 ),
15 ',', ''
16 )
17 ) / 100,
18 'fm999G999G999G990D00',
19 'nls_numeric_characters = '', '''
20 ) result
21 from test;
COL RESULT
------------ -------------------
4,500,400,40 4 500 400,40
5400200,00 5 400 200,00
1200,988,00 1 200 988,00
28 200 600,5 2 820 060,05
40 500 600 40 500 600,00
SQL>
What does it do (now that it got more complicated)?
nested REPLACE:
inner checks whether source string contains comma , character; if not, appends ,00 suffix (that's for values like '40 500 600')
outer is as before - substitutes commas with an empty string
to_number converts such a string into a number
it is divided by 100
to_char formats it

Related

Count occurences of values in table, when I treat one value as occurence of all other values

I have a table with one column (just to simplify the problem) with values 0-23 or *.
I want to count occurrences of each value 0-23, but treat * as occurrence of all other values
for example:
column_name
-------------
3
4
5
6
7
*
4
4
3
*
I want to get something like that:
column_name | count
--------------------
1 | 2
2 | 2
3 | 4
4 | 5
5 | 3
6 | 3
7 | 3
.....
I tried experimenting with different count and "group by" methods, but always getting very strange results. Basically the main problem here is to how count rows when I need to have one value in all other groups.
You could use analytic function that counts values where * is replaced by actual value between 0 and 23:
SELECT DISTINCT n.RN "COL_1", Count(REPLACE(t.COL_1, '*', n.RN)) OVER(Partition By n.RN) "CNT"
FROM tbl t
INNER JOIN ( Select To_Char(LEVEL - 1) "RN" From Dual Connect By LEVEL <=24 ) n ON(n.RN = REPLACE(t.COL_1, '*', n.RN))
WHERE n.RN IN(SELECT COL_1 FROM tbl)
ORDER BY To_Number(n.RN)
which with your sample data:
WITH
tbl (COL_1) AS
(
Select '3' From Dual Union All
Select '4' From Dual Union All
Select '5' From Dual Union All
Select '6' From Dual Union All
Select '7' From Dual Union All
Select '*' From Dual Union All
Select '4' From Dual Union All
Select '4' From Dual Union All
Select '3' From Dual Union All
Select '*' From Dual Union All
Select '3' From Dual
)
... results as:
COL_1 CNT
---------------------------------------- ----------
3 5
4 5
5 3
6 3
7 3
... and if you exclude the Where clause you will get all the rows (0 - 23) with number of occurances counted by REPLACE of * with any of the numbers
COL_1 CNT
---------------------------------------- ----------
0 2
1 2
2 2
3 5
4 5
5 3
6 3
7 3
8 2
9 2
10 2
11 2
12 2
13 2
14 2
15 2
16 2
17 2
18 2
19 2
20 2
21 2
22 2
23 2
You can do it using successive WITH's :
First one to calculate number of occurrence of *.
And the second is to calculate number of occurrence of each number.
with cte as (
select count(1) as c
from mytable
where column_name = '*'
),
cte2 as (
select column_name, count(1) as c
from mytable, cte
group by column_name
)
select column_name, cte.c + cte2.c
from cte2, cte;
You can with nested statements too,
SELECT ID,(count_ + (
SELECT COUNT(ID) FROM sql_test_a
WHERE ID = '*')) as count_
FROM (
SELECT ID,COUNT(ID) as count_
FROM sql_test_a WHERE ID != '*' GROUP BY ID);

Comparing a column value with another column value + a Value not working (Better description in the body)

My apologies for the title, don't know a better one.
I have the following issue, i got a complex Oracle database which requires the query to go through some hoops. For this specific query i want to see if there are any records in which Column A is Smaller then the Column B plus 0.1.
The WHERE is as following:
(
COALESCE(TO_NUMBER(REGEXP_SUBSTR("Column1", '^\d+')), 0)
<
(COALESCE(TO_NUMBER(REGEXP_SUBSTR("Column2", '^\d+')), 0) + 0.1)
)
The problem being is that it ignores the values behind the dot. For example :
Column1 = 25.4
Column2 = 25.2
This one should not be shown seeing 25.4 is larger then 25.3
If i say + 0.0 it does work correctly only showing the values where Column1 is small then column2 by nature.
If i use + 1.0 it also does it correctly, if i do +1.2 it only shows those that it jsut reats it as if it were + 1.0
Any suggestions? i'm not that experienced with Oracle, this is the only database (i sadly) have to work with using it.
Sample:
ID Column1 column2
1 25.4 25.2
2 RA 16
3 22 23.1
4 21 22
5 -12.2 -12.15
6 RA 0
7 11.3792 11.3538
8 0 -.023
9 1.05 .95
Based on your sample data, see if something like this helps:
SQL> with test (id, col1, col2) as
2 (select 1, '25.4', '25.2' from dual union all
3 select 2, 'RA' , '16' from dual union all
4 select 3, '22' , '23.1' from dual union all
5 select 4, '21' , '22' from dual union all
6 select 5, '-12.2', '-12.15' from dual union all
7 select 6, 'RA' , '0' from dual union all
8 select 7, '11.3972', '11.3538' from dual union all
9 select 8, '0', '-0.023' from dual union all
10 select 9, '1.05', '.95' from dual
11 ),
12 subset as
13 (select *
14 from test
15 where regexp_like(col1, '^(-)?(\d+)?(\.\d+)?$')
16 and regexp_like(col2, '^(-)?(\d+)?(\.\d+)?$')
17 )
18 select *
19 from subset
20 where to_number(col1, '999999999.99999999', 'nls_numeric_characters = .,') <
21 to_number(col2, '999999999.99999999', 'nls_numeric_characters = .,') + 0.1;
ID COL1 COL2
---------- ------- -------
3 22 23.1
4 21 22
5 -12.2 -12.15
7 11.3972 11.3538
8 0 -0.023
SQL>
subset CTE checks whether columns contain numbers
if so, the final select converts strings to numbers using format mask along with NLS_NUMERIC_CHARACTERS (to distinguish thousands separators from decimal points)
If data in those columns differ from the example and cause problems, post such cases and we'll see what next.
This is a situation where you can use a case expression:
(case when not regexp_like(column1, '^[0-9]+[.]?[0-9]*$')
then null
when not regexp_like(column2, '^[0-9]+[.]?[0-9]*$')
then null
where to_number(column1) < to_number(column1)
then 1
end) = 1
I'm not usually a fan of case expressions in conditional expressions. However, type conversion issues are a situation where they are useful.

Oracle query to convert a value with brackets to negitive number

I have a string column that has the values like below
Column A
(9167)
1042
21463
9979
(26351)
I need the output like below -
Output
-9167
1042
21463
9979
-26351
I need to trim the brackets and replace that with '-' Symbol by using update statement. Is there a way we can do it with a query?
How about this:
with demo (column_a) as
( select '(9167)' from dual union all
select '1042' from dual union all
select '21463' from dual union all
select '9979' from dual union all
select '(26351)' from dual )
select column_a
, case
when trim(column_a) like '(%)' then
to_number(regexp_replace(column_a,'[()]')) * -1
else to_number(column_a)
end as result
from demo;
COLUMN_A RESULT
-------- ----------
(9167) -9167
1042 1042
21463 21463
9979 9979
(26351) -26351
Use Translate 'from characters', 'to characters'
As the user guide states, "TRANSLATE lets you make several single-character, one-to-one substitutions in one operation."
One just maps the replacement characters as follows:
from: ()
to: -
where the 1st from character ( is mapped to the 1st to character -
where the 2nd from character ) is mapped to the 2nd to character, which doesn't exist so is null
SCOTT#db>WITH tst AS (
2 SELECT
3 '(9167)' val FROM
4 dual
5 union all
6 select
7 '1042' val FROM
8 dual
9 union all
10 select
11 '21463' val FROM
12 dual
13 union all
14 select
15 '9979' val FROM
16 dual
17 union all
18 select
19 '(26351)' val
20 FROM
21 dual
22 ) SELECT
23 translate(t.val,'()','-')
24 FROM
25 tst t;
TRANSLA
-------
-9167
1042
21463
9979
-26351

SQL : Find the position of a character in a string value

I have a column with below sample values
MyColumn
----------
NNNNNYYNNNNNYYNNNNNYYNNNNNYYNNN
NNYYNNNNNYYNNNNNYYNNNNNYYNNN
YYNNNNNYYNNNNNYYNNNNNYYNNNNNYY
YYNNNNNYYNNNNNYYNNNNNYYNNNNNYYN
I want to display the position of 'Y' through SQL select statement.
Below is my SQL query.
SELECT LISTAGG(instr(MyColumn, 'Y', 1, level), ' ') WITHIN
GROUP(
ORDER BY level)
FROM dual
CONNECT BY level < instr(MyColumn, 'Y', 1, level) Y_Position from MyTable;
Output of the query is,
Y_Position
------------
6 7 13 14 20 21 27 28
3 4 10 11 17 18 24 25
1
1
The query is not working for 3rd and 4th rows. How to fix this? Why is it not working?
Your query has invalid syntax as it has two FROM clauses one of which does not have a matching SELECT clause.
It also has:
CONNECT BY level < instr(MyColumn, 'Y', 1, level)
Which will not work when the string starts with a Y as LEVEL is 1 and INSTR( 'YYYY', 'Y', 1, 1 ) is 1 and then the filter is CONNECT BY 1 < 1 which is not true. You want to check that CONNECT BY INSTR( MyColumn, 'Y', 1, LEVEL ) > 0.
You also need another filter to check for the case when there are no Y characters as a hierarchical query will always return at least one row.
You can adapt your query to use a correlated sub-query:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE MyTable( MyColumn ) AS
SELECT 'NNNNNYYNNNNNYYNNNNNYYNNNNNYYNNN' FROM DUAL UNION ALL
SELECT 'NNYYNNNNNYYNNNNNYYNNNNNYYNNN' FROM DUAL UNION ALL
SELECT 'YYNNNNNYYNNNNNYYNNNNNYYNNNNNYY' FROM DUAL UNION ALL
SELECT 'YYNNNNNYYNNNNNYYNNNNNYYNNNNNYYN' FROM DUAL UNION ALL
SELECT 'NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN' FROM DUAL
Query 1:
SELECT (
SELECT LISTAGG( INSTR( t.MyColumn, 'Y', 1, LEVEL ), ' ' )
WITHIN GROUP ( ORDER BY LEVEL )
FROM DUAL
WHERE INSTR( t.MyColumn, 'Y' ) > 0
CONNECT BY INSTR( t.MyColumn, 'Y', 1, LEVEL ) > 0
) AS Y_position
FROM Mytable t
Results:
| Y_POSITION |
|---------------------------|
| 6 7 13 14 20 21 27 28 |
| 3 4 10 11 17 18 24 25 |
| 1 2 8 9 15 16 22 23 29 30 |
| 1 2 8 9 15 16 22 23 29 30 |
| (null) |

Get list of what special characters and how many times in oracle column

I'm searching for a way to get a list of special characters and how many times they appear in my column. I've tried using using regexp_count which works, but I'm not sure how to extend it to make it work for all special characters in one query.
For example for syntax = 'x=y*100' with the following query I get
select *
from (
select regexp_count(syntax, '\*') as charCnt, syntax
from tblTemp
)
where charCnt > 0
charCnt=1 and syntax='x=y*100'.
Which is correct but I want to be able to get back
specChar Cnt
\* 1
= 1
etc..
Oracle Setup:
CREATE TABLE table_name(
id INT,
value NVARCHAR2(200)
);
INSERT INTO table_name
SELECT 1, N'y=20x+3' FROM DUAL UNION ALL
SELECT 2, N'***^%$%$%*&*.&\?' FROM DUAL UNION ALL
SELECT 3, UNISTR('\00B5\00B6\00B5') FROM DUAL UNION ALL
SELECT 4, N'!"£$%^&*()!"£$%^&*()!"£$%^&*()!"£$%^&*()!"£$%^&*()'
|| N'!"£$%^&*()!"£$%^&*()!"£$%^&*()!"£$%^&*()!"£$%^&*()'
|| N'!"£$%^&*()!"£$%^&*()!"£$%^&*()!"£$%^&*()!"£$%^&*()'
|| N'!"£$%^&*()!"£$%^&*()!"£$%^&*()!"£$%^&*()!"£$%^&*()' FROM DUAL;
CREATE OR REPLACE TYPE CHAR_LIST IS TABLE OF CHAR(1 CHAR);
/
Query:
SELECT t.id,
--MAX( t.value ) AS value,
CAST( c.COLUMN_VALUE AS CHAR(1 CHAR) ) AS character,
COUNT(1) AS frequency
FROM table_name t,
TABLE(
CAST(
MULTISET(
SELECT SUBSTR( t.value, LEVEL, 1 )
FROM DUAL
WHERE REGEXP_LIKE( SUBSTR( t.value, LEVEL, 1 ), '[^a-zA-Z0-9]' )
CONNECT BY LEVEL <= LENGTH( t.value )
) AS CHAR_LIST
)
) c
GROUP BY t.id, c.COLUMN_VALUE
ORDER BY id, character;
Output:
ID CHARACTER FREQUENCY
---------- --------- ----------
1 + 1
1 = 1
2 $ 2
2 % 3
2 & 2
2 * 5
2 . 1
2 ? 1
2 \ 1
2 ^ 1
3 µ 2
3 ¶ 1
4 ! 20
4 " 20
4 $ 20
4 % 20
4 & 20
4 ( 20
4 ) 20
4 * 20
4 ^ 20
4 £ 20