Match SQL Tables and find if entry exist in each group - sql

Thanks for the response but if the scenario is extended up to add a third Group_ID with just 2 GroupNames only the it picks up that too .
Please help me in writing a query which will match 2 tables and find the group id in which all the entries of second table match.
Table 1
Group_Id GroupNames GroupValues
111 G1 A
111 G1 B
111 G1 C
111 G2 D
111 G2 E
111 G2 F
111 G3 G
222 G1 A
222 G1 B
222 G1 C
222 G2 E
222 G2 F
222 G3 G
333 G3 G
333 G1 B
333 G1 C
Table 2:
GroupValues
B
D
G
H
OUTPUT
111
The Output of the query should be "111" since it has atleast one entry for all three Group Names "G1,G2,G3" . "222" is missing entry for Group Name G2 so it will not be returned.
Please assist.

You can do this with aggregation and a having clause:
select t1.Group_Id
from table1 t1 inner join
table2
on t2.GroupValues = t1.GroupValues
group by t1.Group_id
having count(distinct t1.GroupValues) = (select count(distinct GroupValues) from table2);
Note that the distinct is not necessary if you know there are no duplicate values.

Posting two solutions, one is little modification to what Gordon Linoff suggested and another a new one.
1) The inline view T1 add , incase if you have duplicate GroupValues within a particular Group_ID
select t1.Group_Id
from (select distinct Group_Id,GroupValues from table1 )t1 inner join
table2
on t2.GroupValues = t1.GroupValues
group by t1.Group_id
having count(distinct GroupValues) = (select count(distinct GroupValues) from table2)
2) In the SELECT bellow minus , is used for identifying the Group_ID's which are not complete (or has all the combinations of group_id and GroupValues ). These id's are removed from all the group_id's present in the table1, hence the result.
select distinct group_id
from table1
minus
select distinct Group_Id
from Table2 t2,(select distinct Group_Id from Table1 ) t1
where not exists (select 1
from Table1 t11
where t1.Group_Id = t11.Group_Id
and t11.GroupValues = t2.GroupValues)

Related

Joining table get the first data on first table if duplicate

Hi i have table1 and table2.
table1 is the logtime table of employees and table2 is the groupcode of the employee.
On table1 some employees has duplicate time in because they time in multiple time to just to secure their time in.
Table1
ID EMPID Time_IN
1 001 7:01 AM
2 004 7:04 AM
3 034 7:10 AM
4 034 7:11 AM
5 019 7:11 AM
6 019 7:12 AM
Table2
ID empID GroupName
1 001 AA
2 004 AB
3 034 AA
4 019 AA
result
GroupName CNT
AA 5
AB 1
Expected result
GroupName CNT
AA 3
AB 1
current query
Select b.GroupName, count(*) as cnt
from table1 a
inner join table2 b
on a.EMPID = b.empID
Group by b.GroupName
How can i achive as expected result above?
Thankyou in advance.
you can use distinct count as follows:
select t2.groupname, count(distinct empid) as cnt
from table1 t1 join table2 t2
on t1.empid = t2.empid
group by t2.groupname
The join is superfluous for the question you have asked:
select t2.GroupName, count(*) as cnt
from table2 t2
group by t2.GroupName;
This is much more efficient than joining and using count(distinct). You probably really have a different question, which should be asked as a new question.

how to get the data using Joins or Cross apply

I have requirement
sample data :
Table A :
ID name
1 cat
2 Dog
3 Bird
Table B :
ID name
1 aaa
1 bbb
2 ccc
2 ddd
Table C :
ID name
1 xxx
1 yyy
1 zzz
2 www
Required Output :
ID name name name
1 cat aaa xxx
1 cat bbb yyy
1 cat null zzz
2 Dog ccc www
2 Dog ddd www
3 Bird NULL NULL
I have tried with different joins
Select a.ID,a.name,b.name,c.name from #A a
full join #b b
on a.ID = b.ID
full join #c c
on b.ID = c.ID
Can anyone suggest me the best way to Proceed?
You can use window function row_number to assign sequence number within each id in the order of increasing name for table b and c and then do a full join between them. Finally, do a left join with a table:
with b1 as (
select b.*, row_number() over (partition by id order by name) as rn
from b
),
c1 as (
select c.*, row_number() over (partition by id order by name) as rn
from c
)
select a.*, t.b_name, t.c_name
from a
left join (
select coalesce(b1.id, c1.id) as id,
b1.name as b_name,
c1.name as c_name
from b1
full join c1 on b1.id = c1.id
and b1.rn = c1.rn
) t on a.id = t.id;
This assumes that you need to join the tables b and c based on id and the position (in the order of name column).

How to get Oracle to return unique results in a one to many relationship tables with a left join

I have a three tables
Table 1
Id Department
1 A
2 B
3 C
4 D
Table 2
Id DepartId Name
1 1 ABC
2 1 DEF
3 1 ASD
4 2 FGH
5 2 HJK
6 3 ZXC
Table 3
Id Depart Area
1 A pp
2 B
3 C nn
4 D oo
I need the result
Id Depart Name Area
1 A ABC pp
2 B FGH Null
3 C ZXC nn
4 D NULL oo
I need one matching entry from table 2 and table 3 to corresponding entry in the table 1
Do a left join to also get t1 rows without any reference in the t2 table. GROUP BY to get only 1 row per Department.
select t1.id, t1.Department, min(t2.Name)
from t1
left join t2 on t1.id = t2.DepartId
group by t1.id, t1.Department
I think I would do this with a correlated subquery:
select t1.*,
(select t2.name
from t2
where t1.id = t2.DepartId and rownum = 1
) as t2name
from t1;
This saves the overhead of an aggregation. An index on t2(DepartId, name) is optimal for this query.
by the way not the answer to your specific question but if instead of just one you want all the names you can use listagg
SELECT t1.id,
department,
LISTAGG (name, ',') WITHIN GROUP (ORDER BY name) names
FROM t1, t2
WHERE t1.id = t2.departId(+)
GROUP BY t1.id, department
ORDER BY 1
ID Department Names
1 A ABC,ASD,DEF
2 B FGH, HJK
3 C ZXC
4 D

