Oracle - how to replace and sum from the output? - sql

I have output of group by as below.
SELECT param2,count(*) FROM table WHERE CALLED='1234' GROUP BY PARAM2
Param2 count(*)
135; 616
135;135; 18
135;135;135; 4
135;135;135;135; 2
135;135;135;135;135;135;27; 15
135;135;135;27; 5
136; 43
136;136; 383
136;136;136; 47
136;136;136;27; 32
Expected: For all the output param2 that includes 135 or 136 should be group by once again such that result should be as below:
Param2 count(*)
135 660
136 505

For the given example, a simple straight-forward solution would do the job. Not sure if this is as general as necessary:
select '135' as "Param2", count(*) from table
where called = '1234' and ';'||param2 like '%;135;%'
union all
select '136' as "Param2", count(*) from table
where called = '1234' and ';'||param2 like '%;136;%'

Try below query,
Keeping my base source as the output you have mentioned ,we can use traditional regular expression to split the row and get distinct values present for param2 and then a sum over it would give the result as expected. I am little skeptical about how the performance will be but you can give it a try.
with sample data:
with table1
as
(
select '135;' param2, 616 cnt from dual union all
select '135;135;', 18 from dual union all
select '135;135;135;', 4 from dual union all
select '135;135;135;135;', 2 from dual union all
select '135;135;135;135;135;135;27;', 15 from dual union all
select '135;135;135;27;', 5 from dual union all
select '136;', 43 from dual union all
select '136;136;', 383 from dual union all
select '136;136;136;', 47 from dual union all
select '136;136;136;27;', 32 from dual
)
select split,sum(cnt) cnt
from
(select distinct t1.*, regexp_substr(param2,'[^;]+', 1, level) split
from table1 t1;
connect by regexp_substr(param2, '[^;]+', 1, level) is not null)
group by split;
So the final query should be ,
with table1
as
(
select param2, count(*) cnt
from table
where called = '1234'
group by param2
)
select split as param2,sum(cnt) cnt
from
(select distinct t1.*, regexp_substr(param2,'[^;]+', 1, level) split
from table1 t1;
connect by regexp_substr(param2, '[^;]+', 1, level) is not null)
group by split;

Related

Is there any alternative to use MYSQL's ADDDATE() in ORACLE?

I have this query that needs to be executed for oracle sql instead of mysql which is where it originally came from, but I have the ADDDATE() function which I don't see any other alternative than DateAdd since it needs more parameters than I really need..
Apart from that, if I try to execute it, it also indicates an error in the
SELECT 0 i UNION.................
part, saying the following ORA-00923: FROM keyword not found where expected
Maybe in oracle it is not allowed to do a select 0 union select 1 union...
Any suggestions or help I appreciate it, thanks
SELECT
ADDDATE('1970-01-01', t4.i * 10000 + t3.i * 1000 + t2.i * 100 + t1.i * 10 + t0.i) selected_date
FROM
(
SELECT 0 i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
) t0,
(
SELECT 0 i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
) t1,
(
SELECT 0 i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
) t2,
(
SELECT 0 i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
) t3,
(
SELECT 0 i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
) t4
In Oracle you must select from the one-row table dual in order to select one row. You cannot select without a from clause.
If you want to generate dates, you'll write a standard SQL recursive CTE. (And this is the typical approach now in MySQL, too, since version 8.0.)
Here is an example selecting all days for 1970:
with dates (dt) as
(
select date '1970-01-01' from dual
union all
select dt + interval '1' day from dates where dt < date '1970-12-31'
)
select dt from dates;
Here is another way to SELECT a list of dates for the year 1970. Adjust the starting and ending dates if you want different years or the INTERVAL if you want different periods like seconds, minutes, hours…
ALTER SESSION SET NLS_DATE_FORMAT = 'DD-MON-YYYY HH24:MI:SS';
with dt (dt, interv) as (
select date '1970-01-01', numtodsinterval(1,'DAY') from dual
union all
select dt.dt + interv, interv from dt
where dt.dt + interv <= date '1970-12-31')
select dt from dt;
/

sql query for like to Order exact match at top in list

