(Oracle)Getting 25th number using interpolating - oracle

My goal is to get 25th number. For instance I have 4 row, such as 3,4,5 and 7.
My goal is to get 1.25th(=(4+1)0.25).
Expected result is 3.25 which is obtained by interpolating(3+0.25(4-3)).
I have tried as below.
But is there any other efficient way?
WITH DATASET AS (
SELECT 3 C1 FROM DUAL
UNION
SELECT 4 FROM DUAL
UNION
SELECT 5 FROM DUAL
UNION
SELECT 7 FROM DUAL
)
SELECT
--RNK, C1, NEXTC1-C1, FIRSTQLOCAION, FIRSTQLOCAION-RNK, C1+(NEXTC1-C1)*(FIRSTQLOCAION-RNK)
C1+(NEXTC1-C1)*(FIRSTQLOCAION-RNK)
FROM(
SELECT C1,
LEAD(C1, 1) OVER (ORDER BY C1) as NEXTC1 ,
RANK() OVER (ORDER BY C1) AS RNK,
((SUM(1) OVER (PARTITION BY NULL)) +1) * 0.25 AS FIRSTQLOCAION
FROM DATASET
)
WHERE
FIRSTQLOCAION>=RNK AND FIRSTQLOCAION<=RNK+1;

You can use analytical function as follows:
Select c1,
c1 + (
(((Count(1) over () + 1)*0.25) - 1) * (lead(c1) over (order by c1) - c1)
) as calculated_number from
From your_table t
In this solution last record will have calculated value null as lead value will be null and you will have to adjust its value as per your requirement.
If your expectation is a single number from query then usw following:
Select min(c1) +
0.25 * (min(case when rn = 2 then c1 end)
- min(case when rn = 1 then c1 end)) as calculated_number
from
(Select t.*,
Row_number() over (order by c1)
From t)

WITH t AS (
SELECT 3 C1 FROM DUAL
UNION
SELECT 4 FROM DUAL
UNION
SELECT 5 FROM DUAL
UNION
SELECT 7 FROM DUAL
)
SELECT rn,location, calculated
FROM (
Select rn, c1,
C1 +(Count(1) over () + 1)*0.25
-trunc( (Count(1) over () + 1)*0.25 ) *(lead(c1) over (order by c1) - c1) as calculated, --
trunc( (Count(1) over () + 1)*0.25 ) as location --
From (Select t.*, Row_number() over (order by c1) rn From t) ) WHERE rn=location;

Related

How to get the data from oracle on the following demand?

The table like this.
bh sl productdate
a1 100 2022-1-1
a1 220 2022-1-2
a1 220 2022-1-3
a2 200 2022-1-1
a2 350 2022-1-2
a2 350 2022-1-3
The result like this.
bh sl_q(sl_before) sl_h(sl_after) sl_b(changeValue) productdate
a1 100 220 120 2022-1-2
a2 200 350 150 2022-1-2
Rules:the same field bh, when the field sl change,then get the record.
We can use a ROW_NUMBER trick here:
WITH cte AS (
SELECT t.*, ROW_NUMBER() OVER (PARTITION BY bh ORDER BY productdate) rn1,
ROW_NUMBER() OVER (PARTITION BY bh ORDER BY productdate DESC) rn2
FROM yourTable t
)
SELECT bh, MAX(CASE WHEN rn1 = 1 THEN sl END) AS sl_q,
MAX(CASE WHEN rn2 = 1 THEN sl END) AS sl_h,
MAX(CASE WHEN rn2 = 1 THEN sl END) -
MAX(CASE WHEN rn1 = 1 THEN sl END) AS sl_b
FROM cte
GROUP BY bh;

Divide columns - oracle

