insert missing row in table oracle - sql

I have data in table like below:
Primary_id Serial_ID PRIMARY_ID SECONDARY_ID queue_ID
1 100 58 89 Q1428291
2 100 58 89 Q1428281
3 100 58 89 Q1428293
4 100 89 58 Q1428293
5 100 89 58 Q1428291
6 100 89 58 Q1428000281
7 200 16 28 Q1433144
8 200 16 28 Q1431953
9 200 16 28 Q1432397
10 200 16 28 Q1431921
11 200 28 16 Q1433144
12 200 28 16 Q1432397
13 200 28 16 Q1431921
We have primary_ID and Secondary_ID column. for serial_ID 100 we have 3 primary and 3 secondary records ..If you see the data Primary_ID become secondary and secondary become primary(3 each).but for serial_id 200 we have 4 primary records but 3 secondry records..
I want to insert missing record in the table.For ex serial_id 200 has missing data for primary ID 28 so insert this into table.Please assist

You can use something like this. In the temp_table, I put rows into two different types ('P' or 'S').
I assume that the queue_id = 'Q1428000281' (from row 6) is a mistake, and It should be 'Q1428281'.
drop table table_test ;
create table table_test (id, serial_id, primary_id, secondary_id, queue_id) as (
select 1 , 100, 58, 89, 'Q1428291' from dual union all
select 2 , 100, 58, 89, 'Q1428281' from dual union all
select 3 , 100, 58, 89, 'Q1428293' from dual union all
select 4 , 100, 89, 58, 'Q1428293' from dual union all
select 5 , 100, 89, 58, 'Q1428291' from dual union all
select 6 , 100, 89, 58, 'Q1428000281' from dual union all
select 7 , 200, 16, 28, 'Q1433144' from dual union all
select 8 , 200, 16, 28, 'Q1431953' from dual union all
select 9 , 200, 16, 28, 'Q1432397' from dual union all
select 10, 200, 16, 28, 'Q1431921' from dual union all
select 11, 200, 28, 16, 'Q1433144' from dual union all
select 12, 200, 28, 16, 'Q1432397' from dual union all
select 13, 200, 28, 16, 'Q1431921' from dual
)
;
insert into table_test (id, serial_id, primary_id, secondary_id, queue_id)
with temp_table as (
select t.*
, case when primary_id < secondary_id then 'P' else 'S' end type_t
from table_test t
)
select v_max.id_max + rownum ID, SERIAL_ID, PRIMARY_ID, SECONDARY_ID, queue_id
from (
select SERIAL_ID
, max(SECONDARY_ID) PRIMARY_ID
, min(PRIMARY_ID) SECONDARY_ID
, max(queue_id)queue_id
, count(TYPE_T) cnt
from temp_table tp
group by SERIAL_ID
, least(PRIMARY_ID, SECONDARY_ID)
, greatest(PRIMARY_ID, SECONDARY_ID)
, QUEUE_ID
having count(TYPE_T) != 2
)t
cross join (select max(id) id_max from table_test) v_max
;

Hmmm . . . You can generate the missing rows using cross join and then something like left join or not exists:
select sp.serial_id, sp.primary_id, sp.secondary_id
from (select d.serial_id, d.primary_id
from data d
) sp join
(select d.serial_id, d.secondary_id
from data d
) ss
on sp.serial_id = ss.serial_id left join
data d
on d.serial_id = sp.serial_id and
d.primary_id = sp.primary_id and
d.secondary_id = sp.secondary_id
where d.serial_id is null;
You can then insert the results of this query into your table.
Your question doesn't specify what value of queue_id should be used. NULL seems like a very reasonable value under these circumstances.

Related

Recursive query - Oracle

