UPDATE based on multiple "WHERE IN" conditions - sql

Let's say I have a table I want to update based on multiple conditions. Each of these conditions is an equal-sized array, and the only valid cases are the ones which match the same index in the arrays.
That is, if we use the following SQL clause
UPDATE Foo
SET bar = 1
WHERE a IN ( 1, 2, 3, 4, 5)
AND b IN ( 6, 7, 8, 9, 0)
AND c IN ('a', 'b', 'c', 'd', 'e')
bar will be set to 1 for any row which has, for example, a = 1, b = 8, c = 'e'.
That is not what I want.
I need a clause where only a = 1, b = 6, c = 'a' or a = 2, b = 7, c = 'b' (etc.) works.
Obviously I could rewrite the clause as
UPDATE Foo
SET bar = 1
WHERE (a = 1 AND b = 6 AND c = 'a')
OR (a = 2 AND b = 7 AND c = 'b')
OR ...
This would work, but it's hardly extensible. Given the values of the conditions are variable and obtained programmatically, it'd be far better if I could set each array in one place instead of having to build a string-building loop to get that WHERE call right.
So, is there a better, more elegant way to have the same behavior as this last block?

Use the Table Values Constructor :
UPDATE f
SET bar = 1
WHERE EXISTS (
SELECT * FROM (VALUES (1,6,'a'),(2,7,'b'),(3,8,'c')) AS Trios(a,b,c)
WHERE Trios.a = f.a AND Trios.b = f.b AND Trios.c = f.c
)

You can use values() and join:
UPDATE f
SET bar = 1
FROM Foo f JOIN
(VALUES (1, 6, 'a'),
(2, 7, 'b'),
. . .
) v(a, b, c)
ON f.a = v.a AND f.b = v.b AND f.c = v.c;

Try this might work
DECLARE #Temp AS Table ( a int, b int, c varchar(50))
INSERT INTO #Temp(a,b,c)
VALUES(1, 6, 'a'),
(2, 7, 'b'),
(3, 8, 'c'),
(4, 9, 'd'),
(5, 0, 'e')
UPDATE F
SET bar = 1
FROM FOO F INNER JOIN #Temp T
ON F.a = T.a AND F.b = T.b AND F.c = T.c

When you read the data don't save it as separated values but as a single string and then use the following:
update foo
set bar = 1
where concat(a,b,c) in ('16a','27b','38c','49d','50e')
it may not be the most elegant way but it is very practical and simple.

I could be entirely off the mark here--I'm not sure if you're passing in a set of values or what-have-you--but my first thought is using a series of CTEs.
I'm making considerable assumptions about your data, but here's an example you can run in SSMS based on my thoughts of your question.
-- Create #Data and insert some, er... data ---
DECLARE #Data TABLE ( id INT IDENTITY(100,1) PRIMARY KEY, a VARCHAR(1), b VARCHAR(1), c VARCHAR(1) );
INSERT INTO #Data ( a ) VALUES ('1'), ('2'), ('3'), ('4'), ('5');
INSERT INTO #Data ( b ) VALUES ('6'), ('7'), ('8'), ('9'), ('0');
INSERT INTO #Data ( c ) VALUES ('a'), ('b'), ('c'), ('d'), ('e');
So let's assume this is your data. I've kept it simple to make it easier to understand.
+-----+---+---+---+
| id | a | b | c |
+-----+---+---+---+
| 100 | 1 | | |
| 101 | 2 | | |
| 102 | 3 | | |
| 103 | 4 | | |
| 104 | 5 | | |
| 105 | | 6 | |
| 106 | | 7 | |
| 107 | | 8 | |
| 108 | | 9 | |
| 109 | | 0 | |
| 110 | | | a |
| 111 | | | b |
| 112 | | | c |
| 113 | | | d |
| 114 | | | e |
+-----+---+---+---+
Query the data with aligned "array" indexes:
;WITH CTE_A AS (
SELECT
id,
ROW_NUMBER() OVER ( ORDER BY id ) AS a_row_id,
a
FROM #Data WHERE a IS NOT NULL
)
, CTE_B AS (
SELECT
id,
ROW_NUMBER() OVER ( ORDER BY id ) AS b_row_id,
b
FROM #Data WHERE b IS NOT NULL
)
, CTE_C AS (
SELECT
id,
ROW_NUMBER() OVER ( ORDER BY id ) AS c_row_id,
c
FROM #Data WHERE c IS NOT NULL
)
SELECT
CTE_A.id, CTE_A.a_row_id, CTE_A.a
, CTE_B.id, CTE_B.b_row_id, CTE_B.b
, CTE_C.id, CTE_C.c_row_id, CTE_C.c
FROM CTE_A
JOIN CTE_B ON CTE_A.a_row_id = CTE_B.b_row_id
JOIN CTE_C ON CTE_A.a_row_id = CTE_C.c_row_id;
Which returns:
+-----+----------+---+-----+----------+---+-----+----------+---+
| id | a_row_id | a | id | b_row_id | b | id | c_row_id | c |
+-----+----------+---+-----+----------+---+-----+----------+---+
| 100 | 1 | 1 | 105 | 1 | 6 | 110 | 1 | a |
| 101 | 2 | 2 | 106 | 2 | 7 | 111 | 2 | b |
| 102 | 3 | 3 | 107 | 3 | 8 | 112 | 3 | c |
| 103 | 4 | 4 | 108 | 4 | 9 | 113 | 4 | d |
| 104 | 5 | 5 | 109 | 5 | 0 | 114 | 5 | e |
+-----+----------+---+-----+----------+---+-----+----------+---+
Again, assumptions made on your data (in particular an id exists that can be sorted), but this basically pivots it by linking the a, b and c values on their relative "index" (ROW_NUMBER). By using ROW_NUMBER in this way, we can create a makeshift array index value ( a_row_id, b_row_id, c_row_id ) that can be used to join the resulting values.
This example can easily be changed to an UPDATE statement.
Does this address your question?

