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

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;

Related

aggregate data from 2 tables oracle SQL

I have these data in 3 tables:
table 1: BU
BU_CODE
ARCHIVE_FLG
1001
Y
1002
Y
1003
Y
1004
N
1005
Y
table 2: STG_ACCOUNT
BU_CODE
ACCOUNT_ID
1001
A0001
1001
A0003
1002
A0002
table 3: STG_CONTRACT
BU_CODE
CONTRACT_ID
1002
C0001
1002
C0002
These 2 queries work fine:
Query 1:
SELECT
T2.BU_CODE, COUNT(T1.ACCOUNT_ID) AS COUNT_OF_ACCOUNT
FROM STG_ACCOUNT T1
FULL JOIN S_BU T2 ON T2.BU_CODE = T1.BU_CODE
WHERE T2.ARCHIVE_FLG = '1'
GROUP BY T2.BU_CODE
ORDER BY T2.BU_CODE;
BU_CODE
COUNT_OF_ACCOUNT
1001
2
1002
1
1003
0
1005
0
Query 2:
SELECT
T2.BU_CODE, COUNT(T1.CONTRACT_ID) AS COUNT_OF_CONTRACT
FROM STG_CONTRACT T1
FULL JOIN S_BU T2 ON T2.BU_CODE = T1.BU_CODE
WHERE T2.ARCHIVE_FLG = '1'
GROUP BY T2.BU_CODE
ORDER BY T2.BU_CODE;
BU_CODE
COUNT_OF_CONTRACT
1001
0
1002
2
1003
0
1005
0
Now I would like to merge the result of these 2 queries to show a more elegant output:
BU_CODE
COUNT_OF_ACCOUNT
COUNT_OF_CONTRACT
1001
2
0
1002
1
2
1003
0
0
1005
0
0
What Oracle SQL function can help me?
One option might be using CTE expressions
with x as
(
SELECT
T2.BU_CODE, COUNT(T1.ACCOUNT_ID) AS COUNT_OF_ACCOUNT
FROM STG_ACCOUNT T1
FULL JOIN S_BU T2 ON T2.BU_CODE = T1.BU_CODE
WHERE T2.ARCHIVE_FLG = '1'
GROUP BY T2.BU_CODE
ORDER BY T2.BU_CODE
),
y as
(
SELECT
T2.BU_CODE, COUNT(T1.CONTRACT_ID) AS COUNT_OF_CONTRACT
FROM STG_CONTRACT T1
FULL JOIN S_BU T2 ON T2.BU_CODE = T1.BU_CODE
WHERE T2.ARCHIVE_FLG = '1'
GROUP BY T2.BU_CODE
ORDER BY T2.BU_CODE
)
select x.bu_code , x.count_of_account, y.count_of_contract
from x join y on x.bu_code=y.bu_code
You can join both tables.
SELECT
T1.BU_CODE AS BU_CODE, COUNT(DISTINCT T2.ACCOUNT_ID) AS COUNT_OF_ACCOUNT, COUNT(DISTINCT T3.CONTRACT_ID) AS COUNT_OF_CONTRACT
FROM S_BU T1
LEFT JOIN STG_ACCOUNT T2 ON T1.BU_CODE = T2.BU_CODE
LEFT JOIN STG_CONTRACT T3 ON T1.BU_CODE = T3.BU_CODE
WHERE T1.ARCHIVE_FLG = '1'
GROUP BY T1.BU_CODE
ORDER BY T1.BU_CODE;

(Oracle)Getting 25th number using interpolating

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;

how i need to pick values depending upon other values

