How to use comma separated value from column to IN clause [duplicate] - oracle

This question already has answers here:
Using the "IN" clause with a comma delimited string from the output of a replace() function in Oracle SQL
(6 answers)
Closed 6 years ago.
I have a table, which contains below values(small snap of data):
Row#| Column A | Column B
--------------------------
1 |A | A,B
2 |C | A,D
3 |M | Z
4 |C |A,B,L
5 |F |F
6 |C |O
List goes on and on. Column A and B is populated by other application, so its combinations are not predictable. Table has more than 4000 rows.
My search values are not fixed; I have to use search value from Column B of same row for which I am looking for data in Column A.
I want to check Column A's value is IN Column B or not. Basically compare Column A with B, if find match in B result should skip that row.
In above case Row#1's Column A's value is in B which is not true for row#2.
My query should reach all rows and compare Column A with B. Query should return Row#1 and 5
This question is different Using the "IN" clause with a comma delimited string from the output of a replace() function in Oracle SQL
As it has fixed set of search value

select * from my_table where instr(columnB, columnA) > 0;
Example: Should contain
select * from (
select 1 a, 'A' b, 'A,B' c from dual union all
select 2, 'C', 'A,D' from dual ) t
where instr(c,b) > 0;
Result:
A B C
-----------
1 A A,B
Example 2: Shouldn't contain
select * from (
select 1 a, 'A' b, 'A,B' c from dual union all
select 2, 'C', 'A,D' from dual ) t
where instr(c,b) = 0;
Result:
A B C
-----------
2 C A,D

Related

Oracle: prioritizing results based on column’s value

I have a data-set in which there are duplicate IDs in the first column. I'm hoping to obtain a single row of data for each ID based on the second column's value. The data looks like so:
ID Info_Source Prior?
A 1 Y
A 3 N
A 2 Y
B 1 N
B 1 N
B 2 Y
C 2 N
C 3 Y
C 1 N
Specifically the criteria would call for prioritizing based on the second column's value (3 highest priority; then 1; and lastly 2): if the 'Info_Source' column has a value of 3, return that row; if there is no 3 in the second column for a given ID, look for a 1 and if found return that row; and finally if there is no 3 or 1 associated with the ID, search for 2 and return that row for the ID.
The desired results would be a single row for each ID, and the resulting data would be:
ID Info_Source Prior?
A 3 N
B 1 N
C 3 Y
row_number() over() usually solves these needs nicely and efficiently e.g.
select ID, Info_Source, Prior
from (
select ID, Info_Source, Prior
, row_number() over(partition by id order by Info_source DESC) as rn
)
where rn = 1
For prioritizing the second column's value (3 ; then 1, then 2) use a case expression to alter the raw value into an order that you need.
select ID, Info_Source, Prior
from (
select ID, Info_Source, Prior
, row_number() over(partition by id
order by case when Info_source = 3 then 3
when Infor_source = 1 then 2
else 1 end DESC) as rn
)
where rn = 1

How to match a value from a table upto a particular position in oracle?

I have to write a query to match values in two tables, Table A and Table B , Table A is havingvalues in column XYZ as "91517181","915171812", i want to check if its exist in table B or not , but in table B, the value in column ABC is "9151718", but in another column in table B it is having its match length as "10". Which means it is upto "9151718XXX".
So i have to write a query where value from table A should match with value in table B, because in table B, the value is upto 10 characters.
Kindly help...
I think that you need something like this:
table a: table b:
xyz x y
---------- ---------- ---
9151718 9151718 10
91517181 91360 5
913601
select a.xyz, rpad(xyz, b.y, 'x') result, b.x pattern, b.y len
from a
left join b on a.xyz like b.x||'%' and length(a.xyz)<=b.y
xyz result pattern len
---------- ---------- ---------- ---
9151718 9151718xxx 9151718 10
91517181 91517181xx 9151718 10
913601 <- not matched
I think something like that:
select * from a where
exists(select 'x' from b where substr(xyz, 1, y) = x)
x - value in b
y - length in b

Update with group by

