Get Prime Number records in Oracle using ROWNUM - sql

I am trying to get all the prime(row) numbered records from my table.Can someone please shed some light on how to solve this problem?
Here is my sample data as below.
EMPID EMPNAME
1 A
2 B
3 C
4 D
5 E
6 F
7 G
8 H
9 I
10 J
Required output:
EMPID EMPNAME
2 B
3 C
5 E
7 G
If I have huge data how do I get the output like this instead of using IN operator?

Sieve of Eratosthenes
CREATE TABLE primes (
num number PRIMARY KEY
);
INSERT INTO primes (num)
SELECT LEVEL + 1
FROM dual
CONNECT BY LEVEL < 1000;
DELETE FROM primes p1
WHERE EXISTS (
SELECT NULL
FROM primes p2
WHERE p2.num < p1.num
AND MOD(p1.num, p2.num) = 0
);
And then
SELECT emps.*
FROM emps
INNER JOIN primes ON primes.num = emps.EMPID;
Or
SELECT EMPID, EMPNAME
FROM (
SELECT ROWNUM AS rn, emps.EMPID, emps.EMPNAME
FROM emps
)
INNER JOIN primes ON primes.num = rn;
If you don't want to calculate the primes, you could add them from existing data: List of small primes

select l prime_number
from (select level l from dual connect by level <= 100)
, (select level m from dual connect by level <= 100)
-- where m<=l --this doesnt matter but including it will perform better
group by l
having count(case l/m when trunc(l/m) then 1 end) in (1,2)
order by l;
PRIME_NUMBER
1
2
3
5
7
11
13
17
19
23
29
31
37
41
43
47
53
59
61
67
71
73
79
83
89
97

You can use the following query to Select all prime numbers
SQL> select l prime_number
2 from (select level l from dual connect by level <= 100)
3 , (select level m from dual connect by level <= 100)
4 where m<=l
5 group by l
6 having count(case l/m when trunc(l/m) then 'Y' end) = 2
7 order by l
8 /
PRIME_NUMBER
2
3
5
7
11
13
17
19
23
29
31
37
41
43
47
53
59
61
67
71
73
79
83
89
97
or this one:
SQL> with t as (select level l from dual connect by level <= 100)
2 --
3 SELECT l prim_num FROM
4 (select * from t
5 model
6 dimension by (l dim)
7 measures (l,2 temp)
8 rules iterate (1000000) until (power(temp[1],2)>100)
9 (l[DIM>TEMP[1]]=decode(mod(l[CV()],temp[1]),0,null,l[CV()]),
10 temp[1]=min(l)[dim>temp[1]])
11 )
12 WHERE l IS NOT NULL
13 /
and then just do this:
Select *
from myTable
where ROWNUM in (query)

The following can be used:
with lvl as
(
select level l from dual connect by level<=100
), unprime as(select l2.* from lvl l1,lvl l2 where l1.l<l2.l and mod(l2.l,l1.l)=0 and l1.l>1)
select lvl.l from lvl where l>1
minus select l from unprime;

Prime numbers up to 10 can be listed belows. Then join with your data on EMPID.
(SELECT LEVEL P FROM DUAL CONNECT BY ROWNUM<=10)
MINUS
(SELECT T1.P FROM
(SELECT LEVEL P FROM DUAL CONNECT BY ROWNUM<=10) T1 JOIN
(SELECT LEVEL D FROM DUAL CONNECT BY ROWNUM<=10) T2 ON mod(T1.P,T2.D)=0 AND T1.P>T2.D
AND T2.D>1
)
MINUS (SELECT 1 FROM DUAL);
Assuming your table name is EMP:
WITH EMP AS
(
SELECT 1 EMPID, 'A' EMPNAME FROM DUAL
UNION ALL
SELECT 2 EMPID, 'B' EMPNAME FROM DUAL
--etc.
)
SELECT EMP.* FROM
(
(SELECT LEVEL P FROM DUAL CONNECT BY ROWNUM<=10)
MINUS
(SELECT T1.P FROM
(SELECT LEVEL P FROM DUAL CONNECT BY ROWNUM<=10) T1 JOIN
(SELECT LEVEL D FROM DUAL CONNECT BY ROWNUM<=10) T2 ON mod(T1.P,T2.D)=0 AND T1.P>T2.D
AND T2.D>1
)
MINUS (SELECT 1 FROM DUAL)
) T1,EMP WHERE T1.P=EMP.EMPID;

