Joining on a match with at least one argument - sql

I have a two tables:
T1(RES1, RES2)
T2(RES, WORD, COUNT)
I need to generate T3:
T3(RES1, RES2, WORD, COUNT)
As in the following example
T1
RES1 RES2
------------
A B
C D
T2
RES WORD COUNT
----------------------
A W1 10
B W1 5
B W2 7
C W2 8
T3
RES1 RES2 WORD COUNT
-----------------------------
A B W1 15 = (10+5)
A B W2 7 = (NOTHING FOR A+7)
C D W2 8 = (8+NOTHING FOR D)
That is, for every pair in T1, generate the sum distinct word counts that occur with it. What would be the most efficient way to do this in SQL?

-- sample of data from your question
SQL> with t1(RES1, RES2) as(
2 select 'A', 'B' from dual union all
3 select 'C', 'D' from dual
4 ),
5 t2(RES , WORD, COUNT1) as(
6 select 'A', 'W1', 10 from dual union all
7 select 'B', 'W1', 5 from dual union all
8 select 'B', 'W2', 7 from dual union all
9 select 'C', 'W2', 8 from dual
10 ) -- the query
11 select t1.res1
12 , t1.res2
13 , t2.word
14 , sum(t2.count1) as count
15 from t1
16 join t2
17 on (t1.res1 = t2.res or
18 t1.res2 = t2.res)
19 group by t1.res1
20 , t1.res2
21 , t2.word
22 order by t1.res1
23 ;
RES1 RES2 WORD COUNT
---- ---- ---- ----------
A B W1 15
A B W2 7
C D W2 8

Related

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

Oracle PL/SQL SUM OVER( ) starting from certain row

I have a table who looks like this:
Pam_A Week Value_1
A 1 10
A 2 13
B 3 15
B 4 10
B 5 11
B 6 10
I want to achieve the following:
Pam_A Week Value_1 Value_2
A 1 10
A 2 13
B 3 15 28
B 4 10 38
B 5 11 49
B 6 10 59
When Pam_A=B, sum the current Value_1 and its preceding row value and keep that value increasing accordding the next value in Value_1
Any ideas for achieve this cumulative sum?
First of all you need to mark all rows that you want to count. You can do it like this:
with t(Pam_A, Week, Value_1) as (
select 'A', 1, 10 from dual union all
select 'A', 2, 13 from dual union all
select 'B', 3, 15 from dual union all
select 'B', 4, 10 from dual union all
select 'B', 5, 11 from dual union all
select 'B', 6, 10 from dual
)
select
Pam_A, Week, Value_1
,case
when Pam_A='B' or lead(Pam_A)over(order by week) = 'B'
then 'Y'
else 'N'
end as flag
from t;
Results:
PAM_A WEEK VALUE_1 FLAG
----- ---------- ---------- ----
A 1 10 N
A 2 13 Y
B 3 15 Y
B 4 10 Y
B 5 11 Y
B 6 10 Y
6 rows selected.
Then you can aggregate only rows that have flag='Y':
with t(Pam_A, Week, Value_1) as (
select 'A', 1, 10 from dual union all
select 'A', 2, 13 from dual union all
select 'B', 3, 15 from dual union all
select 'B', 4, 10 from dual union all
select 'B', 5, 11 from dual union all
select 'B', 6, 10 from dual
)
select
v.*
,case
when flag='Y' and Pam_a='B'
then sum(Value_1)over(partition by flag order by Week)
end as sums
from (
select
Pam_A, Week, Value_1
,case
when Pam_A='B' or lead(Pam_A)over(order by week) = 'B'
then 'Y'
else 'N'
end as flag
from t
) v;
Results:
PAM_A WEEK VALUE_1 FLAG SUMS
----- ---------- ---------- ---- ----------
A 1 10 N
A 2 13 Y
B 3 15 Y 28
B 4 10 Y 38
B 5 11 Y 49
B 6 10 Y 59
6 rows selected.
Using a combination of LEAD and SUM analytic functions, you can determine which rows have the next PAM_A as a B, then only SUM if the next row is a B or the current row is a B.
Query
WITH
d (pam_a, week, value_1)
AS
(SELECT 'A', 1, 10 FROM DUAL
UNION ALL
SELECT 'A', 2, 13 FROM DUAL
UNION ALL
SELECT 'B', 3, 15 FROM DUAL
UNION ALL
SELECT 'B', 4, 10 FROM DUAL
UNION ALL
SELECT 'B', 5, 11 FROM DUAL
UNION ALL
SELECT 'B', 6, 10 FROM DUAL)
SELECT pam_a,
week,
value_1,
CASE
WHEN pam_a = 'B'
THEN
SUM (CASE WHEN next_pam_a = 'B' OR pam_a = 'B' THEN value_1 ELSE 0 END)
OVER (ORDER BY week)
ELSE
NULL
END value_2
FROM (SELECT pam_a, week, value_1, LEAD (pam_a) OVER (ORDER BY week) AS next_pam_a FROM d);
Result
PAM_A WEEK VALUE_1 VALUE_2
________ _______ __________ __________
A 1 10
A 2 13
B 3 15 28
B 4 10 38
B 5 11 49
B 6 10 59
If I understand, you want a cumulative sum but with conditionality:
select t.*,
(case when pam_A = 'B' then sum(value_1) over (order by week) end) as value_2
from t;