I have this data and expected result:
Data Expected Result
No A B A B
1 10 500 10 500
2 10 c=20 20 400
3 20 400 30 600
4 30 600 30 700
5 30 c=40 30 800
6 30 c=50 40 700
7 40 700 50 900
8 50 c=60 60 900
9 60 c=70 70 900
10 70 900 10 400
I need to perform a self join and get the result.
For line number 1 the expected result is same as the row.
For line number 2, I need to take the substring of column B (c=20) as 20 and join with column B and get the result as 400.
Lines number 5 and 6 need to substring column B and get the result from column A.
I tried a recursive query, but still am not getting the expected result.
with rec(A, B, nested) as
(
select A, B, case when instr(B, 'C=') != 0 then substr(B, instr(B, 'C=')) as nested
from table
union all
select A, rec.B from table
inner join rec
on (table.A = rec.nested)
)
select A, B, nested from rec;
Answer for the initial version of the question
You do not need a recursive query. To get your desired output you just need to exclude the rows where B starts with c=:
SELECT a, b
FROM table_name
WHERE b NOT LIKE 'c=%';
Which, for the sample data:
CREATE TABLE table_name (no, a, b) AS
SELECT 1, 10, '500' FROM DUAL UNION ALL
SELECT 2, 10, 'c=20' FROM DUAL UNION ALL
SELECT 3, 20, '400' FROM DUAL UNION ALL
SELECT 4, 30, '600' FROM DUAL UNION ALL
SELECT 5, 30, 'c=40' FROM DUAL UNION ALL
SELECT 6, 30, 'c=50' FROM DUAL UNION ALL
SELECT 7, 40, '700' FROM DUAL UNION ALL
SELECT 8, 50, '800' FROM DUAL;
Outputs your desired output:
A
B
10
500
20
400
30
600
40
700
50
800
fiddle
Answer for the 3rd edit of the question
You can use a hierarchical query:
SELECT DISTINCT
CONNECT_BY_ROOT a AS a,
b
FROM table_name
WHERE CONNECT_BY_ISLEAF = 1
CONNECT BY
PRIOR b LIKE 'c=%'
AND PRIOR SUBSTR(b, 3) = a
ORDER BY a, b;
Which, for the sample data:
CREATE TABLE table_name (no, a, b) AS
SELECT 1, 10, '500' FROM DUAL UNION ALL
SELECT 2, 10, 'c=20' FROM DUAL UNION ALL
SELECT 3, 20, '400' FROM DUAL UNION ALL
SELECT 4, 30, '600' FROM DUAL UNION ALL
SELECT 5, 30, 'c=40' FROM DUAL UNION ALL
SELECT 6, 30, 'c=50' FROM DUAL UNION ALL
SELECT 7, 40, '700' FROM DUAL UNION ALL
SELECT 8, 50, 'c=60' FROM DUAL UNION ALL
SELECT 9, 60, 'c=70' FROM DUAL UNION ALL
SELECT 10, 70, '900' FROM DUAL;
Outputs:
A
B
10
400
10
500
20
400
30
600
30
700
30
900
40
700
50
900
60
900
70
900
fiddle

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

Compare Difference of Successive Rows Within The Same Table

I need help in comparing data in sql query.
I need to compare within the plan_group_id, what measure_id details having different min_target and max_target compared to the other plan_id
Example:
I mean, I needed to compare what plan measure under the same plan_grp_id have different values in min and max.
what if the values are:
another example:
Group by all three columns
select m.plan_id, m.plan_grp_id, m.measure_id
from plan_measure m
group by m.plan_id, m.plan_grp_id, m.measure_id
having min(m.min_target) <> max(m.min_target) or min(m.max_target) <> max(m.max_target);
Looks like
SQL> with plan_measure (measure_id, min_target, max_target) as
2 (select 111, 10, 10 from dual union all
3 select 222, 20, 20 from dual union all
4 select 333, 30, 30 from dual union all
5 select 111, 33, 55 from dual union all
6 select 222, 20, 20 from dual union all
7 select 333, 30, 30 from dual union all
8 select 111, 10, 10 from dual union all
9 select 222, 20, 20 from dual union all
10 select 111, 10, 10 from dual
11 )
12 select measure_id
13 from plan_measure
14 group by measure_id
15 having min(min_target) <> max(max_target);
MEASURE_ID
----------
111
SQL>
I have no idea what is plan table supposed to do here, though. It isn't related to plan_measure in any obvious way (not to me, at least).

Form and count the most frequented pair sql oracle

