SQL Complex join not giving distinct result - sql

I have two tables :-
Table1:-
ID1
1
1
1
1
4
5
Table2:-
Id2
2
2
1
1
1
8
I want to show all the ID2 from table2 which are present in ID1 of table1 by using joins
I used :-
select ID2 from Table2 t2 left join Table1 t1
on t2.Id2=t1.Id1
But this was giving repeated result as :-
Id2
1
1
1
1
1
1
1
It should show me 1 as 3 times only as it is present in Table2 3 times.
Please help.

You're matching the value 1 with 4 rows on Table1 and 3 rows on Table2 that's why you're seeing 12 rows. You need an additional JOIN condition. You can add a ROW_NUMBER and do an INNER JOIN to achieve your desired result.
WITH Cte1 AS(
SELECT *,
rn = ROW_NUMBER() OVER(PARTITION BY Id1 ORDER BY (SELECT NULL))
FROM Table1
),
Cte2 AS(
SELECT *,
rn = ROW_NUMBER() OVER(PARTITION BY Id2 ORDER BY (SELECT NULL))
FROM Table2
)
SELECT c2.Id2
FROM Cte2 c2
INNER JOIN Cte1 c1
ON c1.Id1 = c2.Id2
AND c1.rn = c2.rn
However, you can achieve the desired result without using a JOIN.
SELECT *
FROM Table2 t2
WHERE EXISTS(
SELECT 1 FROM Table1 t1 WHERE t1.Id1 = t2.Id2
)

It's the expected behavior of Join Operation. It will match every row from the two tables, so you will get 12 rows containing value 1 in result of join query.
You can use below query to get desired result.
select ID2 from Table2 t2 WHERE ID2 IN (SELECT ID1 FROM Table1 t1)

select id2 from table2 t2 where exists ( select 1 from table1 t1 where t1.id1 = t2.id2)

Your join logic works fine, the problem is each of your ID2 is matching against all ID1s. A simple solution would be to join with a table of distinct ID1s to avoid this duplication.
select
t2.ID2
from Table2 t2
left join (select distinct * from Table1) t1
on t1.Id1=t2.Id2
where t1.ID1 is not null
;
Here is a functional example
This will select your entire ID2 list with ID1 populated in a column. ID1 is null where there was no match. Select your ID2 column from this table but just don't pull null values (with where clause):

Related

Query to get oldest date into a column?

I have some tables that look like this:
Table1
Column1
A
B
C
Table2
column1 Id1 Id2
A 2 100
A 3 101
B 2 100
B 3 101
C 2 100
Table3
Id2 Date Item Status
100 10/20/17 A1 A
101 10/21/17 A1 A
100 11/22/17 A2 I
101 11/23/17 A2 A
My query looks like this:
Select
Date, *
FROM TABLE1 T1
LEFT JOIN Table2 T2 ON
T1.Column1 = T2.Column2
LEFT JOIN Table3 T3 ON
T2.Id2 = T3.Id2
I would like to return the oldest date on table 3 where the status is A into the date column in my query.
I was able to return the oldest date with this query but can't integrate this into my query with joins.
SELECT
MIN(DATE)
FROM Table3
WHERE Id2 IN (100,101)
AND STATUS = 'A'
group by Id2, ITEM
How can I get the oldest date into the query that uses the join clauses?
Couple of ways to approach what I believe that you're trying to do.
I'd say the easiest would be to simplify your search parameters with a subquery of Table 3.
select * from table2 t2
LEFT JOIN
(Select Id2, Item, max(Date)from Table 3
WHERE Status = 'A'
GROUP BY Id2, Item)a ON t2.Id2 = a.Id2;
The subquery does the filtering and then a quick join to table 2 would get all your data.

Join two tables into one by adding data of all tables sequentially

I am facing an issue in joining three tables with different data.
Suppose I am having table1 and table2 like :
table1 : table2:
ID1 ID2
----- -----
1 102
2 103
I need to join these two tables into table3 as :
table3
------
ID1 ID2
--- ---
1 102
2 103
I am applying cross join in table1 and table2 but i am gettng:
table3 :
ID1 ID2
--- ---
1 102
2 102
1 103
2 103
If you are simply ordering by ID for each table, and then matching the first row with the first row - the following should work.
Select T1.ID1
, T2.ID2
from (Select ID1, row_number() over (order by ID1) rownum from Table1) T1
inner join (Select ID2, row_number() over (order by ID2) rownum from Table2) T2
on T1.rownum = T2.rownum
It create a subquery for each table with a row number, and then inner joins on the row number.
If your ID's are not always in sequential form then use this:
SELECT t1.ID1, T2.ID2
FROM (SELECT ID1, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) rn FROM table1 ) t1
INNER JOIN (SELECT ID2, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) rn FROM table2) t2
ON t1.rn = t2.rn

