Vous trouverez ci-dessous une solution basée sur des ensembles utilisant des CTE et des fonctions de fenêtrage.
Les ranked_matches
CTE attribue un rang de correspondance le plus proche pour chaque ligne dans TableA
ainsi qu'un rang de correspondance le plus proche pour chaque ligne dans TableB
, en utilisant l'index
valeur en tant que bris d'égalité.
Les best_matches
CTE renvoie les lignes de ranked_matches
qui ont le meilleur rang (valeur de rang 1) pour les deux classements.
Enfin, la requête externe utilise un LEFT JOIN
de TableA
aux best_matches
CTE pour inclure le TableA
les lignes auxquelles une meilleure correspondance n'a pas été attribuée car la correspondance la plus proche est déjà attribuée.
Notez que cela ne renvoie pas de correspondance pour la ligne Index 3 TableA indiquée dans les résultats de votre exemple. La correspondance la plus proche pour cette ligne est l'index 3 de la TableB, soit une différence de 83. Cependant, cette ligne de la TableB est plus proche de la ligne de l'index 2 de la TableA, soit une différence de 14, elle a donc déjà été attribuée. Veuillez clarifier votre question si ce n'est pas ce que vous voulez. Je pense que cette technique peut être modifiée en conséquence.
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];
MODIF :
Bien que cette méthode utilise des CTE, la récursivité n'est pas utilisée et n'est donc pas limitée aux récursions 32K. Cependant, il peut y avoir place à amélioration ici du point de vue des performances.