Abaixo está uma solução baseada em conjunto usando CTEs e funções de janelas.
Os
ranked_matches
CTE atribui uma classificação de correspondência mais próxima para cada linha em TableA
juntamente com uma classificação de correspondência mais próxima para cada linha em TableB
, usando o index
valor como um desempate. Os
best_matches
CTE retorna linhas de ranked_matches
que têm a melhor classificação (valor de classificação 1) para ambas as classificações. Finalmente, a consulta externa usa um
LEFT JOIN
da TableA
para as best_matches
CTE para incluir a TableA
linhas que não receberam uma melhor correspondência devido à correspondência de fechamento já estar atribuída. Observe que isso não retorna uma correspondência para a linha TableA do índice 3 indicada nos resultados da amostra. A correspondência de fechamento para essa linha é o índice TableB 3, uma diferença de 83. No entanto, essa linha TableB é uma correspondência mais próxima da linha do índice TableA 2, uma diferença de 14, portanto, já foi atribuída. Por favor, esclareça sua pergunta se não é isso que você deseja. Eu acho que esta técnica pode ser ajustada de acordo.
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];
EDITAR:
Embora esse método use CTEs, a recursão não é usada e, portanto, não está limitada a recursões de 32K. No entanto, pode haver espaço para melhorias aqui do ponto de vista do desempenho.