SQL query to to check uniqueness of a column value - sql

Need a query to check(Select) if every combination of Value in colA and B has a unique value in col C. Please help. Thank you
This is the query I tried which doesn't give me the desired result:
select unique CONCAT(A,B),C
from tab1
group by CONCAT(A,B),C
having count(distinct (CONCAT(A,B)))>1;
Sample table:
A B C
Tim 123 1
Jill 123 1
Jill 456 2
John 456 1
Jill 456 1
Here row 3 and 5 with same values in col A and B have different values in col C which is incorrect. I need to select those

Something like this? (Sample data in lines #1 - 7; query begins at line #8):
SQL> with test (a, b, c) as
2 (select 'Tim' , 123, 1 from dual union all
3 select 'Jill', 123, 1 from dual union all
4 select 'Jill', 456, 2 from dual union all
5 select 'John', 456, 1 from dual union all
6 select 'Jill', 456, 1 from dual
7 )
8 select t.*
9 from test t
10 where (t.a, t.b) in (select x.a, x.b
11 from test x
12 group by x.a, x.b
13 having count(distinct x.c) > 1
14 );
A B C
---- ---------- ----------
Jill 456 1
Jill 456 2
SQL>

Related

select only those users whose contacts length is not 5

I have table like this:
id
name
contact
1
A
65489
1
A
1
A
45564
2
B
3
C
12345
3
C
1234
4
D
32
4
D
324
I only want users who have no contact or the contact length is not five.
If the user has two or more contacts and the length of one of them is five and the rest is not, then such users should not be included in the table.
so,If the customer has at least one contact length of five, I do not want that.
so, i want table like this:
id
name
contact
2
B
4
D
32
4
D
324
Can you halp me?
You could actually do a range check here:
SELECT id, name, contact
FROM yourTable t1
WHERE NOT EXISTS (
SELECT 1
FROM yourTable t2
WHERE t2.id = t1.id AND TO_NUMBER(t2.contact) BETWEEN 10000 AND 99999
);
Note that if contact already be a numeric column, then just remove the calls to TO_NUMBER above and compare directly.
Yet another option:
SQL> with test (id, name, contact) as
2 (select 1, 'a', 65879 from dual union all
3 select 1, 'a', null from dual union all
4 select 1, 'a', 45564 from dual union all
5 select 2, 'b', null from dual union all
6 select 3, 'c', 12345 from dual union all
7 select 3, 'c', 1234 from dual union all
8 select 4, 'd', 32 from dual union all
9 select 4, 'd', 324 from dual
10 )
11 select *
12 from test a
13 where exists (select null
14 from test b
15 where b.id = a.id
16 group by b.id
17 having nvl(max(length(b.contact)), 0) < 5
18 );
ID N CONTACT
---------- - ----------
2 b
4 d 32
4 d 324
SQL>
COUNT analytic function can also be used to get the job done.
select id, name, contact
from (
select id, name, contact
, count( decode( length(contact), 5, 1, null ) ) over( partition by id, name ) cnt
from YourTable
)
where cnt = 0
demo

How to get the entire partition based on two conditions are met in SQL

TelNO
Type
rank
date
76567
a
1
20210915
76567
b
2
20210611
76567
a
3
20210810
56597
b
1
20210818
56597
a
2
20210916
97658
b
1
20210610
97658
a
2
20210811
97658
b
3
20210915
76567
a
1
20210210
76567
a
2
20210619
I want to return the entire block (which is grouped by TelNO) if the Type= a when the
rank=1 . Expected output is as follows.
TelNO
Type
rank
date
76567
a
1
20210915
76567
b
2
20210611
76567
a
3
20210810
76567
a
1
20210210
76567
a
2
20210619
I am trying the following code. But it gives only the record which satisfices the condition. I need the entire partition to appear. Since there's no aggregation function to perform I am struggling how the partition function can use to get relevant output
select *
from table
where Type=a and rank=1
group by TelNo
This is the way I understood the question:
SQL> with test (telno, type, rank) as
2 (select 76567, 'a', 1 from dual union all
3 select 76567, 'b', 2 from dual union all
4 select 76567, 'c', 3 from dual union all
5 --
6 select 56597, 'b', 1 from dual union all
7 select 56597, 'a', 2 from dual union all
8 --
9 select 97658, 'b', 1 from dual union all
10 select 97658, 'a', 2 from dual union all
11 select 97658, 'b', 3 from dual union all
12 --
13 select 76567, 'a', 1 from dual union all
14 select 76567, 'a', 2 from dual
15 )
16 select *
17 from test
18 where telno in (select telno from test
19 where type = 'a'
20 and rank = 1
21 );
TELNO T RANK
---------- - ----------
76567 a 2
76567 a 1
76567 c 3
76567 b 2
76567 a 1
SQL>

Query to delete duplicate records by keeping original in oracle

This is the table.
Id. Name
1 A
1 A
2 B
2 C
1 A
2 B
2 D
The output should be
Id. Name
1 A
2 B
2 C
2 D
please try
Select distinct id, name
from <name of you table>
order by name
Check this link.
Sample data:
create table demo (id, name) as
select 1, 'A' from dual union all
select 1, 'A' from dual union all
select 2, 'B' from dual union all
select 2, 'C' from dual union all
select 1, 'A' from dual union all
select 2, 'B' from dual union all
select 2, 'D' from dual;
select * from demo order by 1,2;
ID NAME
---------- ------------------------------
1 A
1 A
1 A
2 B
2 B
2 C
2 D
7 rows selected
Delete all but the first row in each (id, name) group:
delete demo where rowid in
( select lag(rowid) over (partition by id, name order by null) from demo );
3 rows deleted
select * from demo order by 1,2
ID N
---------- -
1 A
2 B
2 C
2 D
4 rows selected.

How to find partial duplicate values in Oracle

I have table in which one of the columns is having 1000s of records out which most of them are duplicates. Finding duplicates are easy but in this situation, they are partial duplicates e.g
ID NAME Status
1 abc Capital Approved
2 (abc Capital) Terminated
3 abc capital (dupe) Null
4 abc capitalx Null
5 BT Capital Approved
6 XE Capital Approved
7 xyz Finance Approved
8 xyz Finance X Null
9 xyz finance dupe Null
So from the above data, I want to retrieve duplicate names which are partially duplicate E.g
output:
1 abc Capital Approved
2 (abc Capital) Terminated
3 abc capital (dupe) Null
4 abc capitalx Null
5 xyz Finance Approved
6 xyz Finance X Null
7 xyz finance dupe Null
One option might be to use UTL_MATCH package and one of its functions. I chose edit_distance_similarity for this demonstration, but you might pick another.
SQL> with test (id, name) as
2 (select 1, 'abc Capital' from dual union all
3 select 2, '(abc Capital)' from dual union all
4 select 3, 'abc capital (dupe)' from dual union all
5 select 4, 'abc capitalx' from dual union all
6 select 5, 'BT Capital' from dual union all
7 select 6, 'XE Capital' from dual union all
8 select 7, 'xyz Finance' from dual union all
9 select 8, 'xyz Finance X' from dual union all
10 select 9, 'xyz finance dupe' from dual
11 ),
12 simil as
13 (select a.id, a.name aname, b.name bname,
14 utl_match.edit_distance_similarity(a.name, b.name) simil
15 from test a cross join test b
16 )
17 select distinct id, aname as name
18 from simil
19 where aname <> bname
20 and simil > 80
21 order by id;
ID NAME
---------- ------------------
1 abc Capital
2 (abc Capital)
4 abc capitalx
7 xyz Finance
8 xyz Finance X
SQL>

Split words and insert it new table with counting these words

I have the foolwoing table and data.
i need to:
1- split each sentence in each row into new row
2-count the words in each row based on last part of sentence based on soundex function
create table a (id number(9), words varchar(500));
insert into a values(1,'UK,LONDON,YEMEN,JOHN,CAIRO,OMAR ALI,EGYPT,Cairo,YEMAN,OMAR AMR ALI,LONDAN');
insert into a values(2,'UK,SUDAI,SUDAIN,AYHAM SHAHER YAFOOZ,ALI YAFOOZ');
insert into a values(3,'MALAYSIA, AHMED ALI,MALYSIAN');
expexted output
create table temp_words(id number(9),words varchar2(100), count_words number(9));
id words count_words
1 UK 1
1 LONDON 2
1 YEMEN 2
1 CAIRO 2
1 OMAR ALI 2
1 JOHN 1
2 UK 1
2 SUDAI 2
2 AYHAM SHAHER YAFOOZ 2
3 MALAYSIA 2
3 AHMED ALI 1
regards
to split the data as you want you can use a "connect by" as a row generator.
SQL> with src as (select id,',' || words || ',' as words,
2 length(words) - length(translate(words, '.,', '.')) + 1 no_of_words
3 from a)
4 select a.id,
5 substr(a.words,
6 instr(words, ',', 1, r) + 1,
7 instr(words, ',', 1, r + 1) - instr(words, ',', 1, r) - 1) word,
8 a.no_of_words
9 from (select level r
10 from dual
11 connect by level <= (select max(no_of_words) from src)) d
12 inner join src a
13 on d.r <= a.no_of_words
14 where a.no_of_words is not null
15 order by a.id, d.r
16 /
ID WORD NO_OF_WORDS
---------- -------------------- -----------
1 UK 11
1 LONDON 11
1 YEMEN 11
1 JOHN 11
1 CAIRO 11
1 OMAR ALI 11
1 EGYPT 11
1 Cairo 11
1 YEMAN 11
1 OMAR AMR ALI 11
1 LONDAN 11
2 UK 5
2 SUDAI 5
2 SUDAIN 5
2 AYHAM SHAHER YAFOOZ 5
2 ALI YAFOOZ 5
3 MALAYSIA 3
3 AHMED ALI 3
3 MALYSIAN 3
19 rows selected.
SQL>
Here is a SQLFiddle demo
select id,words,
case when i=0 then
SUBSTR(words,
1,
case when INSTR(words,',', 1, 1)=0
then 100000
else
INSTR(words,',', 1, 1)-1
end
)
ELSE
SUBSTR(words,
INSTR(words,',', 1, i)+1,
case when INSTR(words,',', 1, i+1)=0
then 100000
else
INSTR(words,',', 1, i+1)-INSTR(words,',', 1, i)-1
end
)
END word,
i+1 COUNTWORDS
from a,
(
select * from
(
select 0 i from dual
union
select 1 i from dual
union
select 2 i from dual
union
select 3 i from dual
union
select 4 i from dual
union
select 5 i from dual
union
select 6 i from dual
union
select 7 i from dual
union
select 8 i from dual
union
select 9 i from dual
union
select 10 i from dual
union
select 11 i from dual
union
select 12 i from dual
)
)
table_i
where
case when i>0 then INSTR(words,',', 1, i)
else 100000 end <>0
order by id,i
Another approach(using regexp_count and regexp_substr regular expression functions):
SQL> with Occurence(oc) as(
2 select level
3 from ( select max(regexp_count(words, '[^,]+')) ml
4 from a
5 ) t
6 connect by level <= t.ml
7 )
8 select id
9 , word
10 , count(word) over(partition by id, soundex(word) order by id) as count_words
11 From ( select a.id
12 , regexp_substr(words, '[^,]+', 1, o.oc) as word
13 from occurence o
14 cross join a
15 ) s
16 where s.word is not null
17 order by id
18 ;
ID WORD COUNT_WORDS
---------- -------------------- -----------
1 Cairo 2
1 CAIRO 2
1 EGYPT 1
1 JOHN 1
1 LONDAN 2
1 LONDON 2
1 OMAR ALI 1
1 OMAR AMR ALI 1
1 UK 1
1 YEMEN 2
1 YEMAN 2
2 ALI YAFOOZ 1
2 AYHAM SHAHER YAFOOZ 1
2 SUDAI 1
2 SUDAIN 1
2 UK 1
3 AHMED ALI 1
3 MALAYSIA 1
3 MALYSIAN 1
19 rows selected
You would need to insert your data as separate records. You can keep them as a concatenated string, if you like, but it'll just make your life very difficult. So:
create table words (
id number,
w varchar2(100),
s varchar2(4)
);
create or replace trigger words_auto
before insert or update on words
for each row
begin
select trim(upper(:new.w)), soundex(:new.w)
into :new.w, :new.s
from dual;
end;
insert into words (id, w) values (1, 'UK');
insert into words (id, w) values (1, 'LONDON');
...
insert into words (id, w) values (3, ' AHMED ALI');
insert into words (id, w) values (3, 'MALYSIAN');
You could write a procedure to split your concatenated string and populate the words table appropriately. Note that I have created a trigger that normalises your input to uppercase, removing all extraneous whitespace and automatically produces the Soundex codes.
Now here's a question: You want to group words by their Soundex codes; however, how do you determine the baseline? For example, 'LONDON' and 'LONDAN' both have code 'L535', but how do you know which record is the 'main' one?... You can't, without further lookup tables! As such, the best you can do is to group by the Soundex codes. This doesn't have to be stored in a table and it makes more sense to be a view:
create or replace view word_counts as
select id,
s soundex,
count(w) count_rows
from words
group by id,
s;
Note that I've called the count field count_rows as it counts records, rather than distinct rows. That is: records of 'LONDON', 'LONDAN' and 'LONDON' will show with a count of 3, not 2 (which you might be expecting). Anyway, with your data, the view will look something like this:
id soundex count_rows
----- --------- -----------
1 U200 1
1 L535 2
... ... ...
3 M420 2
3 A534 1
As I say, that's really the best you can expect without further infrastructure.