how to select distinct values from two tables limit to one row

I have two tables with this values:
**table 1**
id name value
1 A 1
2 B 1
3 F 2
4 G 3
**table 2**
id name value
1 C 1
2 D 1
3 E 2
If I do an inner join of both tables by the value I´m getting this:
A C
A D
B C
B D
F E
But, the problem is that I want only distinct values from both columns like that:
A C
B D
F E
Another set of posible result will be:
A D
B C
F E
There´s no chance of name of table 1 will appear in table 2.
If one value from a column was already selected, it can´t be selected again. This example will be an error because C was already selected:
A C
B C
F E
Any ideas?
In order to pair the records, you need a running number per value to link with. Use row_number() for this.
select t1.name as t1_name, t2.name as t2_name
from
(
select name, value, row_number() over (partition by value order by name) as rn
from table1
) t1
join
(
select name, value, row_number() over (partition by value order by name) as rn
from table2
) t2
on t1.value = t2.value and t1.rn = t2.rn;
SQL fiddle: http://www.sqlfiddle.com/#!4/75de0/1.
Based on the data you provided, and the column you're using to join, you're getting the results you should be getting.
To get your desired results, you would need to join on id, not value
as such:
select a.id, a.name, b.name
from tableA a
inner join tableB b on a.id = b.id

How can I avoiding Cartesian product on SQL on multiple tables

Here is my sqlfiddle http://sqlfiddle.com/#!3/671c8/1.
Here are my tables:
Person
PID LNAME FNAME
1 Bob Joe
2 Smith John
3 Johnson Jake
4 Doe Jane
Table1
PID VALUE
1 3
1 5
1 35
2 10
2 15
3 8
Table2
PID VALUE
1 X1
1 X2
1 X3
2 Z1
3 X3
I am trying to join several tables on a person's ID. These tables contain events with dates, but the dates may or may not match across table. So what I really want it to regardless of date join the tables in a way such that when I get results the table with the largest rows will be the amount of rows in my result and all other tables will "fit" within. For example
Instead of this which is a cartesian product:
PID LNAME FNAME THINGONE THINGTWO
1 Bob Joe 3 X1
1 Bob Joe 3 X2
1 Bob Joe 3 X3
1 Bob Joe 5 X1
1 Bob Joe 5 X2
1 Bob Joe 5 X3
1 Bob Joe 35 X1
1 Bob Joe 35 X2
1 Bob Joe 35 X3
I would like something like this:
PID LNAME FNAME THINGONE THINGTWO
1 Bob Joe 3 X1
1 Bob Joe 5 X2
1 Bob Joe 35 X3
My sql statement:
SELECT
p.*,
t1.value as thingone,
t2.value as thingtwo
FROM
person p
left outer join table1 t1 on p.pid=t1.pid
left outer join table2 t2 on p.pid=t2.pid
;
I can't fathom why you want to do this, but...
You need to create an artificial join between table1 and table2, and then link that to the master table. One way of doing that is by ranking the rows in order. eg:
SELECT
p.pid, p.lname,p.fname, thingone, thingtwo
FROM
person p
left outer join
(
select ISNULL(t1.pid, t2.pid) as pid, t1.value as thingone, t2.value as thingtwo
from
(select *, ROW_NUMBER() over (partition by pid order by value) rn
from table1) t1
full outer join
(select *, ROW_NUMBER() over (partition by pid order by value) rn
from table2) t2
on t1.pid=t2.pid and t1.rn=t2.rn
) v
on p.pid = v.pid
This is a trickier problem than I thought. The challenge is being sure that all the records appear, regardless of the lengths of the two lists. The following works by enumerating each of the lists and using that for the join conditions:
SELECT p.*,
t1.value as thingone,
t2.value as thingtwo
FROM person p left outer join
(select t1.*,
row_number() over (partition by pid order by pid) as seqnum,
count(*) over (partition by pid) as cnt
from table1 t1
) t1
on p.pid = t1.pid left outer join
(select t2.*, row_number() over (partition by pid order by pid) as seqnum,
count(*) over (partition by pid) as cnt
from table2 t2
) t2
on p.pid = t2.pid
WHERE t1.seqnum = t2.seqnum or
(t2.seqnum > t1.cnt) or
(t1.seqnum > t2.cnt) or
t1.seqnum is null or
t2.seqnum is null;
Here is a slight modification to your SQL Fiddle that has better test data.
EDIT:
The logic in the where clause handles these cases (in order by the clauses):
Where the two lists have sequence numbers, these must match.
Where list2 is longer and list1 has at least one element.
Where list1 is longer and list2 has at least one element.
Where list1 is empty
Where list 2 is empty
These were arrived at by trial and error, because the original condition did not work:
on p.pid = t2.pid and t1.seqnum = t2.seqnum
This returns NULL values for p.id for the extra elements on the list. Podliuska's approach may also work; I had just started down this path and the where conditions do the trick.