If you are the only person creating tables, this may not be relevant, but several solutions will give a different number of output rows from the code in question, when one of the tables may not contain unique rows.
When I see an in with two columns, I can imagine it to mean two things:
Scenario 1 is fairly trivial, simply use two IN statements.
In line with most existing answers, I hereby provide an overview of mentioned and additional approaches for Scenario 2 (and a brief judgement):
As provided by @mrdenny, EXISTS sounds exactly as what you are looking for, here is his example:
SELECT * FROM T1
WHERE EXISTS
(SELECT * FROM T2
WHERE T1.a=T2.a and T1.b=T2.b)
This is a very concise way to join, but unfortunately most SQL dialects, including SQL server do not currently suppport it.
SELECT * FROM T1
LEFT SEMI JOIN T2 ON T1.a=T2.a and T1.b=T2.b
As mentioned by @cataclysm using two IN statements can do the trick as well, perhaps it will even outperform the other solutions. However, what you should be very carefull with is code duplication. If you ever want to select from a different table, or change the where statement, it is an increased risk that you create inconsistencies in your logic.
Basic solution
SELECT * from T1
WHERE a IN (SELECT a FROM T2 WHERE something)
AND b IN (SELECT b FROM T2 WHERE something)
Solution without code duplication (I believe this does not work in regular SQL Server queries)
WITH mytmp AS (SELECT a, b FROM T2 WHERE something);
SELECT * from T1
WHERE a IN (SELECT a FROM mytmp)
AND b IN (SELECT b FROM mytmp)
The reason why I don't recommend using an inner join as a filter, is because in practice people often let duplicates in the right table cause duplicates in the left table. And then to make matters worse, they sometimes make the end result distinct whilst the left table may actually not need to be unique (or not unique in the columns you select). Futhermore it gives you the chance to actually select a column that does not exists in the left table.
SELECT T1.* FROM T1
INNER JOIN
(SELECT DISTINCT a, b FROM T2) AS T2sub
ON T1.a=T2sub.a AND T1.b=T2sub.b
Most common mistakes:
The functional problem is that if you use a separator which might occur in a column, it gets tricky to ensure that the outcome is 100% accurate. The technical problem is that this method often incurs type conversions and completely ignores indexes, resulting in possibly horrible performance. Despite these problems, I have to admit that I sometimes still use it for ad-hoc queries on small datasets.
SELECT * FROM T1
WHERE CONCAT(a,"_",b) IN
(SELECT CONCAT(a,"_",b) FROM T2)
Note that if your columns are numeric, some SQL dialects will require you to cast them to strings first. I believe SQL server will do this automatically.
To wrap things up: As usual there are many ways to do this in SQL, using safe choices will avoid suprises and save you time and headaces in the long run.