I have a table with data shown below
no s d
100 I D
100 C D
101 C null
101 I null
102 C D
102 I null
then i'm using this query to partition
create table pinky nologging as
select no,status,dead
from(select no,status,dead,
row_number() over(partition by no order by dead desc) seq
from PINK) d
where seq = 1;
i'm getting this results
100 I D
101 C null
102 I null
but i want data like shown below
100 C D
101 I NULL
102 I NULL
i.e,
FOR I AND C COMBINATION and both d column is D then pick C
FOR I AND C COMBINATION and both d column is null then pick I
FOR I AND C COMBINATION and d column is null AND d then pick null corresponding value
Assuming that there can be only one dead is null record for each no:
with
-- you data, remove it when running the query
-- in your environment ...
pink (no, status, dead) as
(select 100, 'I', 'D' from dual union
select 100, 'C', 'D' from dual union
select 101, 'C', null from dual union
select 101, 'I', null from dual union
select 102, 'C', 'D' from dual union
select 102, 'I', null from dual
),
-- ... end of you data
--
temp as ( -- a temporary table (CTE) where we make some
-- preliminary calculations
select pink.*,
-- count rows with status = 'I' for this no
sum(case when status = 'I' then 1 else 0 end) over(partition by no) ni,
-- count rows with status = 'C' for this no
sum(case when status = 'C' then 1 else 0 end) over(partition by no) nc,
-- count rows with dead = 'D' for this no
sum(case when dead = 'D' then 1 else 0 end) over(partition by no) nd,
-- total number of rows (in case it's not always = 2)
count(*) over(partition by no) n
from pink
)
select no, status, dead
from temp
where -- pick 'C' if there's also 'I' and all dead = 'D'
status = 'C' and ni > 0 and nd = n
-- pick 'I' if there's also 'C' and all dead is null
or status = 'I' and nc > 0 and nd = 0
-- pick dead is null if there are I's and C's and
-- all other dead's = 'D'
or dead is null and ni > 0 and nc > 0 and n - nd = 1;

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

how to add a subtotal rows in oracle?

i want to add subtotal in between the rows
for example
this the rows and column
NOTE : these three column i am fetching from maintable
a b c
a1 100 100
a2 200 200
a3 300 300
a4 400 400
a5 500 500
a6 600 600
a7 700 700
a8 800 800
a9 900 900
a10 900 900
i want like this
a b c
a1 100 100
a2 200 200
a3 300 300
a4 400 400
subtotal(a1 to a4) 1000 1000
a5 500 500
a6 600 600
subtotal(a5 to a6) 1100 1100
a7 700 700
subtotal(a7) 700 700
a8 800 800
a9 900 900
a10 900 900
subtotal(a8 to a10) 2600 2600
Grandtotal 5400 5400
note : grandtotal = addition of subtotals
Try to combine the use of rollup and grouping functions in this way:
select
decode(grouping(a),1,decode(grouping(subtot_id), 1, 'total', 'subtotal'),a) as a,
sum(b) as b, sum(c) as c
from (
select trunc((rownum-1)/3) as subtot_id, a, b, c
from your_table
order by a
)
group by rollup(subtot_id, a)
I made a sample here
I'm not very experienced in Oracle but I came out with this solution. Required types and function needs to be defined for each table you want to work with. It should be used on small tables because of the time of executing:
CREATE OR REPLACE TYPE row_abc
AS OBJECT (
a varchar2(10),
b integer,
c integer
);
CREATE OR REPLACE TYPE table_abc
AS TABLE OF row_abc;
CREATE OR REPLACE FUNCTION function_name(
report_freq INTEGER := 3
) RETURN table_abc AS
ret table_abc;
counter INTEGER := 0;
CURSOR abc_cursor IS
SELECT row_abc(a, b, c) FROM main_table;
BEGIN
ret := table_abc();
OPEN abc_cursor;
LOOP
counter := counter + 1;
IF counter!=1 AND mod(counter, report_freq)=1 THEN
ret.extend;
SELECT row_abc(
'subtotal',
(SELECT SUM(b) FROM
(SELECT b, rownum rn FROM main_table)
WHERE rn>=counter-report_freq AND rn<counter),
(SELECT SUM(c) FROM
(SELECT c, rownum rn FROM main_table)
WHERE rn>=counter-report_freq AND rn<counter)
) INTO ret(ret.count) FROM dual;
END IF;
ret.extend;
FETCH abc_cursor INTO ret(ret.count);
EXIT WHEN abc_cursor%NOTFOUND;
END LOOP;
CLOSE abc_cursor;
counter := counter - 1;
IF mod(counter, report_freq) != 0 THEN
SELECT row_abc(
'subtotal',
(SELECT SUM(b) FROM
(SELECT b, rownum rn FROM main_table)
WHERE rn>counter-mod(counter, report_freq) AND rn <=counter),
(SELECT SUM(c) FROM
(SELECT c, rownum rn FROM main_table)
WHERE rn>counter-mod(counter, report_freq)AND rn <=counter)
) INTO ret(ret.count) FROM dual;
ret.extend;
END IF;
SELECT row_abc(
'grandtotal',
(SELECT SUM(b) FROM TABLE(ret) WHERE a='subtotal'),
(SELECT SUM(c) FROM TABLE(ret) WHERE a='subtotal')
) INTO ret(ret.count) FROM dual;
RETURN ret;
END function_name;
And you execute it in sql with following line to get your requested output
SELECT * FROM table(function_name(report_freq));
where report_freq is value of frequency of subtotal reports.

Resources