Sql range query - sql

Query returning 2 values from table TBL_CHARGES:
Range_in_hrs Range_to_hours charges
4 48 5
48 70 10
70 90 20
Select charges from table if range 47.59 is passed the return charge should be 5. If 48.00 is passed the charges will be 5.
If 48.01 is passed the charges should be 10.
I am trying this
SELECT *
FROM TBL_CHARGES
WHERE 48.00 between Range_in_hrs and Range_to_hours
But it does not works.

Sounds like you need to avoid using BETWEEN and instead use explicit ranges, like so:
select *
from tbl_charges
where 48 > range_in_hrs
and 48 <= range_to_hours;
And here's an example that shows the output you might get with various different values:
with tbl_charges as (select 4 range_in_hours, 48 range_to_hours, 5 charges from dual union all
select 48 range_in_hours, 70 range_to_hours, 10 charges from dual union all
select 70 range_in_hours, 90 range_to_hours, 20 charges from dual),
vals as (select 4 val from dual union all
select 4.01 val from dual union all
select 47.59 val from dual union all
select 48 val from dual union all
select 48.01 val from dual union all
select 70 val from dual union all
select 71 val from dual)
select vals.val,
tc.charges
from vals
left outer join tbl_charges tc on (vals.val > tc.range_in_hours and vals.val <= tc.range_to_hours);
VAL CHARGES
---------- ----------
4
4.01 5
47.59 5
48 5
48.01 10
70 10
71 20

My problem got solved by this query.
SELECT *
FROM TBL_CHARGES
WHERE 48.00 between Range_in_hrs||'.01' and Range_to_hours||'.00'

SELECT charges
FROM TBL_CHARGES
WHERE ceil(to_number('48.01')) between Range_in_hrs and Range_to_hours
Use Replace and Ceil.

Related

All tables consisting of numbers less than a fixed number

I am trying to find out all the tables where table names consist of numbers less than a fixed number 16284961 at the end preceded by an underscore for example LOG_16282961.
Sample User_segments table:
Segment_name Bytes
---------------------------------------
LOG_16282961 34
BAL1_16282961 78
BIN$xIDte/qXAFbgU4IeBEeQpw==$0 12
EXCH_16282961 28
C$_0LOG_16282961 17
LOG_16283961 89
BAL1_16283961 10
BIN$xIDte/qWAFbgU4IeBEeQpw==$0 19
EXCH_16283961 90
C$_0LOG_16283961 45
LOG_16284961 21
BAL1_16284961 81
BIN$w1RLAvSeAWjgU4IeBEe2Mw==$0 33
EXCH_16284961 67
C$_0LOG_16284961 39
.......................................
.......................................
Expected Output:
Segment_name Bytes
----------------------
LOG_16282961 34
BAL1_16282961 78
EXCH_16282961 28
C$_0LOG_16282961 17
LOG_16283961 89
BAL1_16283961 10
EXCH_16283961 90
C$_0LOG_16283961 45
.......................
.......................
Query:
SELECT segment_name, bytes/1024/1024 AS "SIZE in MB" FROM user_segments WHERE segment_type='TABLE' AND to_number(regexp_substr(segment_name, '[0-9]+')) < 16284961;
Using above query, although I am getting my result but additionally it also includes following tables which are not required in my output:
BIN$xIDte/qXAFbgU4IeBEeQpw==$0 12
BIN$xIDte/qWAFbgU4IeBEeQpw==$0 19
BIN$w1RLAvSeAWjgU4IeBEe2Mw==$0 33
Can you please help fix my query to get the desired output? Thanks.
Here's one way - using regexp_substr to isolate one or more consecutive digits at the end of the input string, only if immediately preceded by underscore. (If the string does not have that structure, regexp_substr returns null and the filter condition becomes null < [something], which is never true.)
Create mock-up table for testing:
create table test_data (segment_name, bytes) as
select 'LOG_16282961' , 34 from dual union all
select 'BAL1_16282961' , 78 from dual union all
select 'BIN$xIDte/qXAFbgU4IeBEeQpw==$0', 12 from dual union all
select 'EXCH_16282961' , 28 from dual union all
select 'C$_0LOG_16282961' , 17 from dual union all
select 'LOG_16283961' , 89 from dual union all
select 'BAL1_16283961' , 10 from dual union all
select 'BIN$xIDte/qWAFbgU4IeBEeQpw==$0', 19 from dual union all
select 'EXCH_16283961' , 90 from dual union all
select 'C$_0LOG_16283961' , 45 from dual union all
select 'LOG_16284961' , 21 from dual union all
select 'BAL1_16284961' , 81 from dual union all
select 'BIN$w1RLAvSeAWjgU4IeBEe2Mw==$0', 33 from dual union all
select 'EXCH_16284961' , 67 from dual union all
select 'C$_0LOG_16284961' , 39 from dual
;
Query and output:
select *
from test_data
where to_number(regexp_substr(segment_name, '_(\d+)$', 1, 1, null, 1))
< 16284961
;
SEGMENT_NAME BYTES
------------------------------ ----------
LOG_16282961 34
BAL1_16282961 78
EXCH_16282961 28
C$_0LOG_16282961 17
LOG_16283961 89
BAL1_16283961 10
EXCH_16283961 90
C$_0LOG_16283961 45
If query you wrote works, just omit tables you don't want. Those you mentioned have been dropped and are now in recycle bin. So, either purge recyclebin before running the query, or use additional condition, e.g.
SELECT segment_name, bytes / 1024 / 1024 AS "SIZE in MB"
FROM user_segments
WHERE segment_type = 'TABLE'
AND substr(segment_name, 1, 4) <> 'BIN$' --> this
AND TO_NUMBER (REGEXP_SUBSTR (segment_name, '[0-9]+')) < 16284961;

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