Related

Difference of SUM while doing multiple group by

I have table like below
Pid
qid
count
sum
11
13
5
800
12
13
7
300
11
14
3
100
12
14
5
200
I need to get the difference of sum while doing group by using pid and qid
Like
SELECT
/* ??? */
FROM
tbl
GROUP BY
pid,
qid
Id1
id2
qid
difference
11
12
13
500
Use conditional aggregation:
SELECT 11 AS id1,
12 AS id2,
qid,
ABS(SUM(CASE pid WHEN 11 THEN value WHEN 12 THEN -value END)) AS diff
FROM tbl
GROUP BY qid;
Which, for the sample data:
CREATE TABLE tbl (Pid, qid, value) AS
SELECT 11, 13, 160 FROM DUAL CONNECT BY LEVEL <= 5 UNION ALL
SELECT 12, 13, 43 FROM DUAL CONNECT BY LEVEL <= 6 UNION ALL
SELECT 12, 13, 42 FROM DUAL CONNECT BY LEVEL <= 1 UNION ALL
SELECT 11, 14, 30 FROM DUAL CONNECT BY LEVEL <= 2 UNION ALL
SELECT 11, 14, 40 FROM DUAL CONNECT BY LEVEL <= 1 UNION ALL
SELECT 12, 14, 40 FROM DUAL CONNECT BY LEVEL <= 5;
Outputs:
ID1
ID2
QID
DIFF
11
12
13
500
11
12
14
100
fiddle
Looks like you want a difference of sum between all pairs of different pid per each qid. The easiest and most reliable solution in this case would be to use a simple self join, like this:
with agg as (
select pid, qid
, count(*) as cnt
, sum(?) as sum1
from your_table
group by pid, qid
)
select
t1.pid as id1
,t2.pid as id2
,abs(t2.sum1-t1.sum1) as diff
from agg t1
join agg t2
on t1.qid=t2.qid
and t1.pid<t2.pid
Obviously, we could optimize it if we were sure that you have just 2 different PIDs per each qid, but you haven't mention it

I'm trying to join 2 tables that don't have same columns and using case select. Is this possible?

select
case
when code = number_1
then number_1=code
when count(code)>=count(number_1)
then number_1 = sum(code)
else 'Null'
end
from table_1, table_2
ORDER BY code, number_1 ;
table 1
code
value
0
None
1
R
2
W
4
C
8
D
16
U
32
Uown
Table 2
number
0
1
2
3
4
5
8
12
13
16
20
25
26
27
32
43
44
45
60
61
62
63
64
68
70
expected output is
number
output
0
0
1
1
2
2
3
2,1
4
4
7
4,2,1
8
8
16
16
32
32
43
32,8,2,1
63
32,16,8,4,2,1
64
null
70
null
You can use the BITAND function in the JOIN condition:
SELECT t2."NUMBER",
CASE SUM(t1.code)
WHEN t2."NUMBER"
THEN LISTAGG(t1.code, ',') WITHIN GROUP (ORDER BY t1.code DESC)
END AS output,
CASE SUM(t1.code)
WHEN t2."NUMBER"
THEN LISTAGG(t1.value, ',') WITHIN GROUP (ORDER BY t1.code DESC)
END AS value_output
FROM table_2 t2
INNER JOIN table_1 t1
ON ( t2."NUMBER" = t1.code
OR (t1.code > 0 AND BITAND(t2."NUMBER", t1.code) = t1.code))
GROUP BY t2."NUMBER"
Which, for the sample data:
CREATE TABLE table_1 (code, value) AS
SELECT 0, 'None' FROM DUAL UNION ALL
SELECT 1, 'R' FROM DUAL UNION ALL
SELECT 2, 'W' FROM DUAL UNION ALL
SELECT 4, 'C' FROM DUAL UNION ALL
SELECT 8, 'D' FROM DUAL UNION ALL
SELECT 16, 'U' FROM DUAL UNION ALL
SELECT 32, 'Uown' FROM DUAL;
CREATE TABLE Table_2 ("NUMBER") AS
SELECT COLUMN_VALUE
FROM SYS.ODCINUMBERLIST(
0,1,2,3,4,5,8,12,13,16,20,25,26,27,32,43,44,45,60,61,62,63,64,68,70
);
Outputs:
NUMBER
OUTPUT
VALUE_OUTPUT
0
0
None
1
1
R
2
2
W
3
2,1
W,R
4
4
C
5
4,1
C,R
8
8
D
12
8,4
D,C
13
8,4,1
D,C,R
16
16
U
20
16,4
U,C
25
16,8,1
U,D,R
26
16,8,2
U,D,W
27
16,8,2,1
U,D,W,R
32
32
Uown
43
32,8,2,1
Uown,D,W,R
44
32,8,4
Uown,D,C
45
32,8,4,1
Uown,D,C,R
60
32,16,8,4
Uown,U,D,C
61
32,16,8,4,1
Uown,U,D,C,R
62
32,16,8,4,2
Uown,U,D,C,W
63
32,16,8,4,2,1
Uown,U,D,C,W,R
68
null
null
70
null
null
db<>fiddle here