Oracle SQL intersection of 2 comma separated string

What can I apply as function?
Query:
Select x, f(y) from table where y like '%ab%cd%ef';
sample table(y is sorted alphabatically)
x. y
1 ab
2 ab,cd
3 cd,ef
4 ab,ef,gh,yu
5 de,ef,rt
Expected Output:
Output:
x y
1 ab
2 ab,cd
3 cd,ef
4 ab,ef
5 ef
Use regexp_substr function with connect by level expressions as
with tab(x,y) as
(
select 1,'ab' from dual union all
select 2,'ab,cd' from dual union all
select 3,'cd,ef' from dual union all
select 4,'ab,ef,gh,yu' from dual union all
select 5,'de,ef,rt' from dual
), tab2 as
(
Select x, regexp_substr(y,'[^,]+',1,level) as y
from tab
connect by level <= regexp_count(y,',') + 1
and prior x = x
and prior sys_guid() is not null
), tab3 as
(
select x, y
from tab2
where y like '%ab%'
or y like '%cd%'
or y like '%ef%'
)
select x, listagg(y,',') within group (order by y) as y
from tab3
group by x;
X Y
1 ab
2 ab,cd
3 cd,ef
4 ab,ef
5 ef
Demo
Follow comments written within the code.
SQL> with test (x, y) as
2 -- your sample table
3 (select 1, 'ab' from dual union all
4 select 2, 'ab,cd' from dual union all
5 select 3, 'cd,ef' from dual union all
6 select 4, 'ab,ef,gh,yu' from dual union all
7 select 5, 'de,ef,rt' from dual
8 ),
9 srch (val) as
10 -- a search string, which is to be compared to the sample table's Y column values
11 (select 'ab,cd,ef' from dual),
12 --
13 srch_rows as
14 -- split search string into rows
15 (select regexp_substr(val, '[^,]+', 1, level) val
16 from srch
17 connect by level <= regexp_count(val, ',') + 1
18 ),
19 test_rows as
20 -- split sample values into rows
21 (select x,
22 regexp_substr(y, '[^,]+', 1, column_value) y
23 from test,
24 table(cast(multiset(select level from dual
25 connect by level <= regexp_count(y, ',') + 1
26 ) as sys.odcinumberlist))
27 )
28 -- the final result
29 select t.x, listagg(t.y, ',') within group (order by t.y) result
30 from test_rows t join srch_rows s on s.val = t.y
31 group by t.x
32 order by t.x;
X RESULT
---------- --------------------
1 ab
2 ab,cd
3 cd,ef
4 ab,ef
5 ef
SQL>

update value in table oracle depand on oldest date

I have two tables (T1,T2) connected to each other by foreign key columns (B,C).
T1
C B A
11 1 123
12 2 123
13 3 123
14 4 222
15 5 222
16 6 333
T2
A2 B2 C2 D
1 11 25/4/1972
2 12 2/11/1982
3 13 4/6/2000
4 14 2/7/1992
5 15 14/2/2010
6 16 6/3/1999
I need to update A2 value(T2) from A value(T1) according to oldest date in column D (T2) which gives the following result:
T2
A2 B2 C2 D
123 1 11 25/4/1972
2 12 2/11/1982
3 13 4/6/2000
222 4 14 2/7/1992
5 15 14/2/2010
333 6 16 6/3/1999
show resulet
This is far from a smart & nice solution, but - might be OK until someone posts something better.
Test case for the rest of you (saving you some time, as Omar chose not to):
create table t1 (c number, b number, a number);
create table t2 (a2 number, b2 number, c2 number, d date);
insert into t1
select 11, 1, 123 from dual union
select 12, 2, 123 from dual union
select 13, 3, 123 from dual union
select 14, 4, 222 from dual union
select 15, 5, 222 from dual union
select 16, 6, 333 from dual;
insert into t2 (b2, c2, d)
select 1, 11, date '1972-04-25' from dual union
select 2, 12, date '1982-11-02' from dual union
select 3, 13, date '2000-06-04' from dual union
select 4, 14, date '1992-07-02' from dual union
select 5, 15, date '2010-02-14' from dual union
select 6, 16, date '1999-03-06' from dual;
First update every row, then remove unnecessary values:
SQL> update t2 set
2 t2.a2 = (select t1.a
3 from t1
4 where t1.b = t2.b2
5 and t1.c = t2.c2
6 );
6 rows updated.
SQL> update t2 set
2 t2.a2 = (select distinct
3 case when min(x.d) over (partition by x.a2) = t2.d then t2.a2
4 else null
5 end
6 from t2 x
7 where x.a2 = t2.a2
8 );
6 rows updated.
SQL> select * from t2;
A2 B2 C2 D
---------- ---------- ---------- ----------
123 1 11 25.04.1972
2 12 02.11.1982
3 13 04.06.2000
222 4 14 02.07.1992
5 15 14.02.2010
333 6 16 06.03.1999
6 rows selected.
SQL>
Assuming that the dates are unique (as in your example), you can do:
update t2
set t2.a2 = (select t1.a from t1 where t1.b = t2.b2)
where t2.d = (select min(tt2.d)
from t1 join
t2 tt2
on tt2.b2 = t1.b
group by t1.a
);
If you do have duplicates, you can change the where to:
where (t2.b2, t2.d) = (select min(tt2.b2) keep (dense_rank first over order by tt2.d), min(tt2.d)
from t1 join
t2 tt2
on tt2.b2 = t1.b
group by t1.a
)