How to write a query by joining two tables?

I have two tables:
Table 1:
ID NAME
1 ID1
2 ID2
3 ID3
4 ID4
5 ID5
6 ID6
7 ID7
Table 2:
Parent_ID Child_ID
1 2
2 5
2 3
3 6
How do I write a query to get below output if I assign Parent_Id = 1 in where condition?
P_ID NAME Is_Group Selected
1 ID1 Yes No
2 ID2 Yes Yes
3 ID3 Yes Yes
4 ID4 No No
5 ID5 No Yes
6 ID6 No Yes
7 ID7 No No
So, output mainly contains records from table one but also it need to have two additional columns.
Value in Is_Group column should be "Yes" if ID from Table 1 exists in Parent_ID column in Table 2. Value in Selected column should be "yes" if ID from Table 1 exists in Child_ID column in Table 2 and Parent_ID = 1 (like a cross reference).
In additional, I need to check if a Child_ID has any cross reference. For example In Table 2 Child_ID is 2 for Parent_Id 1, 2 also has 5 and 3 as child_Id so I need to have Selected column values as "Yes" for Id's 3 and 5 and so on.
Thanks in advance for your reply. Sorry for my English.
This should give you the output you need.
It uses a recursive cte to get the hierarchy.
Then outer joins to the cte twice to determine if the the ID is a Group, or Selected by checking for null values
WITH cte AS
(
SELECT Parent_ID,
Child_ID
FROM Table2
WHERE Parent_ID = 1
UNION ALL
SELECT t2.Parent_ID,
t2.Child_ID
FROM Table2 t2
INNER JOIN cte ON t2.Parent_ID = cte.Child_ID
)
SELECT DISTINCT
t1.*,
(CASE WHEN grp.Parent_ID IS NULL THEN 'No'
ELSE 'Yes'
END) AS Is_Group,
(CASE WHEN sel.Parent_ID IS NULL THEN 'No'
ELSE 'Yes'
END) AS Selected
FROM Table1 t1
LEFT JOIN cte grp ON t1.ID = grp.Parent_ID
LEFT JOIN cte sel ON t1.ID = sel.Child_ID
The fact that you're selecting everything from Table1 regardless of the whether it's in the selected hierarchy or not would give you No for Is_Group for any ID's that are Parent_IDs, but not actually in the hierachy cte. To always determine if an ID is a Group or not, just left join to Table2 as grp instead of the cte.. like.
;WITH cte AS
(
SELECT Parent_ID,
Child_ID
FROM Table2
WHERE Parent_ID = 1
UNION ALL
SELECT t2.Parent_ID,
t2.Child_ID
FROM Table2 t2
INNER JOIN cte ON t2.Parent_ID = cte.Child_ID
)
SELECT DISTINCT
t1.*,
(CASE WHEN grp.Parent_ID IS NULL THEN 'No'
ELSE 'Yes'
END) AS Is_Group,
(CASE WHEN sel.Parent_ID IS NULL THEN 'No'
ELSE 'Yes'
END) AS Selected
FROM Table1 t1
LEFT JOIN Table2 grp ON t1.ID = grp.Parent_ID
LEFT JOIN cte sel ON t1.ID = sel.Child_ID
try this,
select distinct id, t.NAME,
case when t1.Parent_ID is not null then 'Yes' else 'No' end Is_Group
,case when b.Child_ID is null then 'No' else 'Yes' end Selected
from Table1 t left join Table2 t1 on t.ID =t1.Parent_ID
outer apply (select Child_ID from Table2 a where a.Child_ID=t.ID ) b

require to form a sql query