sql query for like to show exact match first in list:
dataset: "abcd", "a","b","bc","bcd"
select * from table where data like "%bc%";
it should show in order bc, abcd, bcd.
As you said - sort by match.
SQL> with test (col) as
2 (select 'abcd' from dual union all
3 select 'a' from dual union all
4 select 'b' from dual union all
5 select 'bc' from dual union all
6 select 'bcd' from dual
7 )
8 select col
9 from test
10 where col like '%' || '&&par_search_string' ||'%'
11 order by utl_match.jaro_winkler_similarity(col, '&&par_search_string') desc;
Enter value for par_search_string: bc
COL
----
bc
bcd
abcd
SQL> undefine par_search_string
SQL> /
Enter value for par_search_string: cd
COL
----
bcd
abcd
SQL>
One of many methods:
with
t as (
select 'abcd' c from dual union all
select 'a' from dual union all
select 'b' from dual union all
select 'bc' from dual union all
select 'bcd' from dual
)
select *
from t
where c like '%bc%'
order by length(c)
Demo.
I think you can use a query like this one that can return your expected result.
select * from table where data='bc'
union all
select * from table where data like '%bc%' and data<>'bc'
One method is:
select *
from t
where data like '%bc%'
order by (case when data = 'bc' then 1 else 2 end);
Or if you don't want to type so much:
order by nullif(data, 'bc') desc
The descending sort puts NULL values first.

how can to_char with zero before number in oracle

I have database like below:
WITH TB AS(
SELECT 1 NONB FROM DUAL UNION ALL
SELECT 89 NONB FROM DUAL UNION ALL
SELECT 193 NONB FROM DUAL
)
SELECT * FROM TB
I want change column NONB to_char(NONB) and display zero before the number like below.
001
089
189
How can I do this? Thanks.
Use lpad():
select lpad(nonb, 3, '0')
from tb;
Here is a rextester.
Use this:
WITH TB AS(
SELECT 1 NONB FROM DUAL UNION ALL
SELECT 89 NONB FROM DUAL UNION ALL
SELECT 193 NONB FROM DUAL
)
SELECT to_char(nonb, 'FM000') FROM TB

Splitting a comma separated string in Oracle not working