SQL pad query result for missing groups

Assume the following table:
TableA:
ID GroupName SomeValue
1 C 1
2 C 1
2 B 1
2 A 1
I need to construct a query that selects the following result:
ID GroupName SomeValue
1 C 1
1 B 0
1 A 0
2 C 1
2 B 1
2 A 1
The GroupName is actually derived from TableA column's CASE expression and can take only 3 values: A, B, C.
Are the analytic functions the way to go?
EDIT
Sorry, for not mentioning it, but the ID could consist of multiple columns. Consider this example:
ID1 ID2 GroupName SomeValue
1 1 C 1
1 2 C 1
2 2 C 1
2 2 B 1
2 2 A 1
I need to pad SomeValue with 0 for each unique combination ID1+ID2. So the result should be like this:
ID1 ID2 GroupName SomeValue
1 1 C 1
1 1 B 0
1 1 A 0
1 2 C 1
1 2 B 0
1 2 A 0
2 2 C 1
2 2 B 1
2 2 A 1
EDIT2
Seems like solution, proposed by #Laurence should work even for multiple-column 'ID'. I couldn't rewrite the query proposed by #Nicholas Krasnov to conform to this requirement. But could somebody compare these solutions performance-wise? Will the analytic function work faster than 'cross join + left outer join'?
To fill in gaps, you could write a similar query using partition by clause of outer join:
SQL> with t1(ID,GroupName,SomeValue) as
2 (
3 select 1, 'C', 1 from dual union all
4 select 2, 'C', 1 from dual union all
5 select 2, 'B', 1 from dual union all
6 select 2, 'A', 1 from dual
7 ),
8 groups(group_name) as(
9 select 'A' from dual union all
10 select 'B' from dual union all
11 select 'C' from dual
12 )
13 select t1.ID
14 , g.group_name
15 , nvl(SomeValue, 0) SomeValue
16 from t1
17 partition by (t1.Id)
18 right outer join groups g
19 on (t1.GroupName = g.group_name)
20 order by t1.ID asc, g.group_name desc
21 ;
ID GROUP_NAME SOMEVALUE
---------- ---------- ----------
1 C 1
1 B 0
1 A 0
2 C 1
2 B 1
2 A 1
6 rows selected
UPDATE: Response to the comment.
Specify ID2 column in the partition by clause as well:
SQL> with t1(ID1, ID2, GroupName,SomeValue) as
2 (
3 select 1, 1, 'C', 1 from dual union all
4 select 1, 2, 'C', 1 from dual union all
5 select 2, 2, 'C', 1 from dual union all
6 select 2, 2, 'B', 1 from dual union all
7 select 2, 2, 'A', 1 from dual
8 ),
9 groups(group_name) as(
10 select 'A' from dual union all
11 select 'B' from dual union all
12 select 'C' from dual
13 )
14 select t1.ID1
15 , t1.ID2
16 , g.group_name
17 , nvl(SomeValue, 0) SomeValue
18 from t1
19 partition by (t1.Id1, t1.Id2)
20 right outer join groups g
21 on (t1.GroupName = g.group_name)
22 order by t1.ID1, t1.ID2 asc , g.group_name desc
23 ;
ID1 ID2 GROUP_NAME SOMEVALUE
---------- ---------- ---------- ----------
1 1 C 1
1 1 B 0
1 1 A 0
1 2 C 1
1 2 B 0
1 2 A 0
2 2 C 1
2 2 B 1
2 2 A 1
9 rows selected
Select
i.Id1,
i.Id2,
g.GroupName,
Coalesce(a.SomeValue, 0) As SomeValue
From
(select distinct ID1, ID2 from TableA) as i
cross join
(select distinct GroupName from TableA) as g
left outer join
tableA a
on i.ID = a.ID and g.GroupName = a.GroupName
Order By
1,
2,
3 Desc