Related

Replace value in column based on another column

I have the following table:
+----+--------+------------+----------------------+
| ID | Name | To_Replace | Replaced |
+----+--------+------------+----------------------+
| 1 | Fruits | 1 | Fruits |
| 2 | Apple | 1-2 | Fruits-Apple |
| 3 | Citrus | 1-3 | Fruits-Citrus |
| 4 | Orange | 1-3-4 | Fruits-Citrus-Orange |
| 5 | Empire | 1-2-5 | Fruits-Apple-Empire |
| 6 | Fuji | 1-2-6 | Fruits-Apple-Fuji |
+----+--------+------------+----------------------+
How can I create the column Replaced ? I thought of creating 10 maximum columns (I know there are no more than 10 nested levels) and fetch the ID from every substring split by '-', and then concatenating them if not null into Replaced, but I think there is a simpler solution.
While what you ask for is technically feasible (probably using a recursive query or a tally), I will take a different stance and suggest that you fix your data model instead.
You should not be storing multiple values as a delimited list in a single database column. This defeats the purpose of a relational database, and makes simple things both unnecessarily complicated and inefficient.
Instead, you should have a separate table to store that data, which each replacement id on a separate row, and possibly a column that indicates the sequence of each element in the list.
For your sample data, this would look like:
id replace_id seq
1 1 1
2 1 1
2 2 2
3 1 1
3 3 2
4 1 1
4 3 2
4 4 3
5 1 1
5 2 2
5 5 3
6 1 1
6 2 2
6 6 3
Now you can efficiently generate the expected result with either a join, a subquery, or a lateral join. Assuming that your table is called mytable and that the mapping table is mymapping, the lateral join solution would be:
select t.*, r.*
from mytable t
outer apply (
select string_agg(t1.name) within group(order by m.seq) replaced
from mymapping m
inner join mytable t1 on t1.id = m.replace_id
where m.id = t.id
) x
You can try something like this:
DECLARE #Data TABLE ( ID INT, [Name] VARCHAR(10), To_Replace VARCHAR(10) );
INSERT INTO #Data ( ID, [Name], To_Replace ) VALUES
( 1, 'Fruits', '1' ),
( 2, 'Apple', '1-2' ),
( 3, 'Citrus', '1-3' ),
( 4, 'Orange', '1-3-4' ),
( 5, 'Empire', '1-2-5' ),
( 6, 'Fuji', '1-2-6' );
SELECT
*
FROM #Data AS d
OUTER APPLY (
SELECT STRING_AGG ( [Name], '-' ) AS Replaced FROM #Data WHERE ID IN (
SELECT CAST ( [value] AS INT ) FROM STRING_SPLIT ( d.To_Replace, '-' )
)
) List
ORDER BY ID;
Returns
+----+--------+------------+----------------------+
| ID | Name | To_Replace | Replaced |
+----+--------+------------+----------------------+
| 1 | Fruits | 1 | Fruits |
| 2 | Apple | 1-2 | Fruits-Apple |
| 3 | Citrus | 1-3 | Fruits-Citrus |
| 4 | Orange | 1-3-4 | Fruits-Citrus-Orange |
| 5 | Empire | 1-2-5 | Fruits-Apple-Empire |
| 6 | Fuji | 1-2-6 | Fruits-Apple-Fuji |
+----+--------+------------+----------------------+
UPDATE
Ensure the id list order is maintained when aggregating names.
DECLARE #Data TABLE ( ID INT, [Name] VARCHAR(10), To_Replace VARCHAR(10) );
INSERT INTO #Data ( ID, [Name], To_Replace ) VALUES
( 1, 'Fruits', '1' ),
( 2, 'Apple', '1-2' ),
( 3, 'Citrus', '1-3' ),
( 4, 'Orange', '1-3-4' ),
( 5, 'Empire', '1-2-5' ),
( 6, 'Fuji', '1-2-6' ),
( 7, 'Test', '6-2-7' );
SELECT
*
FROM #Data AS d
OUTER APPLY (
SELECT STRING_AGG ( [Name], '-' ) AS Replaced FROM (
SELECT TOP 100 PERCENT
Names.[Name]
FROM ( SELECT CAST ( '<ids><id>' + REPLACE ( d.To_Replace, '-', '</id><id>' ) + '</id></ids>' AS XML ) AS id_list ) AS xIds
CROSS APPLY (
SELECT
x.f.value('.', 'INT' ) AS name_id,
ROW_NUMBER() OVER ( ORDER BY ( SELECT NULL ) ) AS row_id
FROM xIds.id_list.nodes('//ids/id') x(f)
) AS ids
INNER JOIN #Data AS Names ON Names.ID = ids.name_id
ORDER BY row_id
) AS x
) List
ORDER BY ID;
Returns
+----+--------+------------+----------------------+
| ID | Name | To_Replace | Replaced |
+----+--------+------------+----------------------+
| 1 | Fruits | 1 | Fruits |
| 2 | Apple | 1-2 | Fruits-Apple |
| 3 | Citrus | 1-3 | Fruits-Citrus |
| 4 | Orange | 1-3-4 | Fruits-Citrus-Orange |
| 5 | Empire | 1-2-5 | Fruits-Apple-Empire |
| 6 | Fuji | 1-2-6 | Fruits-Apple-Fuji |
| 7 | Test | 6-2-7 | Fuji-Apple-Test |
+----+--------+------------+----------------------+
I'm sure there's optimization that can be done here, but this solution seems to guarantee the list order is kept.

