Reputation: 6919
I am joining two tables, or rather a query and a sub-query on multiple fields. So For each PK in the first query I can get one or more records from the second sub-query. Is there a way to return only TOP 1 match from both queries?
Example:
SELECT t1.ID, a.FieldID, a.Field1, a.Field2, a.Field3, a.Field4
FROM Table1 as t1
INNER JOIN
(SELECT t2.FieldID, t2.Field1, t2.Field2, t3.Field3, t3.Field4 FROM Table2 as t2
INNER JOIN Table3 as t3 ON t2.ID=t3.IDFrom2) as a
ON t1.IDFor2=a.FieldID
WHERE LEN(a.[Field1]) < 80 AND LEN(a.Field3) < 60
AND (CHARINDEX(LTRIM(RTRIM(REPLACE(t1.Field1,' ',''))),LTRIM(RTRIM(REPLACE(a.Field1,' ',''))))>0 OR CHARINDEX(LTRIM(RTRIM(REPLACE(a.Field1,' ',''))),LTRIM(RTRIM(REPLACE(t1.Field1,' ',''))))>0 OR t1.Field1 IS NULL)
AND (CHARINDEX(LTRIM(RTRIM(t1.Field2)),LTRIM(RTRIM(a.Field2)))>0 or CHARINDEX(LTRIM(RTRIM(a.Field2)),LTRIM(RTRIM(t1.Field2)))>0 OR t1.Field2 IS NULL)
AND (CHARINDEX(LTRIM(RTRIM(t1.Field3)),LTRIM(RTRIM(a.Field3)))>0 OR t1.Field3 IS NULL)
AND CHARINDEX(LTRIM(RTRIM(t1.Field4)),LTRIM(RTRIM(a.Field4)))>0
Subquery
SELECT t2.FieldID, t2.Field1, t2.Field2, t3.Field3, t3.Field4 FROM Table2 as t2
INNER JOIN Table3 as t3 ON t2.ID=t3.IDFrom2
returns a number of records (that have the same t2.Field) that have relationship with Table1 when ran separately, just filtering t2.FieldID equals to a particular IDFor2 from Table1
But when the two queries are joined together and matched on various fields, I get twice as many records because it returns some duplicates for each t1.ID. How can I modify my query in order to retrieve only one record (TOP 1) per each t1.ID?
P.S. Unfortunately I cannot provide real data as it is confidential.
UPDATE:
the sub-query
SELECT t2.FieldID, t2.Field1, t2.Field2, t3.Field3, t3.Field4 FROM Table2 as t2
INNER JOIN Table3 as t3 ON t2.ID=t3.IDFrom2
returns two tables below combined:
Table2 Table3
_____________________________________ _________________
abc123 Company1 Industry1 def def 300 0.67
abc123 Company1 Industry1 fgh fgh 500 0.81
abc123 Company1 Industry1 hjk hjk 80 0.0045
abc123 Company2 Industry2 lmn lmn 250 0.003
abc123 NULL NULL vck vck 500 0.81
Table1
______________________________________________________
10001 abc123 1 Comp Industry1 300 0.67
10002 abc123 2 NULL NULL 500 0.81
10003 abc123 3 NULL NULL 80 0.0045
10004 abc123 4 Company2 Industr 250 0.003
10005 abc123 5 NULL NULL 500 0.81
I need to combine Table2 and Table3 data on keys which are def, fgh etc...
Then I need to match Table1 to the merged Table2 and Table3 and sort of "enrich" Table1 data with whatever gets pulled out of Tables 2 and 3. I want a result of something like:
10001 abc123 Company1 Industry1 300 0.67
10002 abc123 Company1 Industry1 500 0.81
10003 abc123 Company1 Industry1 80 0.0045
10004 abc123 Company2 Industry2 250 0.003
10005 abc123 Company2 Industry2 500 0.81
instead I get sort of a cartesian product:
10001 abc123 1 Company1 Industry1 300 0.67
10002 abc123 2 Company1 Industry1 500 0.81
10002 abc123 2 Company1 Industry2 500 0.81
10003 abc123 3 Company1 Industry1 80 0.0045
10004 abc123 4 Company2 Industry2 250 0.003
10005 abc123 5 Company2 Industry2 500 0.81
10005 abc123 5 Company2 Industry1 500 0.81
UPDATE 2:
I used Ionic's approach and it worked. I modified my query to use row_number and partition:
SELECT * FROM
(SELECT t1.ID, a.FieldID, a.Field1, a.Field2, a.Field3, a.Field4
FROM Table1 as t1
INNER JOIN
(SELECT t2.FieldID, t2.Field1, t2.Field2, t3.Field3, t3.Field4 FROM Table2 as t2
INNER JOIN Table3 as t3 ON t2.ID=t3.IDFrom2) as a
ON t1.IDFor2=a.FieldID
WHERE LEN(a.[Field1]) < 80 AND LEN(a.Field3) < 60
AND (CHARINDEX(LTRIM(RTRIM(REPLACE(t1.Field1,' ',''))),LTRIM(RTRIM(REPLACE(a.Field1,' ',''))))>0 OR CHARINDEX(LTRIM(RTRIM(REPLACE(a.Field1,' ',''))),LTRIM(RTRIM(REPLACE(t1.Field1,' ',''))))>0 OR t1.Field1 IS NULL)
AND (CHARINDEX(LTRIM(RTRIM(t1.Field2)),LTRIM(RTRIM(a.Field2)))>0 or CHARINDEX(LTRIM(RTRIM(a.Field2)),LTRIM(RTRIM(t1.Field2)))>0 OR t1.Field2 IS NULL)
AND (CHARINDEX(LTRIM(RTRIM(t1.Field3)),LTRIM(RTRIM(a.Field3)))>0 OR t1.Field3 IS NULL)
AND CHARINDEX(LTRIM(RTRIM(t1.Field4)),LTRIM(RTRIM(a.Field4)))>0) as b
WHERE b.rn=1
Upvotes: 4
Views: 9577
Reputation: 55
You can do it with the help of CTE function. Where you can create the select statement as you wish inside the CTE and then joining that CTE with the main table to get the results that you like
WITH A
(
SELECT t2.id, t2.name, t2.create_date,
ROW_NUMBER() OVER(PARTITION BY t2.id ORDER BY t2.create_date) as rn
FROM t2
)
SELECT t1.ID, a.FieldID, a.Field1, a.Field2, a.Field3, a.Field4 FROM A
INNER JOIN T1
ON t1.id = t2.id
WHERE A.RN = 1
Upvotes: 0
Reputation: 3935
Well you can run it on a subquery and run it for every t1.ID
to retrieve the first value back. But on the other hand you can try to solve this using ROW_NUMBER()
.
Here is a small example, given you have two tables t1
and t2
. You want just the first occurrence in t2
back.
SELECT *
FROM t1
INNER JOIN (
SELECT t2.id, t2.name, t2.create_date,
ROW_NUMBER() OVER(PARTITION BY t2.id ORDER BY t2.create_date) as rn
FROM t2
) as t2
ON t1.id = t2.id
WHERE t2.rn = 1
This will number all rows in this case based on the create_date
(in your case your logic behind the sentence "the first occurrence") and partition (group) based on the t2.id
.
Afterwards you join it and only take care of the rows where t2.rn = 1
Upvotes: 5