Hello/ I need divide this table
I try use this but have bad result
SELECT MIN(y1),
MAX(y1),
MIN(redempamount1),
MAX(redempamount1),
MIN(f1),
MIN(f2),
MAX(f1),
MAX(f2)
FROM (SELECT to_number(y1) y1,
redempamount1,
add_months('02.03.2018', 12 * y1 - 12) f1,
add_months('02.03.2018', 12 * y1) - 1 f2,
row_number() over(PARTITION BY ntl ORDER BY rownum DESC) grp
FROM (SELECT to_number(t.y1) y1,
redempamount1,
add_months('02.03.2018', 12 * y1 - 12) f1,
add_months('02.03.2018', 12 * y1) - 1 f2,
ntile(2) over(ORDER BY rownum DESC) ntl
FROM inspolreport t))
GROUP BY grp;
Y1 REDEMPAMOUNT1 F1 F2
-- ------------- ---------- ----------
1 0 18.12.2008 17.12.2009
2 3362.54 18.12.2009 17.12.2010
3 6030.84 18.12.2010 17.12.2011
4 8873.52 18.12.2011 17.12.2012
5 11830.9 18.12.2012 17.12.2013
6 15041.83 18.12.2013 17.12.2014
7 18382.37 18.12.2014 17.12.2015
8 21857.15 18.12.2015 17.12.2016
9 25472.97 18.12.2016 17.12.2017
10 27359.51 18.12.2017 17.12.2018
11 31690.65 18.12.2018 17.12.2019
12 36195 18.12.2019 17.12.2020
13 40879.06 18.12.2020 17.12.2021
14 45750.5 18.12.2021 17.12.2022
15 53491.44 18.12.2022 17.12.2023
expected output
Y1 REDEMPAMOUNT1 F1 F2 Y2 REDEMPAMOUNT1 F3 F4
-- ------------- ---------- ---------- -- ------------- ---------- ----------
1 0 18.12.2008 17.12.2009 9 25472.97 18.12.2016 17.12.2017
2 3362.54 18.12.2009 17.12.2010 10 27359.51 18.12.2017 17.12.2018
3 6030.84 18.12.2010 17.12.2011 11 31690.65 18.12.2018 17.12.2019
4 8873.52 18.12.2011 17.12.2012 12 36195 18.12.2019 17.12.2020
5 11830.9 18.12.2012 17.12.2013 13 40879.06 18.12.2020 17.12.2021
6 15041.83 18.12.2013 17.12.2014 14 45750.5 18.12.2021 17.12.2022
7 18382.37 18.12.2014 17.12.2015 15 53491.44 18.12.2022 17.12.2023
8 21857.15 18.12.2015 17.12.2016
If what you want to do is split your results into two side-by-side sets of data, then you will need to pivot your data accordingly.
I can see that is what you're trying to do, but you need to use conditional aggregates to produce the columns.
So you want something like:
WITH your_table AS (SELECT 1 y1, 0 redempamount1, to_date('18/12/2008', 'dd/mm/yyyy') f1, to_date('17/12/2009', 'dd/mm/yyyy') f2 FROM dual UNION ALL
SELECT 2 y1, 3362.54 redempamount1, to_date('18/12/2009', 'dd/mm/yyyy') f1, to_date('17/12/2010', 'dd/mm/yyyy') f2 FROM dual UNION ALL
SELECT 3 y1, 6030.84 redempamount1, to_date('18/12/2010', 'dd/mm/yyyy') f1, to_date('17/12/2011', 'dd/mm/yyyy') f2 FROM dual UNION ALL
SELECT 4 y1, 8873.52 redempamount1, to_date('18/12/2011', 'dd/mm/yyyy') f1, to_date('17/12/2011', 'dd/mm/yyyy') f2 FROM dual UNION ALL
SELECT 5 y1, 15041.83 redempamount1, to_date('18/12/2012', 'dd/mm/yyyy') f1, to_date('17/12/2011', 'dd/mm/yyyy') f2 FROM dual UNION ALL
SELECT 6 y1, 15041.83 redempamount1, to_date('18/12/2013', 'dd/mm/yyyy') f1, to_date('17/12/2011', 'dd/mm/yyyy') f2 FROM dual UNION ALL
SELECT 7 y1, 18382.37 redempamount1, to_date('18/12/2014', 'dd/mm/yyyy') f1, to_date('17/12/2011', 'dd/mm/yyyy') f2 FROM dual UNION ALL
SELECT 8 y1, 21857.15 redempamount1, to_date('18/12/2015', 'dd/mm/yyyy') f1, to_date('17/12/2011', 'dd/mm/yyyy') f2 FROM dual UNION ALL
SELECT 9 y1, 25472.97 redempamount1, to_date('18/12/2016', 'dd/mm/yyyy') f1, to_date('17/12/2011', 'dd/mm/yyyy') f2 FROM dual UNION ALL
SELECT 10 y1, 27359.51 redempamount1, to_date('18/12/2017', 'dd/mm/yyyy') f1, to_date('17/12/2011', 'dd/mm/yyyy') f2 FROM dual UNION ALL
SELECT 11 y1, 31690.65 redempamount1, to_date('18/12/2018', 'dd/mm/yyyy') f1, to_date('17/12/2011', 'dd/mm/yyyy') f2 FROM dual UNION ALL
SELECT 12 y1, 36195 redempamount1, to_date('18/12/2019', 'dd/mm/yyyy') f1, to_date('17/12/2020', 'dd/mm/yyyy') f2 FROM dual UNION ALL
SELECT 13 y1, 40879.06 redempamount1, to_date('18/12/2020', 'dd/mm/yyyy') f1, to_date('17/12/2021', 'dd/mm/yyyy') f2 FROM dual UNION ALL
SELECT 14 y1, 45750.5 redempamount1, to_date('18/12/2021', 'dd/mm/yyyy') f1, to_date('17/12/2022', 'dd/mm/yyyy') f2 FROM dual UNION ALL
SELECT 15 y1, 53491.44 redempamount1, to_date('18/12/2022', 'dd/mm/yyyy') f1, to_date('17/12/2023', 'dd/mm/yyyy') f2 FROM dual)
SELECT MAX(CASE WHEN grp = 1 THEN y1 END) AS y1,
MAX(CASE WHEN grp = 1 THEN redempamount1 END) AS redempamount1,
MAX(CASE WHEN grp = 1 THEN f1 END) AS f1,
MAX(CASE WHEN grp = 1 THEN f2 END) AS f2,
MAX(CASE WHEN grp = 2 THEN y1 END) AS y2,
MAX(CASE WHEN grp = 2 THEN redempamount1 END) AS redempamount2,
MAX(CASE WHEN grp = 2 THEN f1 END) AS f3,
MAX(CASE WHEN grp = 2 THEN f2 END) AS f4
FROM (SELECT y1,
redempamount1,
f1,
f2,
grp,
row_number() OVER (PARTITION BY grp ORDER BY y1) rn
FROM (SELECT y1,
redempamount1,
f1,
f2,
NTILE(2) OVER (ORDER BY y1) grp
FROM your_table))
GROUP BY rn
ORDER BY rn;
Y1 REDEMPAMOUNT1 F1 F2 Y2 REDEMPAMOUNT2 F3 F4
---------- ------------- ----------- ----------- ---------- ------------- ----------- -----------
1 0 18/12/2008 17/12/2009 9 25472.97 18/12/2016 17/12/2011
2 3362.54 18/12/2009 17/12/2010 10 27359.51 18/12/2017 17/12/2011
3 6030.84 18/12/2010 17/12/2011 11 31690.65 18/12/2018 17/12/2011
4 8873.52 18/12/2011 17/12/2011 12 36195 18/12/2019 17/12/2020
5 15041.83 18/12/2012 17/12/2011 13 40879.06 18/12/2020 17/12/2021
6 15041.83 18/12/2013 17/12/2011 14 45750.5 18/12/2021 17/12/2022
7 18382.37 18/12/2014 17/12/2011 15 53491.44 18/12/2022 17/12/2023
8 21857.15 18/12/2015 17/12/2011
However, if you are in 11g and above, you can use the PIVOT command, which I would argue makes your intentions clearer:
SELECT o_y1 AS y1,
o_redempamount1 AS redempamount1,
o_f1 AS f1,
o_f2 AS f2,
t_y1 AS y2,
t_redempamount1 AS redempamount2,
t_f1 AS f3,
t_f2 AS f4
FROM (SELECT y1,
redempamount1,
f1,
f2,
grp,
row_number() OVER (PARTITION BY grp ORDER BY y1) rn
FROM (SELECT y1,
redempamount1,
f1,
f2,
NTILE(2) OVER (ORDER BY y1) grp
FROM your_table))
PIVOT (MAX(y1) AS y1, MAX(redempamount1) AS redempamount1, MAX(f1) AS f1, MAX(f2) AS f2
FOR grp IN (1 AS o, 2 AS t))
ORDER BY rn;