Select MAX date using data from several columns SQL

I know this is a much asked question and I've had a look through whats already available but I believe my case is slightly unique (and if it's not please point me in the right direction).
I am trying to find the latest occurrence of a row associated to a user a currently across two tables and several columns.
table: statusUpdate
+-------+-----------+-----------+-------------------+
| id | name | status | date_change |
+-------+-----------+-----------+-------------------+
| 1 | Matt | 0 | 01-01-2001 |
| 2 | Jeff | 1 | 01-01-2001 |
| 3 | Jeff | 2 | 01-01-2002 |
| 4 | Bill | 2 | 01-01-2001 |
| 5 | Bill | 3 | 01-01-2004 |
+-------+-----------+-----------+-------------------+
table: relationship
+-------+-----------+--------------+
| id | userID |stautsUpdateID|
+-------+-----------+--------------+
| 1 | 22 | 1 |
| 2 | 33 | 2 |
| 3 | 33 | 3 |
| 4 | 44 | 4 |
| 5 | 44 | 5 |
+-------+-----------+--------------+
There is a third table which links userID to its own table but these sample tables should be good enough to get my question over.
I am looking to get the latest status change by date. The problem currently is that it returns all instances of a status change.
Current results:
+-------+---------+-----------+-------------------+
|userID |statusID | status | date_change |
+-------+---------+-----------+-------------------+
| 33 | 2 | 1 | 01-01-2001 |
| 33 | 3 | 2 | 01-01-2002 |
| 44 | 4 | 2 | 01-01-2001 |
| 44 | 5 | 3 | 01-01-2004 |
+-------+---------+-----------+-------------------+
Expected results:
+-------+-----------+-----------+-------------------+
|userID |statusID | status | date_change |
+-------+-----------+-----------+-------------------+
| 33 | 3 | 2 | 01-01-2002 |
| 44 | 5 | 3 | 01-01-2004 |
+-------+-----------+-----------+-------------------+
I hope this all makes sense, please ask for more information otherwise.
Just to reiterate I just want to return the latest instance of a users status change by date.
Sample code of one of my attempts:
select
st.ID, st.status, st.date_change, r.userID
from statusUpdate st
inner join Relationship r on st.ID = r.statusUpdateID
inner join (select ID, max(date_change) as recent from statusUpdate
group by ID) as y on r.stausUpdateID = y.ID and st.date_change =
y.recent
Hope someone can point me in the right direction.
use row_number() to get the last row by user
select *
from
(
select st.ID, st.status, st.date_change, r.userID,
rn = row_number() over (partition by r.userID order by st.date_change desc)
from statusUpdate st
inner join Relationship r on st.ID = r.statusUpdateID
) as d
where rn = 1
I ADDED MAX condition to your answer
CREATE TABLE #Table1
([id] int, [name] varchar(4), [status] int, [date_change] datetime)
;
INSERT INTO #Table1
([id], [name], [status], [date_change])
VALUES
(1, 'Matt', 0, '2001-01-01 00:00:00'),
(2, 'Jeff', 1, '2001-01-01 00:00:00'),
(3, 'Jeff', 2, '2002-01-01 00:00:00'),
(4, 'Bill', 2, '2001-01-01 00:00:00'),
(5, 'Bill', 3, '2004-01-01 00:00:00')
;
CREATE TABLE #Table2
([id] int, [userID] int, [stautsUpdateID] int)
;
INSERT INTO #Table2
([id], [userID], [stautsUpdateID])
VALUES
(1, 22, 1),
(2, 33, 2),
(3, 33, 3),
(4, 44, 4),
(5, 44, 5)
select
max(st.ID) id , max(st.status) status , max(st.date_change) date_change, r.userID
from #Table1 st
inner join #Table2 r on st.ID = r.stautsUpdateID
inner join (select ID, max(date_change) as recent from #Table1
group by ID) as y on r.stautsUpdateID = y.ID and st.date_change =
y.recent
group by r.userID
output
id status date_change userID
1 0 2001-01-01 00:00:00.000 22
3 2 2002-01-01 00:00:00.000 33
5 3 2004-01-01 00:00:00.000 44