SQL logic to fail a check if any of the related customers has failed

I have the requirement to flag the customers Y only when all the related customers have also passed the check.
below are the two tables:
relationship table :
customer_id related_customer
1 1
1 2
1 3
2 1
2 2
2 3
3 1
3 2
3 3
11 11
11 22
22 11
22 22
Check table
customer_id check_flag
1 y
2 y
3 n
11 y
22 y
I want output like below:
customer_id paas_fail_flag
1 n
2 n
3 n
11 y
22 y
output justification: since 1,2,3 are related customers and since one of them (3) has n in table 2 , so all the related customers should also have n.
11,22 are related customers and both have y in table 2.so in output both should have y.
You need to join relationship to check and use conditional aggregation:
SELECT r.customer_id,
COALESCE(MAX(CASE WHEN c.check_flag = 'n' THEN c.check_flag END), 'y') paas_fail_flag
FROM relationship r INNER JOIN "check" c
ON c.customer_id = r.related_customer
GROUP BY r.customer_id
ORDER BY r.customer_id
See the demo.
Something like this? Sample data in lines #1 - 40; query begins at line #41:
SQL> WITH
2 -- sample data
3 rel (customer_id, related_customer)
4 AS
5 (SELECT 1, 1 FROM DUAL
6 UNION ALL
7 SELECT 1, 2 FROM DUAL
8 UNION ALL
9 SELECT 1, 3 FROM DUAL
10 UNION ALL
11 SELECT 2, 1 FROM DUAL
12 UNION ALL
13 SELECT 2, 2 FROM DUAL
14 UNION ALL
15 SELECT 2, 3 FROM DUAL
16 UNION ALL
17 SELECT 3, 1 FROM DUAL
18 UNION ALL
19 SELECT 3, 2 FROM DUAL
20 UNION ALL
21 SELECT 3, 3 FROM DUAL
22 UNION ALL
23 SELECT 11, 11 FROM DUAL
24 UNION ALL
25 SELECT 11, 22 FROM DUAL
26 UNION ALL
27 SELECT 22, 11 FROM DUAL
28 UNION ALL
29 SELECT 22, 22 FROM DUAL),
30 chk (customer_id, check_flag)
31 AS
32 (SELECT 1, 'y' FROM DUAL
33 UNION ALL
34 SELECT 2, 'y' FROM DUAL
35 UNION ALL
36 SELECT 3, 'n' FROM DUAL
37 UNION ALL
38 SELECT 11, 'y' FROM DUAL
39 UNION ALL
40 SELECT 22, 'y' FROM DUAL),
41 temp
42 AS
43 -- minimum CHECK_FLAG per customer and related customer
44 ( SELECT r.customer_id, r.related_customer, MIN (c.check_flag) mcf
45 FROM rel r JOIN chk c ON c.customer_id = r.related_customer
46 GROUP BY r.customer_id, r.related_customer)
47 SELECT customer_id, MIN (mcf) flag
48 FROM temp
49 GROUP BY customer_id
50 ORDER BY customer_id;
CUSTOMER_ID FLAG
----------- ----
1 n
2 n
3 n
11 y
22 y
SQL>
Assuming that your relationship data could be sparse, for example:
CREATE TABLE relationship ( customer_id, related_customer ) AS
SELECT 2, 3 FROM DUAL UNION ALL
SELECT 3, 1 FROM DUAL UNION ALL
SELECT 3, 2 FROM DUAL UNION ALL
SELECT 11, 22 FROM DUAL;
CREATE TABLE "CHECK" ( customer_id, check_flag ) AS
SELECT 1, 'y' FROM DUAL UNION ALL
SELECT 2, 'y' FROM DUAL UNION ALL
SELECT 3, 'n' FROM DUAL UNION ALL
SELECT 11, 'y' FROM DUAL UNION ALL
SELECT 22, 'y' FROM DUAL;
(Note: The below query will also work on your dense data, where every relationship combination is enumerated.)
Then you can use a hierarchical query:
SELECT customer_id,
MIN(check_flag) AS check_flag
FROM (
SELECT CONNECT_BY_ROOT(c.customer_id) AS customer_id,
c.check_flag AS check_flag
FROM "CHECK" c
LEFT OUTER JOIN relationship r
ON (r.customer_id = c.customer_id)
WHERE CONNECT_BY_ISLEAF = 1
CONNECT BY NOCYCLE
( PRIOR r.related_customer = c.customer_id
OR PRIOR c.customer_id = r.related_customer )
AND PRIOR c.check_flag = 'y'
)
GROUP BY
customer_id
ORDER BY
customer_id
Which outputs:
CUSTOMER_ID
CHECK_FLAG
1
n
2
n
3
n
11
y
22
y
db<>fiddle here