calculate percentage of two select counts

I have a query like
select count(1) from table_a where state=1;
it gives 20
select count(1) from table_a where state in (1,2);
it gives 25
I would like to have a query to extract percentage 80% (will be 20*100/25).
Is possible to have these in only one query?
I think without testing that the following SQL command can do that
SELECT SUM(CASE WHEN STATE = 1 THEN 1 ELSE 0 END)
/SUM(CASE WHEN STATE IN (1,2) THEN 1 ELSE 0 END)
as PERCENTAGE
FROM TABLE_A
or the following
SELECT S1 / (S1 + S2) as S1_PERCENTAGE
FROM
(
SELECT SUM(CASE WHEN STATE = 1 THEN 1 ELSE 0 END) as S1
,SUM(CASE WHEN STATE = 2 THEN 1 ELSE 0 END) as S2
FROM TABLE_A
)
or the following
SELECT S1 / T as S1_PERCENTAGE
FROM
(
SELECT SUM(CASE WHEN STATE = 1 THEN 1 ELSE 0 END) as S1
,SUM(CASE WHEN STATE IN (1,2) THEN 1 ELSE 0 END) as T
FROM TABLE_A
)
you have the choice for performance or readability !
Just as a slight variation on #schlebe's first query, you can continue to use count() by making that conditional:
select count(case when state = 1 then state end)
/ count(case when state in (1, 2) then state end) as result
from table_a
or multiplying by 100 to get a percentage instead of a decimal:
select 100 * count(case when state = 1 then state end)
/ count(case when state in (1,2) then state end) as percentage
from table_a
Count ignores nulls, and both of the case expressions default to null if their conditions are not met (you could have else null to make it explicit too).
Quick demo with a CTE for dummy data:
with table_a(state) as (
select 1 from dual connect by level <= 20
union all select 2 from dual connect by level <= 5
union all select 3 from dual connect by level <= 42
)
select 100 * count(case when state = 1 then state end)
/ count(case when state in (1,2) then state end) as percentage
from table_a;
PERCENTAGE
----------
80
Why the plsql tag? Regardless, i think what you need is:
(select count(1) from table_a where state=1) * 100 / (select count(1) from table_a where state in (1,2)) from dual