How to SELECT all rows in a query where specific rows meet a certain criteria?

I have a query that accesses data from three tables:
SELECT fg.factGroupName, mc.CI, mt.Config
FROM MetricTypes mt
INNER JOIN MetricCollection mc ON mt.TypeId = mc.MetricType
INNER JOIN factGroup fg ON fg.factGroupId = mc.Factgroup
WHERE mt.ToolName = 2 AND mt.TypeName = 'inputs' AND mt.Deploy = 'Y' AND mc.Deploy = 'Y';
The tables look like this:
factGroup
|-------------|--------------|
|factGroupId |factGroupName |
|-------------|--------------|
| 20 | test_servers |
| 21 | prod_servers |
|-------------|--------------|
MetricTypes
|-------------|--------------|--------------|------------|-----------|
|TypeId |TypeName |ToolName |Config |Deploy |
|-------------|--------------|--------------|------------|-----------|
| 10 | inputs | 2 | foo | Y |
| 11 | inputs | 2 | bar | |
| 12 | outputs | 4 | giggle | |
| 13 | inbetween | 6 | biz | |
|-------------|--------------|--------------|------------|-----------|
MetricCollection
|-------------|--------------|--------------|------------|-----------|
|MetricId |Factgroup |MetricType |CI |Deploy |
|-------------|--------------|--------------|------------|-----------|
| 1 | 20 | 10 | alpha | Y |
| 2 | 20 | 11 | beta | |
| 3 | 20 | 12 | gamma | |
| 4 | 21 | 13 | theta | |
|-------------|--------------|--------------|------------|-----------|
The output from the query looks like this:
|-------------|--------------|--------------|
|factGroupName| CI | Config |
|-------------|--------------|--------------|
| test_servers| alpha | foo |
|-------------|--------------|--------------|
How would I adjust my query in order to get a result which reflects all CI and Config fields for a given factGroupName, and not just the ones that have the Y flag in the Deploy fields? In other words, I'd like to have my output look like this:
|-------------|--------------|--------------|
|factGroupName| CI | Config |
|-------------|--------------|--------------|
| test_servers| alpha | foo |
| test_servers| beta | bar |
|-------------|--------------|--------------|
To clarify: I want to keep the condition in place to check for the existence of a "Y" flag in the two Deploy fields, as the script associated with this query uses that flag as a trigger to perform additional work.
The INNER JOIN on Deploy = 'Y' appears to be the problem.
Table example:
create table #factgroup
(
factgroupid int,
factgroupname varchar(20)
)
create table #metrictypes
(
typeid int,
typename varchar(10),
toolname int,
config varchar(10),
deploy varchar(1)
)
create table #metriccollection
(
metricid int,
factgroup int,
metrictype int,
ci varchar(10),
deploy varchar(1)
)
insert into #factgroup values (20, 'test_servers')
insert into #metrictypes values (10, 'inputs', 2, 'foo', 'Y')
insert into #metrictypes (typeid, typename, toolname, config) values (11, 'inputs', 2, 'bar')
insert into #metriccollection values (1, 20, 10, 'alpha', 'Y')
insert into #metriccollection (metricid, factgroup, metrictype, ci) values (2, 20, 11, 'beta')
Try this (with hashtags removed):
SELECT fg.factGroupName, mc.CI, mt.Config
FROM #MetricTypes mt
INNER JOIN #MetricCollection mc ON mt.TypeId = mc.MetricType
INNER JOIN #factGroup fg ON fg.factGroupId = mc.Factgroup
WHERE mt.ToolName = 2 AND mt.TypeName = 'inputs'
and mt.toolname in (
select distinct mt.ToolName
from #metrictypes mt
where mt.deploy = 'y'
)
and mc.factgroup in (
select distinct mc.factgroup
from #metriccollection mc
where mc.deploy = 'y'
)

