Poniżej znajduje się rozwiązanie oparte na zbiorach, wykorzystujące CTE i funkcje okienkowe.
ranked_matches
CTE przypisuje najbliższą rangę dopasowania dla każdego wiersza w TableA
wraz z najbliższą rangą dopasowania dla każdego wiersza w TableB
, używając index
wartość jako rozstrzygający remis.
best_matches
CTE zwraca wiersze z ranked_matches
które mają najlepszą rangę (wartość rangi 1) dla obu rankingów.
Wreszcie zapytanie zewnętrzne używa LEFT JOIN
z TableA
do best_matches
CTE, aby uwzględnić TableA
wiersze, do których nie przypisano najlepszego dopasowania, ponieważ dopasowanie zamknięcia jest już przypisane.
Zauważ, że nie zwraca to dopasowania dla wiersza tabeli indeksu 3 wskazanego w przykładowych wynikach. Dopasowanie zamykające dla tego wiersza to indeks 3 tabeli B, różnica 83. Jednak ten wiersz tabeli B jest bliższy wierszowi indeksu 2 tabeli A, różnica 14, więc został już przypisany. Proszę wyjaśnić swoje pytanie, jeśli nie tego chcesz. Myślę, że tę technikę można odpowiednio dostosować.
CREATE TABLE dbo.TableA(
[index] int NOT NULL
CONSTRAINT PK_TableA PRIMARY KEY
, value int
);
CREATE TABLE dbo.TableB(
[index] int NOT NULL
CONSTRAINT PK_TableB PRIMARY KEY
, value int
);
INSERT INTO dbo.TableA
( [index], value )
VALUES ( 1, 123 ),
( 2, 245 ),
( 3, 342 ),
( 4, 456 ),
( 5, 608 );
INSERT INTO dbo.TableB
( [index], value )
VALUES ( 1, 152 ),
( 2, 159 ),
( 3, 259 );
WITH
ranked_matches AS (
SELECT
a.[index] AS a_index
, a.value AS a_value
, b.[index] b_index
, b.value AS b_value
, RANK() OVER(PARTITION BY a.[index] ORDER BY ABS(a.Value - b.value), b.[index]) AS a_match_rank
, RANK() OVER(PARTITION BY b.[index] ORDER BY ABS(a.Value - b.value), a.[index]) AS b_match_rank
FROM dbo.TableA AS a
CROSS JOIN dbo.TableB AS b
)
, best_matches AS (
SELECT
a_index
, a_value
, b_index
, b_value
FROM ranked_matches
WHERE
a_match_rank = 1
AND b_match_rank= 1
)
SELECT
TableA.[index] AS a_index
, TableA.value AS a_value
, best_matches.b_index
, best_matches.b_value
FROM dbo.TableA
LEFT JOIN best_matches ON
best_matches.a_index = TableA.[index]
ORDER BY
TableA.[index];
EDYTUJ:
Chociaż ta metoda używa CTE, rekursja nie jest używana i dlatego nie jest ograniczona do rekurencji 32K. Jednak może być tutaj miejsce na ulepszenia z perspektywy wydajności.