I was working on preparing a query where I was stuck.
Consider tables below:
table1
id key col1
-- --- -----
1 1 abc
2 2 d
3 3 s
4 4 xyz
table2
id col1 foreignkey
-- ---- ----------
1 12 1
2 13 1
3 14 1
4 12 2
5 13 2
Now what I need is to select only those records from table1 for which the corresponding entries in table2 does not have say col1 value as 12.
So the challenge is after applying join even though it will skip for value 1 corresponding to col1 equal to 12 it still has another multiple rows whose values are say 13, 14 for which also they have same foreignkey. Now what I want is if there is a single row having value 12 then it should not pick that id at all from table1.
How can I form a query with this?
The output which i need is say from above table structure i want to get those records from table1 for which col1 value from table2 does not have value as 14.
so my query should return me only row 2 from table1 and not row 1.
Another way of doing that. The first two queries are just for making the sample data.
;WITH t1(id ,[key] ,col1) AS
(
SELECT 1 , 1 , 'abc' UNION ALL
SELECT 2 , 2 , 'd' UNION ALL
SELECT 3 , 3 , 's' UNION ALL
SELECT 4 , 4 , 'xyz'
)
,t2(id ,col1, foreignkey) AS
(
SELECT 1 , 12 , 1 UNION ALL
SELECT 2 , 13 , 1 UNION ALL
SELECT 3 , 14 , 1 UNION ALL
SELECT 4 ,12 , 2 UNION ALL
SELECT 5 ,13 , 2
)
SELECT id, [key], col1
FROM t1
WHERE id NOT IN (SELECT t2.Id
FROM t2
INNER JOIN t1 ON t1.Id = t2.foreignkey
WHERE t2.col1 = 14)
This is a typical case for NOT EXISTS:
SELECT id, [key], col1
FROM table1 t1
WHERE NOT EXISTS (SELECT 1
FROM table2 t2
WHERE t2.foreignkey = t1.id AND t2.col1 = 14)
The above query will not select a row from table1 if there is a single correlated row in table2 having col1 = 14.
Output:
id key col1
-------------
2 2 d
3 3 s
4 4 xyz
If you want to return records that, in addition to the criterion set above, also have correlated records in table2, then you can use the following query:
SELECT t1.id, MAX(t1.[key]) AS [key], MAX(t1.col1) AS col1
FROM table1 t1
INNER JOIN table2 t2 ON t1.id = t2.foreignkey
GROUP BY t1.id
HAVING COUNT(CASE WHEN t2.col1 = 14 THEN 1 END) = 0
Output:
id key col1
-------------
2 2 d
You can also achieve the same result with the second query using a combination of EXISTS and NOT EXISTS:
SELECT id, [key], col1
FROM table1 t1
WHERE EXISTS (SELECT 1
FROM table2 t2
WHERE t2.foreignkey = t1.id)
AND
NOT EXISTS (SELECT 1
FROM table2 t3
WHERE t3.foreignkey = t1.id AND t3.col1 = 14)
select t1.id,t1.key,
(select ROW_NUMBER() OVER(PARTITION BY col1 ORDER BY col1 DESC) AS Row,* into
#Temp from table1)
from table1 t1
inner join table2 t2 on t1.id=t2.foreignkey
where t2.col1=(select col1 from #temp where row>1)

How do I find groups of rows where all rows in each group have a specific column value

Sample data:
ID1 ID2 Num Type
---------------------
1 1 1 'A'
1 1 2 'A'
1 2 3 'A'
1 2 4 'A'
2 1 1 'A'
2 2 1 'B'
3 1 1 'A'
3 2 1 'A'
Desired result:
ID1 ID2
---------
1 1
1 2
3 1
3 2
Notice that I'm grouping by ID1 and ID2, but not Num, and that I'm looking specifically for groups where Type = 'A'. I know it's doable through a join two queries on the same table: one query to find all groups that have a distinct Type, and another query to filter rows with Type = 'A'. But I was wondering if this can be done in a more efficient way.
I'm using SQL Server 2008, and my current query is:
SELECT ID1, ID2
FROM (
SELECT ID1, ID2
FROM T
GROUP BY ID1, ID2
HAVING COUNT( DISTINCT Type ) = 1
) AS SingleType
INNER JOIN (
SELECT ID1, ID2
FROM T
WHERE Type = 'A'
GROUP BY ID1, ID2
) AS TypeA ON
TypeA.ID1 = SingleType.ID1 AND
TypeA.ID2 = SingleType.ID2
EDIT: Updated sample data and query to indicate that I'm grouping on two columns, not just one.
SELECT ID1, ID2
FROM MyTable
GROUP BY ID1, ID2
HAVING COUNT(Type) = SUM(CASE WHEN Type = 'A' THEN 1 ELSE 0 END)
There are two alternatives that don't require the aggregation (but do require distinct)
ANTI-JOIN
SELECT DISTINCT t1.ID1, t1.ID2
FROM
table t1
LEFT JOIN table t2
ON t1.ID1 = t2.ID1
and t1.Type <> t2.Type
WHERE
t1.Type = 'A'
AND
t2.ID1 IS NULL
See it working at this data.se query Sample for 9132209 (Anti-Join)
NOT EXISTS
SELECT DISTINCT t1.ID1, t1.ID2
FROM
table t1
WHERE
t1.Type = 'A'
AND
NOT EXISTS
(SELECT 1
FROM table t2
WHERE t1.ID1 = t2.ID1 AND Type <> 'A')
See it working at this data.se query Sample for 9132209 Not Exists