I am creating a database for the video replay site and I have a table with users and table with viewing history. I need to find using SQL query several the most watched pairs of videos. Exemple: user 1 watched videos 12, 43, 50, 66, 78; user 2 watched 12, 43, 45, 50; user 3 watched 12, 35, 50, 66, 78; user 4 watched 33, 66, 69, 78
So the two most viewed couples are (12,50) and (66,78).
But I can't even get how to form this couples for the future counting.
So, my question is how to form all possible couples and count the quantity of views of each of them.
A self join is the right way to do this. I think the simplest form of the query is:
select vh.*
from (select vh1.movie as movie1, vh2.movie as movie2, count(*) as cnt,
rank() over (order by count(*) desc) as seqnum
from viewing_history vh1 inner join
viewing_history vh2
on vh1.userid = vh2.userid and vh1.movie < vh2.movie
group by vh1.movie, vh2.movie
) vh
where seqnum = 1;
In the solution below, I create a subquery to simulate input data. In your application, instead of viewing_history you should use your viewing history table. I don't see how the "users" table is relevant in this problem. The second subquery, which I named movie_pairs, is an inner join of the viewing history with itself - that's how you create the pairs. I went beyond that - with that in hand, I went on to identify the pairs that are viewed together most often.
with
viewing_history ( userid, movie ) as (
select 1, 12 from dual union all
select 1, 43 from dual union all
select 1, 50 from dual union all
select 1, 66 from dual union all
select 1, 78 from dual union all
select 2, 12 from dual union all
select 2, 43 from dual union all
select 2, 45 from dual union all
select 2, 50 from dual union all
select 3, 12 from dual union all
select 3, 35 from dual union all
select 3, 50 from dual union all
select 3, 66 from dual union all
select 3, 78 from dual union all
select 4, 33 from dual union all
select 4, 66 from dual union all
select 4, 69 from dual union all
select 4, 78 from dual
),
-- end test data, query begins here (but include the keyword WITH from above)
movie_pairs ( movie1, movie2, ct ) as (
select a.movie, b.movie, count(*)
from viewing_history a inner join viewing_history b
on a.userid = b.userid and a.movie < b.movie
group by a.movie, b.movie
)
select movie1, movie2
from movie_pairs
where ct = (select max(ct) from movie_pairs)
order by movie1, movie2 -- ORDER BY is optional
;
Output:
MOVIE1 MOVIE2
---------- ----------
12 50
66 78

Distinct LISTAGG that is inside a subquery in the SELECT list

