How to join data from two views in oracle? - oracle

I have two views view1 and view2 I want to join the data from both views. The data example is the following : view1
old_NUmbers
counts
123
2
324
3
4454
13
343433
20
View2 data:
numbers
counts
343344
10
24344
15
So the desired result which I want is the following:
old_NUmbers
counts
numbers
counts
123
2
343344
10
324
3
24344
15
4454
13
343433
20

If you're combining the results and want to align data from the two views in counts order, you can generate a nominal ordinal value for each row in each view, for example with the row_number() function:
select v.old_numbers, v.counts,
row_number() over (order by v.counts, v.old_numbers)
from view1 v
and something similar for the other view; then use those as inline views or CTEs, and perform a full outer join based on that ordinal value:
with v1 (old_numbers, counts, rn) as (
select v.old_numbers, v.counts,
row_number() over (order by v.counts, v.old_numbers)
from view1 v
),
v2 (numbers, counts, rn) as (
select v.numbers, v.counts,
row_number() over (order by v.counts, v.numbers)
from view2 v
)
select v1.old_numbers, v1.counts, v2.numbers, v2.counts
from v1
full outer join v2 on v2.rn = v1.rn
order by coalesce(v1.rn, v2.rn)
OLD_NUMBERS
COUNTS
NUMBERS
COUNTS
123
2
343344
10
324
3
24344
15
4454
13
null
null
343433
20
null
null
db<>fiddle

Related

Filtering a dataset based on condition SQL Oracle