To find the starting and ending points in a sequence

I've a table (T1) with one column (C1) with the below values
1
2
3
5
6
8
9
10
I want the output to print the continuous sequences with start and ending points like below.
1-3
5-6
8-10
Could you please help?
Any Database is fine.
Oracle: sample data first, while code you really need begins at line #11.
SQL> with t1 (c1) as
2 (select 1 from dual union all
3 select 2 from dual union all
4 select 3 from dual union all
5 select 5 from dual union all
6 select 6 from dual union all
7 select 8 from dual union all
8 select 9 from dual union all
9 select 10 from dual
10 )
11 select min(c1), max(c1)
12 from (select c1, c1 - row_number() over (order by c1) rn
13 from t1
14 )
15 group by rn
16 order by rn;
MIN(C1) MAX(C1)
---------- ----------
1 3
5 6
8 10
SQL>
You can use the following query. I have tested it with SQL Server, but I think it will work without modifications in Oracle:
create table t1(c1 int);
insert into t1
select *
from (values(1),(2),(3),(5),(6),(8),(9),(10))t(x);
select case when count(*) >1 then
concat(min(c1),'-',max(c1))
else concat(max(c1),'')
end as concat_cs
from (
select c1
,ROW_NUMBER() over(order by c1 asc) as rnk
,c1 - ROW_NUMBER() over(order by c1 asc) as grp
from t1
)x
group by x.grp
Output
concat_cs
1-3
5-6
8-10
with stab as (
select 1 as val from dual union all
select 2 as val from dual union all
select 3 as val from dual union all
select 5 as val from dual union all
select 6 as val from dual union all
select 8 as val from dual union all
select 9 as val from dual union all
select 10 as val from dual union all
select 13 as val from dual union all
select 15 as val from dual union all
select 16 as val from dual union all
select 17 as val from dual union all
select 18 as val from dual union all
select 19 as val from dual union all
select 23 as val from dual
),sq2 as(
select
row_number() over(order by 1) as rownumber,val
from stab
)
select
a.val,b.val
from sq2 A
join sq2 b on b.rownumber = a.rownumber+2
where mod(A.rownumber,3)=1
Output:
1 3
5 8
9 13
15 17
18 23

Want a SQL to group values by value scope in oracle

I have a table t1 and there is a column named days, so I want to group the days by 1 to 5 days and 6-15 days and more than 15 days then calculate the count for each group, but I don't know how to write the sql, can anyone tell me? the result should be looked like below:
Number Scope(days)
5 1-5
7 6-15
10 15+
If I understand well, this could be a way:
with t1(days) as (
select 1 from dual union all
select 2 from dual union all
select 5 from dual union all
select 6 from dual union all
select 7 from dual union all
select 8 from dual union all
select 10 from dual union all
select 11 from dual union all
select 16 from dual union all
select 17 from dual union all
select 19 from dual union all
select 19 from dual
)
/* the query */
select count(*),
case
when days between 1 and 5 then '1-5'
when days between 6 and 15 then '6-15'
else '+15'
end
from t1
group by case
when days between 1 and 5 then '1-5'
when days between 6 and 15 then '6-15'
else '+15'
end
that gives:
COUNT(*) CASE
---------- ----
3 1-5
4 +15
5 6-15
The idea is to aggregate by something that say the "group" where every number is, and you can easily build such an information with a CASE.
Accordin to Jarlh's suggestion, this can be re-written as
select count(*), the_group
from (
select case
when days between 1 and 5 then '1-5'
when days between 6 and 15 then '6-15'
else '+15'
end the_group
from t1
)
group by the_group
This obviously assumes that you only have positive numbers.

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);