Oracle SQL intersection of 2 comma separated string

What can I apply as function?
Query:
Select x, f(y) from table where y like '%ab%cd%ef';
sample table(y is sorted alphabatically)
x. y
1 ab
2 ab,cd
3 cd,ef
4 ab,ef,gh,yu
5 de,ef,rt
Expected Output:
Output:
x y
1 ab
2 ab,cd
3 cd,ef
4 ab,ef
5 ef
Use regexp_substr function with connect by level expressions as
with tab(x,y) as
(
select 1,'ab' from dual union all
select 2,'ab,cd' from dual union all
select 3,'cd,ef' from dual union all
select 4,'ab,ef,gh,yu' from dual union all
select 5,'de,ef,rt' from dual
), tab2 as
(
Select x, regexp_substr(y,'[^,]+',1,level) as y
from tab
connect by level <= regexp_count(y,',') + 1
and prior x = x
and prior sys_guid() is not null
), tab3 as
(
select x, y
from tab2
where y like '%ab%'
or y like '%cd%'
or y like '%ef%'
)
select x, listagg(y,',') within group (order by y) as y
from tab3
group by x;
X Y
1 ab
2 ab,cd
3 cd,ef
4 ab,ef
5 ef
Demo
Follow comments written within the code.
SQL> with test (x, y) as
2 -- your sample table
3 (select 1, 'ab' from dual union all
4 select 2, 'ab,cd' from dual union all
5 select 3, 'cd,ef' from dual union all
6 select 4, 'ab,ef,gh,yu' from dual union all
7 select 5, 'de,ef,rt' from dual
8 ),
9 srch (val) as
10 -- a search string, which is to be compared to the sample table's Y column values
11 (select 'ab,cd,ef' from dual),
12 --
13 srch_rows as
14 -- split search string into rows
15 (select regexp_substr(val, '[^,]+', 1, level) val
16 from srch
17 connect by level <= regexp_count(val, ',') + 1
18 ),
19 test_rows as
20 -- split sample values into rows
21 (select x,
22 regexp_substr(y, '[^,]+', 1, column_value) y
23 from test,
24 table(cast(multiset(select level from dual
25 connect by level <= regexp_count(y, ',') + 1
26 ) as sys.odcinumberlist))
27 )
28 -- the final result
29 select t.x, listagg(t.y, ',') within group (order by t.y) result
30 from test_rows t join srch_rows s on s.val = t.y
31 group by t.x
32 order by t.x;
X RESULT
---------- --------------------
1 ab
2 ab,cd
3 cd,ef
4 ab,ef
5 ef
SQL>

Find closest or higher values in SQL