Recursive SQL - count number of descendants in hierarchical structure

Consider a database table with the following columns:
mathematician_id
name
advisor1
advisor2
The database represents data from the Math Genealogy Project, where each mathematician usually has one single advisor, but there are situations when there are two advisors.
Visual aid to make things clearer:
How do I count the number of descendants for each of the mathematicians?
I should probably use Common Table Expressions (WITH RECURSIVE), but I am pretty much stuck at the moment. All the similar examples I found deal with hierarchies having only one parent, not two.
Update:
I adapted the solution for SQL Server provided by Vladimir Baranov to also work in PostgreSQL:
WITH RECURSIVE cte AS (
SELECT m.id as start_id,
m.id,
m.name,
m.advisor1,
m.advisor2,
1 AS level
FROM public.mathematicians AS m
UNION ALL
SELECT cte.start_id,
m.id,
m.name,
m.advisor1,
m.advisor2,
cte.level + 1 AS level
FROM public.mathematicians AS m
INNER JOIN cte ON cte.id = m.advisor1
OR cte.id = m.advisor2
),
cte_distinct AS (
SELECT DISTINCT start_id, id
FROM cte
)
SELECT cte_distinct.start_id,
m.name,
COUNT(*)-1 AS descendants_count
FROM cte_distinct
INNER JOIN public.mathematicians AS m ON m.id = cte_distinct.start_id
GROUP BY cte_distinct.start_id, m.name
ORDER BY cte_distinct.start_id
You didn't say what DBMS you use. I'll use SQL Server for this example, but it will work in other databases that support recursive queries as well.
Sample data
I entered only the right part of your tree, starting from Euler.
The most interesting part is the multiple paths between Lagrange and Dirichlet.
DECLARE #T TABLE (ID int, name nvarchar(50), Advisor1ID int, Advisor2ID int);
INSERT INTO #T (ID, name, Advisor1ID, Advisor2ID) VALUES
(1, 'Euler', NULL, NULL),
(2, 'Lagrange', 1, NULL),
(3, 'Laplace', NULL, NULL),
(4, 'Fourier', 2, NULL),
(5, 'Poisson', 2, 3),
(6, 'Dirichlet', 4, 5),
(7, 'Lipschitz', 6, NULL),
(8, 'Klein', NULL, 7),
(9, 'Lindemann', 8, NULL),
(10, 'Furtwangler', 8, NULL),
(11, 'Hilbert', 9, NULL),
(12, 'Taussky-Todd', 10, NULL);
This is how it looks like:
SELECT * FROM #T;
+----+--------------+------------+------------+
| ID | name | Advisor1ID | Advisor2ID |
+----+--------------+------------+------------+
| 1 | Euler | NULL | NULL |
| 2 | Lagrange | 1 | NULL |
| 3 | Laplace | NULL | NULL |
| 4 | Fourier | 2 | NULL |
| 5 | Poisson | 2 | 3 |
| 6 | Dirichlet | 4 | 5 |
| 7 | Lipschitz | 6 | NULL |
| 8 | Klein | NULL | 7 |
| 9 | Lindemann | 8 | NULL |
| 10 | Furtwangler | 8 | NULL |
| 11 | Hilbert | 9 | NULL |
| 12 | Taussky-Todd | 10 | NULL |
+----+--------------+------------+------------+
Query
It is a classic recursive query with two interesting points.
1) The recursive part of the CTE joins to the anchor part using both Advisor1ID and Advisor2ID:
INNER JOIN CTE
ON CTE.ID = T.Advisor1ID
OR CTE.ID = T.Advisor2ID
2) Since it is possible to have multiple paths to the descendant, recursive query may output the node several times. To eliminate these duplicates I used DISTINCT in CTE_Distinct. It may be possible to solve it more efficiently.
To understand better how the query works run each CTE separately and examine intermediate results.
WITH
CTE
AS
(
SELECT
T.ID AS StartID
,T.ID
,T.name
,T.Advisor1ID
,T.Advisor2ID
,1 AS Lvl
FROM #T AS T
UNION ALL
SELECT
CTE.StartID
,T.ID
,T.name
,T.Advisor1ID
,T.Advisor2ID
,CTE.Lvl + 1 AS Lvl
FROM
#T AS T
INNER JOIN CTE
ON CTE.ID = T.Advisor1ID
OR CTE.ID = T.Advisor2ID
)
,CTE_Distinct
AS
(
SELECT DISTINCT
StartID
,ID
FROM CTE
)
SELECT
CTE_Distinct.StartID
,T.name
,COUNT(*) AS DescendantCount
FROM
CTE_Distinct
INNER JOIN #T AS T ON T.ID = CTE_Distinct.StartID
GROUP BY
CTE_Distinct.StartID
,T.name
ORDER BY CTE_Distinct.StartID;
Result
+---------+--------------+-----------------+
| StartID | name | DescendantCount |
+---------+--------------+-----------------+
| 1 | Euler | 11 |
| 2 | Lagrange | 10 |
| 3 | Laplace | 9 |
| 4 | Fourier | 8 |
| 5 | Poisson | 8 |
| 6 | Dirichlet | 7 |
| 7 | Lipschitz | 6 |
| 8 | Klein | 5 |
| 9 | Lindemann | 2 |
| 10 | Furtwangler | 2 |
| 11 | Hilbert | 1 |
| 12 | Taussky-Todd | 1 |
+---------+--------------+-----------------+
Here DescendantCount counts the node itself as a descendant. You can subtract 1 from this result if you want to see 0 instead of 1 for the leaf nodes.
Here is SQL Fiddle.