Here is a minimal working example of what I'm trying to do and what I'm getting:
I have a query as follows:
/*
with tran_party as -- ALL DUMMY DATA ARE IN THESE CTE FOR YOUR REFERENCE
(select 1 tran_party_id, 11 transaction_id, 101 team_id_redirect
from dual
union all
select 2, 11, 101 from dual
union all
select 3, 11, 102 from dual
union all
select 4, 12, 103 from dual
union all
select 5, 12, 103 from dual
union all
select 6, 12, 104 from dual
union all
select 7, 13, 104 from dual
union all
select 8, 13, 105 from dual),
tran as
(select 11 transaction_id, 1001 account_id, 1034.93 amount from dual
union all
select 12, 1001, 2321.89 from dual
union all
select 13, 1002, 3201.47 from dual),
account as
(select 1001 account_id, 111 team_id from dual
union all
select 1002, 112 from dual),
team as
(select 101 team_id, 'UUU' as team_code from dual
union all
select 102, 'VV' from dual
union all
select 103, 'WWW' from dual
union all
select 104, 'XXXXX' from dual
union all
select 105, 'Z' from dual)
-- */
-- The Actual Query
select a.account_id,
t.transaction_id,
(select listagg (tm_redir.team_code, ', ')
within group (order by tm_redir.team_code)
from tran_party tp_redir
inner join team tm_redir
on tp_redir.team_id_redirect = tm_redir.team_id
inner join tran t_redir
on tp_redir.transaction_id = t_redir.transaction_id
where t_redir.account_id = a.account_id
and t_redir.transaction_id != t.transaction_id)
as teams_redirected
from tran t inner join account a on t.account_id = a.account_id;
NOTE: tran_party.team_id_redirect is a foreign key that references team.team_id.
Current output:
ACCOUNT_ID TRANSACTION_ID TEAMS_REDIRECTED
---------- -------------- ----------------
1001 11 WWW, WWW, XXXXX
1001 12 UUU, UUU, VV
1002 13
Expected output:
I want the repeated items in TEAMS_REDIRECTED column to be selected only once, like this:
ACCOUNT_ID TRANSACTION_ID TEAMS_REDIRECTED
---------- -------------- ----------------
1001 11 WWW, XXXXX
1001 12 UUU, VV
1002 13
What I tried:
Instead of selecting from tran_party directly, I wrote an inline view that selects distinct values from tran_party like this:
select a.account_id,
t.transaction_id,
(select listagg (tm_redir.team_code, ', ')
within group (order by tm_redir.team_code)
from (select distinct transaction_id, team_id_redirect -- Note this inline view
from tran_party) tp_redir
inner join team tm_redir
on tp_redir.team_id_redirect = tm_redir.team_id
inner join tran t_redir
on tp_redir.transaction_id = t_redir.transaction_id
where t_redir.account_id = a.account_id
and t_redir.transaction_id != t.transaction_id)
as teams_redirected
from tran t inner join account a on t.account_id = a.account_id;
While this does give me the expected output, when I use this solution in my actual code, it takes about 13 seconds to retrieve just one row. Thus I cannot use what I already tried.
Any help will be appreciated.
The following method gets rid of the in-line view to fetch duplicates, it uses REGEXP_REPLACE and RTRIM on the LISTAGG function to get the distinct result set in the aggregated list. Thus, it won't do more than one scan.
Adding this piece to your code,
RTRIM(REGEXP_REPLACE(listagg (tm_redir.team_code, ',')
WITHIN GROUP (ORDER BY tm_redir.team_code),
'([^,]+)(,\1)+', '\1'),
',')
Modified query-
SQL> with tran_party as -- ALL DUMMY DATA ARE IN THESE CTE FOR YOUR REFERENCE
2 (select 1 tran_party_id, 11 transaction_id, 101 team_id_redirect
3 from dual
4 union all
5 select 2, 11, 101 from dual
6 union all
7 select 3, 11, 102 from dual
8 union all
9 select 4, 12, 103 from dual
10 union all
11 select 5, 12, 103 from dual
12 union all
13 select 6, 12, 104 from dual
14 union all
15 select 7, 13, 104 from dual
16 union all
17 select 8, 13, 105 from dual),
18 tran as
19 (select 11 transaction_id, 1001 account_id, 1034.93 amount from dual
20 union all
21 select 12, 1001, 2321.89 from dual
22 union all
23 select 13, 1002, 3201.47 from dual),
24 account as
25 (select 1001 account_id, 111 team_id from dual
26 union all
27 select 1002, 112 from dual),
28 team as
29 (select 101 team_id, 'UUU' as team_code from dual
30 union all
31 select 102, 'VV' from dual
32 union all
33 select 103, 'WWW' from dual
34 union all
35 select 104, 'XXXXX' from dual
36 union all
37 select 105, 'Z' from dual)
38 -- The Actual Query
39 select a.account_id,
40 t.transaction_id,
41 (SELECT RTRIM(
42 REGEXP_REPLACE(listagg (tm_redir.team_code, ',')
43 WITHIN GROUP (ORDER BY tm_redir.team_code),
44 '([^,]+)(,\1)+', '\1'),
45 ',')
46 from tran_party tp_redir
47 inner join team tm_redir
48 on tp_redir.team_id_redirect = tm_redir.team_id
49 inner join tran t_redir
50 on tp_redir.transaction_id = t_redir.transaction_id
51 where t_redir.account_id = a.account_id
52 and t_redir.transaction_id != t.transaction_id)
53 AS teams_redirected
54 from tran t inner join account a on t.account_id = a.account_id
55 /
ACCOUNT_ID TRANSACTION_ID TEAMS_REDIRECTED
---------- -------------- --------------------
1001 11 WWW,XXXXX
1001 12 UUU,VV
1002 13
SQL>