I have a table:
table1
rank value
1 10
25 120
29 130
99 980
I have to generate the following table:
table2
rank value
1 10
2 10
3 10
4 10
5 10
6 10
7 10
8 10
9 10
10 10
11 10
12 10
13 120
14 120
15 120
.
.
.
.
25 120
26 120
27 130
28 130
29 130
30 130
.
.
.
.
.
62 980
63 980
.
.
.
99 980
100 980
So, table2 should have all values from 1 to 100. There are 3 cases:
If it's an exact match, for ex. rank 25, value would be 120
Find closest, for ex. for rank 9 in table2, we do NOT have exact match, but 1 is closest to 9 (9-1 = 8 whereas 25-9 = 16), so assign value of 1
If there is equal distribution from both sides, use higher rank value, for ex. for rank 27, we have 25 as well as 29 which are equally distant, so take higher value which is 29 and assign value.
something like
-- your testdata
with table1(rank,
value) as
(select 1, 10
from dual
union all
select 25, 120
from dual
union all
select 29, 130
from dual
union all
select 99, 980
from dual),
-- range 1..100
data(rank) as
(select level from dual connect by level <= 100)
select d.rank,
min(t.value) keep(dense_rank first order by abs(t.rank - d.rank) asc, t.rank desc)
from table1 t, data d
group by d.rank;
If I understand well your need, you could use the following:
select num, value
from (
select num, value, row_number() over (partition by num order by abs(num-rank) asc, rank desc) as rn
from table1
cross join ( select level as num from dual connect by level <= 100) numbers
)
where rn = 1
This joins your table with the [1,100] interval and then keeps only the first row for each number, ordering by the difference and keeping, in case of equal difference, the greatest value.
Join hierarchical number generator with your table and use lag() with ignore nulls clause:
select h.rank, case when value is null
then lag(value ignore nulls) over (order by h.rank)
else value
end value
from (select level rank from dual connect by level <= 100) h
left join t on h.rank = t.rank
order by h.rank
Test:
with t(rank, value) as (
select 1, 10 from dual union all
select 25, 120 from dual union all
select 29, 130 from dual union all
select 99, 980 from dual )
select h.rank, case when value is null
then lag(value ignore nulls) over (order by h.rank)
else value
end value
from (select level rank from dual connect by level <= 100) h
left join t on h.rank = t.rank
order by h.rank
RANK RANK
---------- ----------
1 10
2 10
...
24 10
25 120
26 120
27 120
28 120
29 130
30 130
...
98 130
99 980
100 980
Here's an alternative that doesn't need a cross join (but does use a couple of analytic functions, so you'd need to test whether this is more performant for your set of data than the other solutions):
WITH sample_data AS (SELECT 1 rnk, 10 VALUE FROM dual UNION ALL
SELECT 25 rnk, 120 VALUE FROM dual UNION ALL
SELECT 29 rnk, 130 VALUE FROM dual UNION ALL
SELECT 99 rnk, 980 VALUE FROM dual)
SELECT rnk + LEVEL - 1 rnk,
CASE WHEN rnk + LEVEL - 1 < rnk + (next_rank - rnk)/2 THEN
VALUE
ELSE next_value
END VALUE
FROM (SELECT rnk,
VALUE,
LEAD(rnk, 1, 100 + 1) OVER (ORDER BY rnk) next_rank,
LEAD(VALUE, 1, VALUE) OVER (ORDER BY rnk) next_value
FROM sample_data)
CONNECT BY PRIOR rnk = rnk
AND PRIOR sys_guid() IS NOT NULL
AND LEVEL <= next_rank - rnk;
RNK VALUE
---------- ----------
1 10
2 10
... ...
12 10
13 120
... ...
24 120
25 120
26 120
27 130
28 130
29 130
30 130
... ...
63 130
64 980
65 980
... ...
98 980
99 980
100 980
N.B, I'm not sure why you have 62 and 63 as having a value of 980 - the mid point between 29 and 99 is 64.
Also, you'll see that I've used 100 + 1 instead of 101 - this is because if you wanted to parameterise things, you would replace 100 with the parameter - e.g. v_max_rank + 1
You can use cross join. Use the below query to get your result:
select t1.* from table1 t1
cross join
(select * from table1) t2
on (t1.rank=t2.rank);