Oracle comma seperated to rows with grouping

I have the following requirement.
Do we have direct functions available in oracle 12c to accomplish this.
create table t1(input_name varchar2(500),input_values varchar2(500));
insert into t1 values('a,b,c,d,','1,2,3,4');
insert into t1 values('e,f,g,','5,6,7');
insert into t1 values('a1,b1,c1,d1,','11,12,13,14');
insert into t1 values('d,c,b,a,','100,200,300,400');
commit;
select * from t1;
INPUT_NAME INPUT_VALUES
------------------------------ ----------------
a,b,c,d, 1,2,3,4
e,f,g, 5,6,7
a1,b1,c1,d1, 11,12,13,14
d,c,b,a, 100,200,300,400
output:
a b c d e f g a1 b1 c1 d1
1 2 3 4 5 6 7 11 12 13 14
400 300 200 100
Thanks,
Rahmat Ali
Yes... if you have a known set of input names. But you would be better reorganising your data so that you are not storing correlated pairs of comma-separated lists.
SQL Fiddle
Oracle 11g R2 Schema Setup:
create table t1(input_name,input_values) AS
SELECT 'a,b,c,d,','1,2,3,4' FROM DUAL UNION ALL
SELECT 'e,f,g,','5,6,7' FROM DUAL UNION ALL
SELECT 'a1,b1,c1,d1,','11,12,13,14' FROM DUAL UNION ALL
SELECT 'd,c,b,a,','100,200,300,400' FROM DUAL
/
CREATE TYPE pair IS OBJECT(
name VARCHAR2(20),
value VARCHAR2(20)
)
/
CREATE TYPE pair_table IS TABLE OF PAIR
/
Query 1:
SELECT MAX( CASE name WHEN 'a' THEN value END ) AS a,
MAX( CASE name WHEN 'b' THEN value END ) AS b,
MAX( CASE name WHEN 'c' THEN value END ) AS c,
MAX( CASE name WHEN 'd' THEN value END ) AS d,
MAX( CASE name WHEN 'e' THEN value END ) AS e,
MAX( CASE name WHEN 'f' THEN value END ) AS f,
MAX( CASE name WHEN 'g' THEN value END ) AS g,
MAX( CASE name WHEN 'a1' THEN value END ) AS a1,
MAX( CASE name WHEN 'b1' THEN value END ) AS b1,
MAX( CASE name WHEN 'c1' THEN value END ) AS c1,
MAX( CASE name WHEN 'd1' THEN value END ) AS d1
FROM (
SELECT v.name,
v.value,
ROW_NUMBER() OVER ( PARTITION BY v.name ORDER BY ROWNUM ) AS rn
FROM t1 t
CROSS JOIN
TABLE(
CAST(
MULTISET(
SELECT pair(
REGEXP_SUBSTR( t.input_name, '([^,]+)(,|$)', 1, LEVEL, NULL, 1 ),
REGEXP_SUBSTR( t.input_values, '([^,]+)(,|$)', 1, LEVEL, NULL, 1 )
)
FROM DUAL
CONNECT BY level <= REGEXP_COUNT( t.input_name, '([^,]+)(,|$)' )
) AS pair_table
)
) v
)
GROUP BY rn
Results:
| A | B | C | D | E | F | G | A1 | B1 | C1 | D1 |
|-----|-----|-----|-----|--------|--------|--------|--------|--------|--------|--------|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 14 |
| 400 | 300 | 200 | 100 | (null) | (null) | (null) | (null) | (null) | (null) | (null) |
You can also use a PIVOT statement rather than multiple MAX( CASE ... END ) statements.
If you do not have a static set of input names then you will need to search for dynamic pivots.
Update:
Is there a way where I can avoid using types?
You can avoid creating types and just use a built-in VARRAY or collection like SYS.ODCIVARCHAR2LIST but then you will need two lists and it becomes complicated to correlate between the two.
WITH input_names ( rid, idx, name ) AS (
SELECT t.ROWID,
ROW_NUMBER() OVER ( PARTITION BY t.ROWID ORDER BY ROWNUM ) AS rn,
v.COLUMN_VALUE
FROM t1 t
CROSS JOIN
TABLE(
CAST(
MULTISET(
SELECT REGEXP_SUBSTR( t.input_name, '([^,]+)(,|$)', 1, LEVEL, NULL, 1 )
FROM DUAL
CONNECT BY level <= REGEXP_COUNT( t.input_name, '([^,]+)(,|$)' )
) AS SYS.ODCIVARCHAR2LIST
)
) v
),
input_values ( rid, idx, value ) AS (
SELECT t.ROWID,
ROW_NUMBER() OVER ( PARTITION BY t.ROWID ORDER BY ROWNUM ) AS rn,
v.COLUMN_VALUE
FROM t1 t
CROSS JOIN
TABLE(
CAST(
MULTISET(
SELECT REGEXP_SUBSTR( t.input_values, '([^,]+)(,|$)', 1, LEVEL, NULL, 1 )
FROM DUAL
CONNECT BY level <= REGEXP_COUNT( t.input_values, '([^,]+)(,|$)' )
) AS SYS.ODCIVARCHAR2LIST
)
) v
),
correlated ( name, value, rn ) AS (
SELECT n.name,
v.value,
ROW_NUMBER() OVER ( PARTITION BY n.name
ORDER BY ROWNUM )
FROM input_names n
INNER JOIN
input_values v
ON ( n.rid = v.rid AND n.idx = v.idx )
)
SELECT MAX( CASE name WHEN 'a' THEN value END ) AS a,
MAX( CASE name WHEN 'b' THEN value END ) AS b,
MAX( CASE name WHEN 'c' THEN value END ) AS c,
MAX( CASE name WHEN 'd' THEN value END ) AS d,
MAX( CASE name WHEN 'e' THEN value END ) AS e,
MAX( CASE name WHEN 'f' THEN value END ) AS f,
MAX( CASE name WHEN 'g' THEN value END ) AS g,
MAX( CASE name WHEN 'a1' THEN value END ) AS a1,
MAX( CASE name WHEN 'b1' THEN value END ) AS b1,
MAX( CASE name WHEN 'c1' THEN value END ) AS c1,
MAX( CASE name WHEN 'd1' THEN value END ) AS d1
FROM correlated
GROUP BY rn;

