spongessuck
spongessuck

Reputation: 1073

SQL Server 2005: How to join table rows only once

I think I've seen answers for similar questions for MySQL, but I'm struggling to find an answer applicable to SQL Server 2005.

So I have a table like this:

| ID | RelationalID | Year
----------------------------
| 1  | A            | 2014
| 2  | A            | 2014
| 3  | B            | 2014
| 4  | A            | 2015
| 5  | B            | 2015

And I'd like a result like this when I join the same table where RelationID matches but the year is different:

| 2014_ID | 2015_ID | RelationalID |
------------------------------------
| 1       | 4       | A            |
| 2       | NULL    | A            |
| 3       | 5       | B            |

But a standard JOIN ends up getting duplicate matches:

| 2014_ID | 2015_ID | RelationalID |
------------------------------------
| 1       | 4       | A            |
| 2       | 4       | A            |
| 3       | 5       | B            |

Is there a way to join two tables where the matches from the right table are joined only once in SQL Server 2005?

I tried this query with no success:

SELECT * FROM myTable 
LEFT JOIN (SELECT * FROM myTable) AS t ON t.RelationalID = myTable.RelationalID 
WHERE myTable.Year = 2014 and t.Year = 2015

Upvotes: 0

Views: 2065

Answers (4)

Kevin Cook
Kevin Cook

Reputation: 1932

DECLARE @MyTable TABLE
 (
    ID INT,
    RelationalID VARCHAR(10),
    [Year] INT
)

INSERT INTO @MyTable
VALUES
( 1 ,'A', 2014),
( 2 ,'A', 2014),
( 3 ,'B', 2014),
( 4 ,'A', 2015),
( 5 ,'B', 2015)

;WITH TEST AS
(
    SELECT 
        a.Id AS Id2014,
        NULL AS Id2015,
        a.RelationalId,
        RANK() OVER (PARTITION BY RelationalId ORDER BY ID) Ranked
    FROM @MyTable a
    WHERE a.Year = 2014 
    UNION
    SELECT 
        NULL AS Id2014,
        b.Id AS Id2015,
        b.RelationalId,
        RANK() OVER (PARTITION BY RelationalId ORDER BY ID) Ranked
    FROM @MyTable b
    WHERE b.Year = 2015
)
SELECT
    t1.Id2014,
    t2.Id2015,
    t1.RelationalID
FROM TEST t1
LEFT JOIN TEST t2
    ON t1.Ranked = t2.Ranked
    AND t1.RelationalID = t2.RelationalID
    AND t2.Id2015 IS NOT NULL
WHERE t1.Id2014 IS NOT NULL
ORDER BY t1.Id2014

I used a union and then ranked each side by relational id and left joined them. Here is the output:

Id2014  Id2015  RelationalID
1   4   A
2   NULL    A
3   5   B

Upvotes: 1

dnoeth
dnoeth

Reputation: 60482

You can get the result based on ROW_NUMBERs, but you need a rule how to assign them, I assumed it's based on the Id.

;WITH cte AS
(SELECT Id,
        RelationalId,
        year,
        row_number() 
        over (partition by RelationalId, year
              order by Id) as rn
FROM [YourTable] 
)
select t1.id as Id_2014,t2.id as Id_2015, t1.RelationalId
from cte as t1 left join cte as t2
on t1.RelationalId = t2.RelationalId
and t1.rn = t2.rn
and t2.year = 2015 
where t1.Year = 2014 

This is based on TMNT2014's fiddle

Upvotes: 2

TMNT2014
TMNT2014

Reputation: 2130

Below Sql would give you the result you are looking for but as I said before complexity would depend on the original set of data you have in your table. Here is the SQL Fiddle - http://sqlfiddle.com/#!3/d6300/24 - Good Luck!

;WITH CTE_Union AS
(SELECT 

    a.Id AS Id2014,
    NULL AS Id2015,
    a.RelationalId
  FROM [YourTable] a
  WHERE a.Year = 2014 
  UNION
  SELECT 
    NULL AS Id2014,
    b.Id AS Id2015,
    b.RelationalId
  FROM [YourTable] b
  WHERE b.Year = 2015)
SELECT Distinct CASE WHEN Id2014 IS NULL THEN (SELECT MIN(Id2014) FROM CTE_Union C WHERE    C.RelationalId =M.RelationalId) ELSE Id2014 END AS ID2014 , 
CASE WHEN Id2015 IS NULL AND Id2014 =  (SELECT MIN(Id2014) FROM CTE_Union C2 WHERE      C2.RelationalId =M.RelationalId) THEN (SELECT MIN(Id2015) FROM CTE_Union C WHERE       C.RelationalId =M.RelationalId) ELSE Id2015 END
,RelationalID
FROM CTE_Union M

Upvotes: 1

John Hartsock
John Hartsock

Reputation: 86892

There are probably a few ways to solve this but below shows an example of utilizing "Derived Tables" in a query.

SELECT
  q1.Id AS [2014_Id],
  q2.Id AS [2015_Id],
  q1.RelationalId
FROM (SELECT 
        MAX(a.Id) AS Id,
        a.RelationalId
      FROM [table] a
      WHERE a.Year = 2014
      GROUP BY 
        a.RelationalId) q1
INNER JOIN (SELECT 
              MAX(b.Id) AS Id,
              b.RelationalId
            FROM [table] b
            WHERE b.Year = 2015
            GROUP BY 
                b.RelationalId) q2 
  ON q2.RelationalId = q1.RelationalId

Upvotes: 0

Related Questions