SQL Server : compare two tables and return similar rows

I want to compare two tables, source and target, and get similar rows.
Compare source and target on Id one by one and:
If matched and it's two or more on Target => select All matched from Target
If matched and it's two or more on Source =>
for first matched if it doesn't selected before
select Matched From target
else (IF it have selected before)
check for next one matched
I think need a recursive expression to check source and target one by one
Source
x------x---------x
| Id | Name |
x------x---------x
| 1 | a |
| 2 | b |
| 2 | c |
| 3 | d |
| 3 | e |
| 4 | x |
x------x---------x
Target
x------x---------x
| Id | Name |
x------x---------x
| 1 | f |
| 1 | g |
| 2 | h |
| 3 | i |
| 3 | j |
| 5 | y |
x------x---------x
Result
x------x---------x
| Id | Name |
x------x---------x
| 1 | f |
| 1 | g |
| 2 | h |
| 3 | i |
| 3 | j |
x------x---------x
Test data
declare #s table(Id int, name varchar(20))
DECLARE #t table( Id int, name varchar(20))
INSERT #s values(1, 'a'), (2, 'b'), (2, 'c'), (3, 'd'), (3, 'e')
INSERT #t values(1, 'f'), (1, 'g'), (2, 'h'), (3, 'i'), (3, 'j')
I think you just need Exists operator to do this.
select * from #t t
where exists (select 1 from #s s where t.id=s.id)
SQLFIDDLE DEMO
SELECT DISTINCT
t.Id,
t.name
FROM SOURCE s
INNER JOIN target t ON s.id=t.Id
WHERE s.Id IN (SELECT Id FROM target)