Convert SQL Server CTE into Oracle CTE

Here is the SQL Server CTE, trying to convert to Oracle CTE or regular oracle query..
;with cte as
(Select AC, M, Y, D, E, F, CD
from tblA
WHere
(Y = YEAR(GETDATE()) and M = Month(dateadd(month, -1, GETDATE())))
),
cte2 as
(Select A.AC,Max(A.Y)as Y, Max(A.M) as M, Max(A.CD) as CD
from tbl A
Inner join cte B on B.AC = A.AC
WHere A.CD is Not Null and B.CD is Null
Group by A.AC)
, cte3 as
(Select C.AC, C.Y, C.M, C.D, C.E, C.F, C.CD
from tblA C
Inner join cte2 D on C.AC = D.AC and C.Y= D.Y and C.M = D.M and
D.CD = C.CD
)
select * from cte
union
select * from cte3;
Assuming you didn't have the m and y columns reversed on purpose in your cte/cte3 select lists, I think you could rewrite your query as:
with cte1 as (select a.ac,
a.m,
a.y,
a.d,
a.e,
a.f,
a.cd,
max(case when a.cd is not null and b.cd is not null then a.y end) over (partition by a.ac) max_y,
max(case when a.cd is not null and b.cd is not null then a.m end) over (partition by a.ac) max_m,
max(case when a.cd is not null and b.cd is not null then a.cd end) over (partition by a.ac) max_cd
from tbla a
left outer join tblb b on (a.ac = b.ac))
select ac,
m,
y,
d,
e,
f,
cd
from cte1
where (y = to_char(sysdate, 'yyyy')
and m = to_char(add_months(sysdate, -1), 'mm'))
or (y = max_y
and m = max_m
and cd = max_cd);
You haven't provided any sample data, so I can't test, but it would be worth converting the date functions to their SQL Server equivalents and testing to make sure the data returned is the same.
This way, you're not querying the same table 3 times, which should improve the performance some.

Resources