I have the following input and expected output I am looking for. Basically I would like to filter request_id only when the type = crossborder` and then show the units. I think I would need to use some Min and Max but I am not sure how to use it.
Input
request_id type unit_count
A11 local 10
A11 crossborder 5
B11 local 15
C11 crossborder 25
Output
request_id type unit_count
C11 crossborder 25
I think what you can use here in ranking. You can rank based on anything in the rows and order by the value to leave the highest value on top, that value will get the ranking # 1, all you have to do later is to filter for all the rankings #1.
This little demo will show what I am talking about:
with data as (
select 1 as id, 'cross' as type, 27 as unit from dual union
select 1 as id, 'cross' as type, 23 as unit from dual union
select 1 as id, 'cross' as type, 2 as unit from dual union
select 3 as id, 'cross' as type, 25 as unit from dual union
select 2 as id, 'cross' as type, 23 as unit from dual union
select 5 as id, 'cross' as type, 2 as unit from dual)
select id, type,unit, rank() over ( order by unit desc) ranking from data where type='cross'
Output:
As per my understanding of the question, You don't need MAX or MIN but simply an aggregation -
SELECT request_id, type, unit_count
FROM (SELECT request_id, type, unit_count,
COUNT(*) OVER (PARTITION BY request_id) CNT
FROM YOUR_TABLE)
WHERE CNT = 1
AND type = 'crossborder';

Cursor to Query statement

I am inserting into TABLE_A as given below.
INSERT
INTO Table_A (house_id,
house_key_nbr,
mnty_code,
split)
SELECT wcp.id,
ld.ld_ln_id,
ld.ld_mnty,
ROUND((ld.ld_ln_bal/wla.LOAN_AMT) * 100,2) split
FROM table_B ld,
table_C cc,
TABLE_D wcp,
TABLE_E wla
WHERE cc.conv_id = I_conv_id
AND cc.ev_id = wcp.ev_id
AND cc.client_plan_nbr = ld.plan_id
AND wcp.ssn = ld.ssn
AND wla.house_id = wcp.id
AND wla.house_key_nbr = ld.ld_ln_id
AND ld.status_code in ('V','W');
Once i have loaded into the table_A then i created a cursor to find out the records having the sum of split not equal to 100. For those cases I will find the diff and then update the record as given below.
CURSOR max_percent IS
SELECT house_id,
house_key_nbr,
sum(split) percent_sum
FROM TABLE_A s1,
TABLE_D p1,
table_C c1
WHERE s1.house_id = p1.id
AND p1.ev_id = c1.ev_id
AND c1.conv_id = I_conv_id
GROUP BY house_id, house_key_nbr
HAVING SUM(split) != 100;
OPEN max_percent;
l_debug_msg:='Cursor Opened';
FETCH max_percent BULK COLLECT INTO mnty_rec;
l_debug_msg:='Fetching the values from cursor';
FOR i IN 1..mnty_rec.COUNT
LOOP
v_diff := 100.00 - mnty_rec(i).percent_sum;
l_debug_msg:='The difference is '||v_diff||' for the house_id : '||mnty_rec(i).house_id;
UPDATE work_conv_part_loan_mnty_splt wcplms
SET split = split + v_diff
WHERE wcplms.house_id = mnty_rec(i).house_id
AND wcplms.house_key_nbr = mnty_rec(i).house_key_nbr
AND rownum = 1;
l_debug_msg:='Updated the percentage value for the house_id'||mnty_rec(i).house_id ;
END LOOP;
CLOSE max_percent;
The question here is, I achieved this simple process using a cursor. Is there any way I can achieve it during the insertion time itself instead of writing the cursor?
I'm simplifying a bit your setup with two tables: table_a accumulation the data and table_b containing new data.
-- TABLE_A: Primary Key HOUSE_ID, HOUSE_KEY_NBR
create table table_a as
select 1 house_id, 1 house_key_nbr, 90 split from dual union all
select 1 house_id, 2 house_key_nbr, 30 split from dual union all
select 1 house_id, 3 house_key_nbr, 100 split from dual;
-- TABLE_B: new data
create table table_b as
select 1 house_id, 1 house_key_nbr, 5 split from dual union all
select 1 house_id, 1 house_key_nbr, 5 split from dual union all
select 1 house_id, 4 house_key_nbr, 50 split from dual union all
select 1 house_id, 4 house_key_nbr, 40 split from dual union all
select 1 house_id, 5 house_key_nbr, 100 split from dual;
The important point is that the table_a has the primary key defined, so you need to update only one row for the correction of the SPLIT
The first step is simple to MERGE the new data
MERGE INTO table_a a
USING (select HOUSE_ID, HOUSE_KEY_NBR, sum(SPLIT) SPLIT
from table_b
group by HOUSE_ID, HOUSE_KEY_NBR) b
ON (a.HOUSE_ID = b.HOUSE_ID and a.HOUSE_KEY_NBR = b.HOUSE_KEY_NBR)
WHEN MATCHED THEN
update SET a.SPLIT = a.SPLIT + b.SPLIT
WHEN NOT MATCHED THEN
insert (HOUSE_ID, HOUSE_KEY_NBR, SPLIT)
values (b.HOUSE_ID, b.HOUSE_KEY_NBR, b.SPLIT)
So basically you first aggregates the new data to the level of the PK and than using the MERGE either insert or update the table_a
In the second step perform the correction using the same approach with MERGE only use a different source table containing only the defference of the SPLIT to 100.
MERGE INTO table_a a
USING (select HOUSE_ID, HOUSE_KEY_NBR, 100 - sum(SPLIT) SPLIT
from table_a
group by HOUSE_ID, HOUSE_KEY_NBR
having sum(SPLIT) != 100) b
ON (a.HOUSE_ID = b.HOUSE_ID and a.HOUSE_KEY_NBR = b.HOUSE_KEY_NBR)
WHEN MATCHED THEN
update SET a.SPLIT = a.SPLIT + b.SPLIT
WHEN NOT MATCHED THEN
insert (HOUSE_ID, HOUSE_KEY_NBR, SPLIT)
values (b.HOUSE_ID, b.HOUSE_KEY_NBR, b.SPLIT)
After this step all SPLIT are equal 100
select HOUSE_ID, HOUSE_KEY_NBR, sum(SPLIT)
from table_a
group by HOUSE_ID, HOUSE_KEY_NBR
order by 1,2;
HOUSE_ID HOUSE_KEY_NBR SUM(SPLIT)
---------- ------------- ----------
1 1 100
1 2 100
1 3 100
1 4 100
1 5 100
If you do not want to MERGE in table_a and you use INSERT only, I'd challange this desing, because it is not clear which of the many records with the same key you want to update.
I'll recomend not to UPDATE but to INSERT additional rows with the calculated differece SPLIT.
If mnty_code is unique for each house_id, house_key_nbr pair, then you can use window functions in your insert. Try using this for inserting into the split column:
CASE WHEN 1 = ROWNUMBER() OVER ( PARTITION BY wcp.id, ld.ld_ln_id ORDER BY mnty_code DESC ) THEN
-- This is the last row for the given house_id / house_key_nbr, so do special split calculation
100 - SUM(ROUND((ld.ld_ln_bal/wla.LOAN_AMT) * 100,2)) OVER ( PARTITION BY wcp.id, ld.ld_ln_id ORDER BY mnty_code ASC ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING ) ELSE
-- Normal split calculation
ROUND((ld.ld_ln_bal/wla.LOAN_AMT) * 100,2)
END split
The idea is that, if you are inserting the last row for a given house_id, house_key_nbr, then set the split value to 100 minus the sum of all the previous values.
If mnty_code is not unique within each house_id, house_key_nbr pair, it gets problematic, because there is no way to identify the "last" row in each group.

Order column inside PIVOT on the basis of max(count_date) for each value inside in condition

I have users name in column users, I want to display all users as a column and the order of representation of column must be in descending order of their sum of data.
query:
select *
from (
select sum(tran_count) over (partition by schema) as table_name
from main_table
) pivot (sum(tran_count) for users in ('abc','lmn','pqr'));
ans:
schema table abc lmn pqr
pm sector 32 216 12
history trn 321 61 4
tap issuer 43 325 2
count: 396 602 18
so I want to represent the column abc,lmn and pqr in order of count of their data:
required answer:
schema table lmn abc pqr
pm sector 216 32 12
history trn 61 321 4
tap issuer 325 43 2
You cannot use (sub)query in pivot's in clause. What you can do is to rank users according to their summaric values and use these three values (1, 2, 3) in in. Then either use my inner query, which presents user names and sums in separate columns or make a final union, where names are listed in first row and sums in rows below as strings.
with t as (
select *
from (
select dense_rank() over (order by smu desc, users) rn,
schema_, table_, users, smt
from (
select schema_, table_, users, sum(tran_count) smt,
sum(sum(tran_count)) over (partition by users) smu
from main_table
group by schema_, table_, users))
pivot (max(users) name, max(smt) smt for rn in (1 u1, 2 u2, 3 u3)))
select null schema_, null table_, u1_name u1, u2_name u2, u3_name u3
from t where rownum = 1 union all
select schema_, table_, to_char(u1_smt), to_char(u2_smt), to_char(u3_smt)
from t
dbfiddle demo
If you really need to put user names in headers then you have to use dynamic SQL or external code-writing-code technique.
I don't know if you really have columns like table or schema, these are reserved words, also once you write tran_count and in title count_date, so I am somewhat confused. But you can see in the linked dbfiddle working example with columns schema_, table_, users, tran_count.

How to group by month including all months?

I group my table by months
SELECT TO_CHAR (created, 'YYYY-MM') AS operation, COUNT (id)
FROM user_info
WHERE created IS NOT NULL
GROUP BY ROLLUP (TO_CHAR (created, 'YYYY-MM'))
2015-04 1
2015-06 10
2015-08 22
2015-09 8
2015-10 13
2015-12 5
2016-01 25
2016-02 37
2016-03 24
2016-04 1
2016-05 1
2016-06 2
2016-08 2
2016-09 7
2016-10 103
2016-11 5
2016-12 2
2017-04 14
2017-05 2
284
But the records don't cover all the months.
I would like the output to include all the months, with the missing ones displayed in the output with a default value:
2017-01 ...
2017-02 ...
2017-03 ZERO
2017-04 ZERO
2017-05 ...
Oracle has a good array of date manipulation functions. The two pertinent ones for this problem are
MONTHS_BETWEEN() which calculates the number of months between two dates
ADD_MONTHS() which increments a date by the given number of months
We can combine these functions to generate a table of all the months spanned by your table's records. Then we use an outer join to conditionally join records from USER_INFO to that calendar. When no records match count(id) will be zero.
with cte as (
select max(trunc(created, 'MM')) as max_dt
, min(trunc(created, 'MM')) as min_dt
from user_info
)
, cal as (
select add_months(min_dt, (level-1)) as mth
from cte
connect by level <= months_between(max_dt, min_dt) + 1
)
select to_char(cal.mth, 'YYYY-MM') as operation
, count(id)
from cal
left outer join user_info
on trunc(user_info.created, 'mm') = cal.mth
group by rollup (cal.mth)
order by 1
/

How to return non-empty rows for a given ID - Hive

I have a table X
ID A B
--------------
1 abc 27
1 - 28
2 - 33
3 xyz 41
3 - 07
I need output as
ID A B
--------------
1 abc 27
2 - 33
3 xyz 41
I tried doing
max(A) OVER (PARTITION BY ID) as the_value
but it did not work. I can still see all the rows in the output table.
I was wondering if somebody has come across a similar situation and has a solution to this ?
you can use this simple trick for getting the full record for which some column is maxed:
select original.* from
(select ID,max(B) as B from Tbl group by ID ) maxB
inner join
(select * from Tbl ) original
on original.ID = maxB.ID and original.B = maxB.B
now this is of course an overkill code. you can also do:
select Tbl.* from
(select ID,max(B) as B from Tbl group by ID ) maxB
inner join
Tbl
on Tbl.ID = maxB.ID and Tbl.B = maxB.B
but the first version is more of a template to do whatever you want with further columns, fields, conditions joins etc.

Resources