I'm stumped on what seemed to be a simple UPDATE statement.
I'm looking for an UPDATE that uses two values. The first (a) is used to group, the second (b) is used to find a local minimum of values within the respective group. As a little extra there is a threshold value on b: Any value 1 or smaller shall remain as it is.
drop table t1;
create table t1 (a number, b number);
insert into t1 values (1,0);
insert into t1 values (1,1);
insert into t1 values (2,1);
insert into t1 values (2,2);
insert into t1 values (3,1);
insert into t1 values (3,2);
insert into t1 values (3,3);
insert into t1 values (4,1);
insert into t1 values (4,3);
insert into t1 values (4,4);
insert into t1 values (4,5);
-- 1,0 -> 1,0
-- 1,1 -> 1,1
-- 2,1 -> 2,1
-- 2,2 -> 2,2
-- 3,1 -> 3,1
-- 3,2 -> 3,2
-- 3,3 -> 3,2 <-
-- 4,1 -> 4,1
-- 4,3 -> 4,3 <-
-- 4,4 -> 4,3 <-
-- 4,5 -> 4,3 <-
Obviously not sufficient is:
update t1 x
set b = (select min(b) from t1 where b > 1)
;
Whatever more complicated stuff I try, e.g.
UPDATE t1 x
set (a,b) = (select distinct a,b from (
select a, min(b) from t1 where b > 1 group by a)
)
;
I get
SQL-Fehler: ORA-01427: Unterabfrage für eine Zeile liefert mehr als eine Zeile
01427. 00000 - "single-row subquery returns more than one row"
which is not overly surprising as I need a row for each value of a.
Of course I could write a PL/SQL Procedure with a cursor loop but is it possible in a single elegant SQL statement? Maybe using partition by?
Your question is a bit confusing.
You say that you would like to set value b to a minimum value from partition a that column b is in row with, while the rows containing b = 1 should remain untouched.
From what I can see in your question as comments (I assume it's your expected output) you also want to get the minimum value that follows 1 within a partition - so you basically want the minimum value of b that is greater than 1.
Below is SQL query that does this
UPDATE t1 alias
SET b = (
SELECT min(b)
FROM t1
WHERE alias.a = t1.a
AND t1.b > 1 -- this would get the minimum value higher than 1
GROUP BY a
)
WHERE alias.b > 1 -- update will not affect rows with b <= 1
Output after update
a | b
---+---
1 | 0
1 | 1
2 | 1
2 | 2
3 | 1
3 | 2
3 | 2
4 | 1
4 | 3
4 | 3
4 | 3

Select an included values

I'm using Oracle SQL and i need help with a query. Hope it's not too much easy one. I did't find an answer for it in Google.
I have a table that need to be aggregated by ID column and then to select only the records that two values are included in a certain table (and both of them).
Table for example
ID | Value
1 | Y
1 | N
2 | N
2 | N
2 | Y
3 | Y
3 | Y
4 | Y
5 | Y
5 | N
5 | Y
5 | N
The output table need to include only the IDs that both Y and N are included in Value table. Output:
ID
1
2
5
Another solution that groups by the ID and uses HAVING to return only those with > 1 DISTINCT values:
with v_data(id, value) as (
select 1, 'Y' from dual union all
select 1, 'N' from dual union all
select 2, 'Y' from dual)
select id
from v_data
group by id
having count(distinct value) > 1
select distinct a.id
from your_table a inner join your_table b on a.id = b.id and a.value != b.value;

select distinct column, priority second column value

I want to select distinct/group by column and show for the second column only one value(out of 3 possible) priority by: if A appears in the group show it, if not show B, if not show C.
This is the table:
A B
---- -----
FST A
FST B
FST C
INCS C
INCS B
ASW A
AWR C
WER C
WER C
WER B
RESULT
A B
---- -----
FST A
INCS B
ASW A
AWR C
WER B
For your given data, the easiest way is:
select A, min(B)
from table t
group by A;
For the more general problem where alphabetic ordering is not correct (if you wanted to return 'B' ahead of 'A', say), you can do this with case statements:
select A,
(case min(case B when 'A' then 1 when 'B' then 2 when 'C' then 3 else 4 end)
when 1 then 'A'
when 2 then 'B'
when 3 then 'C'
else '???'
end)
from table t
group by A;
EDIT:
Oracle has an easier way to do this than the nested selects:
select A,
max(B) keep (dense_rank first
order by (case B when 'A' then 1 when 'B' then 2 when 'C' then 3 else 4 end)
) as BestB
from table t
group by A;

Resources