I would like to retrieve a set of values from a comma separated string into an IN clause in a query. Does some one knows why the following code is not working properly;
WITH xtable AS (
SELECT 1 ID, '116,117,169,170,173,175,9015,44008,44367,44446,45081,45083,46779,47161,47222' AGT FROM DUAL
UNION ALL
SELECT 2 ID, '456,789' AGT FROM DUAL
UNION ALL
SELECT 3 ID, '116,117,169,170,173,175,9015,44008,44367,44446,45081,45083,46779,47161' AGT FROM DUAL
)
select regexp_substr(x.AGT,'[^,]+', 1, level)
from xtable x
where x.ID = 3
connect by regexp_substr(x.AGT, '[^,]+', 1, level) is not null;
In this scenario the result should be
AGT
1 116
2 117
3 169
4 170
5 173
6 175
7 9015
8 44008
9 44367
10 44446
11 45081
12 45083
13 46779
14 47161
Instead I get an almost infinite loop of the same values
Issue in your query is that the where clause will be applied to only level 1 not any further.
Try this using nested table:
WITH xtable AS (
SELECT 1 ID, '116,117,169,170,173,175,9015,44008,44367,44446,45081,45083,46779,47161,47222' AGT FROM DUAL
UNION ALL
SELECT 2 ID, '456,789' AGT FROM DUAL
UNION ALL
SELECT 3 ID, '116,117,169,170,173,175,9015,44008,44367,44446,45081,45083,46779,47161' AGT FROM DUAL
)
select regexp_substr(x.AGT,'[^,]+', 1, t.column_value) agt
from xtable x cross join table(
cast(
multiset(
select level
from dual
connect by level <= regexp_count(x.AGT,',') + 1
)as sys.odcinumberlist
)
) t
where x.id = 3;
It's a general purpose query which you can use even without where clause if you wanted to convert all of the them at once.
In Oracle 12c+, you can use OUTER APPLY to achieve the same effect and simpler syntax:
WITH xtable AS (
SELECT 1 ID, '116,117,169,170,173,175,9015,44008,44367,44446,45081,45083,46779,47161,47222' AGT FROM DUAL
UNION ALL
SELECT 2 ID, '456,789' AGT FROM DUAL
UNION ALL
SELECT 3 ID, '116,117,169,170,173,175,9015,44008,44367,44446,45081,45083,46779,47161' AGT FROM DUAL
)
select regexp_substr(x.AGT,'[^,]+', 1, t.n) agt
from xtable x
outer apply (
select level n
from dual
connect by level <= regexp_count(x.AGT,',') + 1
) t
where x.id = 3;
You have the same values in multiple rows, so your connect-by is bouncing between them. Hierarchical queries are a little tricky with multiple rows involved. If you really only want the values for a single ID you can filter in a subquery:
WITH xtable AS (
SELECT 1 ID, '116,117,169,170,173,175,9015,44008,44367,44446,45081,45083,46779,47161,47222' AGT FROM DUAL
UNION ALL
SELECT 2 ID, '456,789' AGT FROM DUAL
UNION ALL
SELECT 3 ID, '116,117,169,170,173,175,9015,44008,44367,44446,45081,45083,46779,47161' AGT FROM DUAL
)
select regexp_substr(x.AGT,'[^,]+', 1, level)
from (select AGT from xtable where ID = 3) x
connect by regexp_substr(x.AGT, '[^,]+', 1, level) is not null;
REGEXP_SUBSTR(X.AGT,'[^,]+',1,LEVEL)
----------------------------------------------------------------------------
116
117
169
170
173
175
9015
44008
44367
44446
45081
45083
46779
47161
14 rows selected.
Or you can include id = prior id, but then also need to use a non-deterministic function call to prevent cycling:
WITH ... (...)
select regexp_substr(x.AGT,'[^,]+', 1, level)
from xtable x
where x.ID = 3
connect by id = prior id and prior dbms_random.value is not null
and regexp_substr(x.AGT, '[^,]+', 1, level) is not null;
which gets the same 14 rows back, and would work if you included multiple IDs.
There are multiple solutions for this in the Splitting Delimited Strings - SO Oracle Documentation page.
A solution using a correlated hierarchical query is:
WITH xtable (id, agt ) AS (
SELECT 1, '116,117,169,170,173,175,9015,44008,44367,44446,45081,45083,46779,47161,47222' FROM DUAL
UNION ALL
SELECT 2, '456,789' FROM DUAL
UNION ALL
SELECT 3, '116,117,169,170,173,175,9015,44008,44367,44446,45081,45083,46779,47161' FROM DUAL
)
SELECT id,
REGEXP_SUBSTR( x.agt, '[^,]+', 1, t.COLUMN_VALUE ) AS item
FROM xtable x
CROSS JOIN
TABLE(
CAST(
MULTISET(
SELECT LEVEL
FROM DUAL
CONNECT BY LEVEL <= REGEXP_COUNT( x.agt, '[^,]+' )
)
) AS SYS.ODCINUMBERLIST
) t
WHERE x.id = 3;
Output:
ID ITEM
-- -----
3 116
3 117
3 169
3 170
3 173
3 175
3 9015
3 44008
3 44367
3 44446
3 45081
3 45083
3 46779
3 47161

get the nearest highest value from a list oracle sql

I have a column in the database in the following format: yymmddhh24miss
Sample Data:
140203101241
140202101141
140102101240
143001101244
142801101245
142701131347
142601121542
142101131744
...
I need to get the nearest high value from the list. Ex: If I pass 142701131333, then it should return 142701131347 from the above list.
Any help appreciated!
SELECT data
FROM
(
SELECT data
FROM tbl
WHERE data > '142701131333'
ORDER BY data
) a
WHERE rownum = 1
SQL> with t (x) as (
2 select 140203101241 from dual union all
3 select 140202101141 from dual union all
4 select 140102101240 from dual union all
5 select 143001101244 from dual union all
6 select 142801101245 from dual union all
7 select 142701131347 from dual union all
8 select 142601121542 from dual union all
9 select 142101131744 from dual
10 )
11 select min(x) minx from t where x > 142701131333
12 /
MINX
-----------------
142701131347
SELECT MIN(sample_data)
FROM tableName
WHERE sample_data > 142701131333